Java單鏈表接本操作

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;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章