Java單鏈表基本操作:
(一)順序查找;
(二)指定位置增加節點;
(三)刪除當前節點;
(四)單鏈表反轉;
(五)輸出倒數第K個節點;
(六)刪除重複節點;
(七)排序
(八)合併兩個排序單鏈表;
(九)交換相鄰節點的值;
(十)O(n)時間內查找單鏈表的中間節點
(十一)逆序(從尾至頭)輸出單鏈表
(十二)判斷單鏈表是否有環
(十三)判斷兩個鏈表是否相交
(十四)已知一個單鏈表中存在環,求進入環中的第一個節
定義node:
public class Node {
int data;
Node next;
Node(int data){
this.data=data;
}
}
定義ListNode:
public class ListNode {
public static Node getSingleList(){
Node head=new Node(3);
Node node1=new Node(6);
Node node2=new Node(8);
Node node3=new Node(6);
Node node4=new Node(2);
head.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=null;
return head;
}
public static void printList(Node node){
System.out.print("List:");
while(node!=null){
System.out.print(node.data+"-->");
node=node.next;
}
System.out.println();
}
}
(一)順序查找
/*
* 查找值爲num的元素位置,沒有返回-1*/
public class SeqSearch {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
int num=9;
int id=new SeqSearch().searchNumId(head,num);
System.out.println("要查找的元素位置爲:"+id);
}
public int searchNumId(Node head,int num){
int id=1;
while(head!=null&&head.data!=num){
head=head.next;
id++;
}
if(head==null) id=-1;
return id;
}
}
(二)指定位置增加節點
/*
* 將新節點增加在指定位置*/
public class AddNode {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
Node add=new Node(0);
int id=0;
head=new AddNode().addNode(head,add,id);
ListNode.printList(head);
}
public Node addNode(Node head,Node add,int id){
if(id==0) {
add.next=head;
head=add;
}else{
while(head.next!=null&&id>1){//尋找節點增加的位置
head=head.next;
id--;
}
add.next=head.next;
head.next=add;
}
return head;
}
}
(三)刪除當前節點
單鏈表中要刪除當前節點,如果當前節點不是頭節點,則無法使前面的節點直接指向後面的節點,這時候我們可以換一種思路,即:將當前節點的下一節點值附給當前節點,然後刪除當前節點的下一節點,這樣就等效爲刪除當前接節點了。
/**
* Definition for singly-linked list.
* public class Node {
* int data;
* Node next;
* Node(int x) { data = x; }
* }
*/
public void deleteNode(Node node) {
node.val=node.next.val;
node.next=node.next.next;
}
(四)單鏈表反轉
可以參考http://blog.csdn.net/u010305706/article/details/50810005
表示更詳細
public class ReverseSingleList {
public static void main(String args[]){
Node head=ListNode.getSingleList();
ListNode.printList(head);
head=new ReverseSingleList().reverseSingleList(head);
ListNode.printList(head);
}
public Node reverseSingleList(Node head){
if(head== null||head. next== null){
return head;
}
Node preNode=head;
Node pNode=head. next;
Node markNode=head. next;
head. next= null; //一定要斷開head節點的next節點,不然形成了循環
while(markNode!= null){
markNode=markNode. next;
pNode. next= preNode;
preNode=pNode;
pNode=markNode;
}
return preNode;
}
}
(五)查找倒數第K個節點
package listnode;
public class LastKNode {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
int k=3;
head=new LastKNode().getLastKNode(head,k);
System.out.println(head.data);
}
public Node getLastKNode(Node head, int k){
Node node=head;
while(node. next!= null&&k>0){
node=node. next;
k--;
}
while(node!= null){
node=node. next;
head=head. next;
}
return head;
}
}
(六)刪除重複節點
package listnode;
public class DeleteDuplecate_SingleList {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
deleteNode(head);
ListNode.printList(head);
}
public static void deleteNode(Node head){
while(head.next!=null){
Node p=head;
while(p.next!=null){
if(p.next.data==head.data){
p.next=p.next.next;
}
p=p.next;
}
head=head.next;
}
}
}
(七)排序
單鏈表的插入排序比數組麻煩,因爲每次都都要從頭節點開始往後遍歷,頭節點也需要單獨處理
package listnode;
public class SortList {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
head=new SortList().insertSortList(head);
ListNode.printList(head);
}
public Node insertSortList(Node head){
Node p=head.next;
Node pre=head;
while(p!=null){
Node cur=head; //比較節點,每次都是從頭節點開始
Node q=p.next;
if(p.data<head.data){ //由於是單鏈表,每次只能從頭節點開始比較
pre.next=q;
p.next=head;
head=p;
}else
while(cur.next!=p){
if(p.data<cur.next.data){//將P與cur.next進行比較,方便單鏈表插入
pre.next=q;
p.next=cur.next;
cur.next=p;
p=pre; //保證pre每次指向的都是p前面的一個節點
}else
cur=cur.next;
}
pre=p;
p=q;
}
return head;
}
}
(八)合併兩個有序單鏈表
package listnode;
public class MergeSeqList {
public static void main(String[] args) {
Node head1=SortList.insertSortList(ListNode.getSingleList());
Node head2=SortList.insertSortList(ListNode.getSingleList());
head1=new MergeSeqList().mergeTwoLists(head1, head2);
ListNode.printList(head1);
}
public Node mergeTwoLists(Node l1, Node l2) {
Node head=null;
if(l1==null){//先判斷兩個鏈表是否爲空
return l2;
}
if(l2==null){
return l1;
}
if(l1.data<=l2.data){
head=l1;
l1=l1.next;
}else{
head=l2;
l2=l2.next;
}
Node temp=head;
while(l1!=null&&l2!=null){
if(l1.data<=l2.data){
temp.next=l1;
l1=l1.next;
}else{
temp.next=l2;
l2=l2.next;
}
temp=temp.next;
}
if(l1!=null){
temp.next=l1;
}
if(l2!=null){
temp.next=l2;
}
return head;
}
}
(九)交換相鄰節點對的值
本題目來源於:Leetcode:
24.swap nodes in pairs(單鏈表中交換節點對的值)
Given a linked list, swap every two adjacent nodes and return its head.
For example,
Given 1->2->3->4, you should return the list as 2->1->4->3.
Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.
有兩種思路解決本題:
(1)利用鏈表的特點,改變鏈表指針的指向,改變了節點的位置
package listnode;
public class SwapPairs {
public static void main(String[] args) {
Node head=ListNode.getSingleList();
ListNode.printList(head);
head=new SwapPairs().swapPairs(head);
ListNode.printList(head);
}
public Node swapPairs(Node head) {
Node root=head;
if(head!=null&&head.next!=null){
root=head.next;
Node pre=head;
Node p=null;
Node q=null;
while(head!=null&&head.next!=null){
p=head.next;
q=p.next;
pre.next=p;
p.next=head;
head.next=q;
pre=head;
head=q;
}
}
return root;
}
}
(2)改變相鄰節點對的值,不改變節點指針(通過數組思維實現)
public ListNode swapPairs(ListNode head){
public ListNode swapPairs(ListNode head) {
ListNode p=head;
while(p!=null){
ListNode q=p.next;
if(q!=null){
int temp=p.val;
p.val=q.val;
q.val=temp;
p=p.next;
}
p=p.next;
}
return head;
}
}
(十)判斷是否有環並輸出環長度以及環的入口節點
本文解決三個問題:
1.單鏈表是否有環?
2.有則輸出環的長度?
3.找到環的入口節點?
分析:
定義兩個指針fast 和slow,fast每次向後移動兩個節點,slow每次想後移動一個節點。
1.如果沒有環,則fast首先到達鏈表結尾;
2.鏈表有環的情況下:fast與slow兩次相遇,slow中間走過的節點處即爲環的長度;
3.找環的入口節點稍微複雜點,有如下的推導過程:
相遇的時候,slow共移動了s步,fast共移動了2s步。
定義a如下: 鏈表頭移動a步到達入口點。
定義x如下: 入口點移動x步到達相遇點。
定義r如下: 環的長度。
定義L如下: 鏈表總長度爲L。
其中L = a + r
那麼slow和fast相遇了,fast必然比slow多走了n個圈,也就是 n*r 步,那麼
s = a + x
2s = s + n*r , 可得 s = n*r
將s=a+x,帶入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r
從表頭移動到入口點,再從入口點移動到相遇點,也就是移動了整個鏈表的距離,即是 L = a + r , 所以r = L - a
所以 a+x = (n-1)*r + L - a , 於是 a = (n-1)*r + L - a - x
得到:從表頭到入口點的距離,等於從相遇點到入口點的距離。
所以,從表頭設立一個指針,從相遇點設立一個指針,兩個同時移動,必然能夠在入口點相遇,這樣,就求出了相遇點。
上面三個問題的java解決代碼:
方法一:一次性求解
public class ExistCircle {
static int id = 1;
public Node existCircle(Node head){
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
//輸出環長
while(fast == slow) {
int len = 1;
fast = fast.next.next;
slow = slow.next;
while (fast != slow) {
len++;
fast = fast.next.next;
slow = slow.next;
}
System.out.println("The length of circle is:" + len);
//輸出環入口節點
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
id++;
}
return slow;
}
}
return null;
}
public static void main(String[] args) {
Node head=new Node(3);
Node node1=new Node(6);
Node node2=new Node(8);
Node node3=new Node(5);
Node node4=new Node(2);
Node node5=new Node(7);
head.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node3;
Node port = new ExistCircle().existCircle(head);
System.out.println("環入口爲第" + id + "個節點:" + port.data);
}
}
方法二:分開求解:
package listnode;
public class ExitCircle {
static int id = 1;
public static void main(String[] args) {
//測試
Node head=new Node(3);
Node node1=new Node(6);
Node node2=new Node(8);
Node node3=new Node(5);
Node node4=new Node(2);
Node node5=new Node(7);
head.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node3;
new ExitCircle().exitCircle(head);
Node port = new ExitCircle().findLoopPort(head);
System.out.println("環入口爲第" + id + "個節點:" + port.data);
}
//環入口節點
//環的入口節點到快慢指針相遇的距離 與 鏈表頭節點到環入口節點的距離相等
public Node findLoopPort(Node head){
Node slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
fast = head;
while(head != slow){
id++;
head = head.next;
slow = slow.next;
}
return slow;
}
}
System.out.print("NoLoop !");
return null;
}
public boolean exitCircle(Node head){
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){//判斷是否由環,注意fast.next = null的情況
fast = fast.next.next;
slow = slow.next;
if(fast == slow){//存在環
int count = 0;
while(true){
count ++;
fast = fast.next.next;
slow = slow.next;
if(fast == slow){//快慢指針在第二次相遇,這個點肯定是第一次相遇的點
System.out.println("環的長度:" + count);
return true;
}
}
}
}
System.out.println("false!");
return false;
}
}