【算法】-- 【單鏈表的反轉、增刪、去重、中間節點、是否有環、有環的第一個節點、是否相交等】

1.定義一個數據類來存儲節點信息

public class Node {
    Node next = null;
    int data;
    public Node(int data){
        this.data=data;
    }
}

2.單鏈表的基本操作

public class MyLinkedList {

    Node head = null;  //鏈表表頭的引用

    /**
     * 向鏈表中插入數據
     * @param d
     */
    public void addNode(int d){
        Node newNode = new Node(d);

        if (head == null){
            head = newNode;
            return;
        }
        Node tmp = head;
        while (tmp.next!=null){
            tmp = tmp.next;
        }
        tmp.next = newNode;
    }

    /**
     * 刪除節點
     * @param index
     * @return  成功返回true 失敗返回false
     */
    public Boolean deleteNode(int index){
        if (index<1 || index>length() ){//刪除元素的位置不合理
            return false;
        }

        //刪除鏈表第一個元素
        if (index==1){
            head = head.next;
            return true;
        }
        int i =2;
        Node preNode = head;
        Node curNode = preNode.next;

        while (curNode.next!=null){
            if (i == index){
                preNode.next = curNode.next;
                return true;
            }
            preNode = curNode;
            curNode = curNode.next;
            i++;
        }
        return true;
    }

    /**
     * 返回節點的長度
     * @return
     */
    public int length(){
        int length = 0;
        Node tmp = head;
        while (tmp.next!=null){
            length++;
            tmp = tmp.next;
        }
        return length;
    }

    /**
     * 對鏈表進行排序
     * @return
     */
    public Node orderNode(){
        Node nextNode = null;
        int temp =0;
        Node curNode = head;
        while (curNode.next!=null){
            nextNode =curNode.next;
            while (nextNode != null){
                if (curNode.data > nextNode.data){
                    temp = curNode.data;
                    curNode.data = nextNode.data;
                    nextNode.data = temp;
                }
                nextNode = nextNode.next;
            }
            curNode = curNode.next;
        }
        return head;
    }

    /**
     * 打印鏈表
     */
    public void printList(){
        Node tmp = head;
        while(tmp!=null){
            System.out.println(tmp.data);
            tmp = tmp.next;
        }
    }
  
    public static void main(String[] args) {
        MyLinkedList list = new MyLinkedList();
        list.addNode(5);
        list.addNode(3);
        list.addNode(1);
        list.addNode(3);
        System.out.println("listLength: "+list.length());
        System.out.println("before order:");
        list.printList();
        list.orderNode();
        System.out.println("after order:");
        list.printList();

    }

}

3.去掉重複的數據

  1. Hashtable法,通過key鍵去掉重複的數據
    /**
     * 去重
     * @param head
     */
    public void deleteDuplecate(Node head){
        Hashtable<Integer,Integer> table = new Hashtable<>();
        Node tmp = head;
        Node preNode = null;

        while(tmp!=null){
            if (table.containsKey(tmp.data)){
                preNode.next = tmp.next;
            }else{
                table.put(tmp.data,1);
                preNode = tmp;
            }
            tmp=tmp.next;

        }

    }
  1. 對鏈表雙重循環,外層進行正常循環,內層循環進行比較,如果於外層相同,就刪掉
/**
     * 去重
     * @param head
     */
    public void deleteDuplecate1(Node head){
        Node p = head;
        while(p!=null){
            Node q = p;
            while(q.next!=null){
                if (p.data == q.next.data){
                    q.next = q.next.next;
                }else{
                    q=q.next;
                }

            }
            p=p.next;
        }
    }

4.找出單鏈表中的倒數第k個元素

設置兩個指針,讓其中一個先前移k-1步,然後兩個指針同時往前移動。直到先行的指針指到null,另一個就指到k。

    /**
     * 找出單鏈表中倒數第k個元素
     * @param head
     * @param k
     * @return
     */
    public Node findElem(Node head, int k){
        if (k<1)
            return null;
        Node p1 =head;
        Node p2 =head;
        for (int i=0;i<k-1&&p1!=null;i++)
            p1=p1.next;
        if (p1==null){
            System.out.println("k不合法!");
            return null;
        }
        while (p1.next!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }

5.鏈表的反轉

    /**
     * 鏈表反轉
     * @param head
     */
    public void ReverseIteratively(Node head){
        Node pReversedHead = head;
        Node pNode = head;
        Node pPrev = null;

        while(pNode !=null){
            Node pNext = pNode.next;
            if (pNext==null){
                pReversedHead = pNode;
            }
            pNode.next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        this.head = pReversedHead;
    }

6.從尾到頭輸出單鏈表

用遞歸的方法實現的

    /**
     * 從尾到頭輸出單鏈表
     * @param pListHead
     */
    public void printListReversely(Node pListHead){
        if (pListHead != null){
            printListReversely(pListHead.next);
            System.out.println(pListHead.data);
        }
    }

7.查找單鏈表的中間節點

設置兩個指針從頭遍歷,一個快指針走兩步,一個慢指針走一步。快指針走到頭,慢指針恰好到中間節點

    /**
     * 尋找單鏈表的中間節點
     * @param head
     * @return
     */
    public Node searchMid(Node head){
        Node p =head;
        Node q =head;
        while(p!= null && p.next!=null &&p.next.next!=null){
            p = p.next.next;
            q = q.next;
        }
        return q;
    }

8.檢查一個鏈表是否有環及找出第一個入環口

設置兩個指針,fast快指針,走兩步,slow慢指針,走一步。比較兩個指針的值是否相等。當fast與slow的值相同,測試循環鏈表

    /**
     * 檢測一個鏈表是否有環
     * @param head
     * @return
     */
    public boolean isLoop(Node head){
        Node fast = head;
        Node slow = head;

        if (fast==null){
            return false;
        }

        while(fast!=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast.data==slow.data){
                return true;
            }
        }
        return !(fast==null || fast.next==null);
    }

如何找到環的入口點呢?
如果單鏈表有環,按照上述思路,當走的快的指針fast與走的慢的指針slow相遇時,slow指針肯定沒有遍歷完鏈表,而fast指針已經在環內循環了n圈 ( n > = 1)。假設slow指針走了 s 步,則fast 指針走了 2s 步 (fast步數還等於 s 加上在環內多轉的 n 圈),設環長爲 r ,滿足如下關係表達式:
2s = s + nr;
s = nr;
設整個鏈表長L,入口環與相遇點距離 x ,起點到環入口點的距離爲a,則滿足如下關係表達式:
a + s = nr;
a + s = (n - 1)r + r = (n - 1)r + L - a ;
a = (n - 1)r + (L - a - x);
(L - a - x)爲相遇點到環入口點的距離,從鏈表頭到環入口點等於(n - 1)循環內環+ 相遇點到環入口點,於是在鏈表頭與相遇點分別設置一個指針,每一次各走一步,兩個指針必定相遇,且遇到的第一個點爲環入口點。
具體代碼如下:

 /**
     * 尋找循環鏈表的環入口
     * @return
     */
    public Node findLoopPort(Node head){
        Node slow = head;
        Node fast = head;

        while(fast!=null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if (slow==fast)
                break;
        }
        if (fast == null && fast.next==null){
            return null;
        }
        slow = head;
        while (slow!=fast){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

9.在不知道頭結點情況下刪除指定節點

  1. 如果爲鏈表尾節點,則不能刪除,因爲不能設置前驅節點的next指針置爲空
  2. 可以交換節點與後續節點的值,然後刪除後續節點。
    具體代碼如下:
    /**
     * 不知道頭結點的情況下,刪除指定節點
     * @param n
     * @return
     */
    public boolean deleteNode(Node n){
        if (n==null || n.next==null)
            return false;
        int tmp = n.data;
        n.data = n.next.data;
        n.next.data = tmp;
        n.next = n.next.next;
        return true;
    }

10.判斷兩個鏈表是否相交及找出第一個相交點

如果兩個鏈表相交,那麼他們的尾結點一定相同

    /**
     * 判斷兩個鏈表是否相交
     * @param h1
     * @param h2
     * @return
     */
    public boolean isIntersect(Node h1,Node h2){
        if (h1==null || h2==null){
            return false;
        }
        Node tail1 =h1;
        while (tail1!=null){
            tail1 = tail1.next;
        }
        Node tail2 = h2;
        while (tail2!=null){
            tail2 = tail2.next;
        }
        return tail1==tail2;
    }

如何找出第一個相交的點?
首先分別計算兩個鏈表的長度。長的鏈表先遍歷(len1 - len2)步,使剩下的長度相等,然後兩個鏈表一起遍歷。每一步都比較值是否相等。如果相等,就返回節點。

   public static Node getFirstMeetNode(Node h1,Node h2){
        if (h1==null||h2==null){
            return null;
        }
        Node tail1 = h1;
        int len1=1;
        while (tail1!=null){
            tail1=tail1.next;
            len1++;
        }
        Node tail2 = h2;
        int len2=1;
        while (tail2.next!=null){
            tail2=tail2.next;
            len2++;
        }
        //兩鏈表不相交
        if (tail1!=tail2){
            return null;
        }
        Node t1 = h1;
        Node t2 = h2;
        //找出較長的鏈表,先遍歷
        if (len1>len2){
            int d =len1-len2;
            while (d!=0){
                t1 = t1.next;
                d--;
            }
        }else {
            int d =len2 - len1;
            while (d!=0){
                t2 = t2.next;
                d--;
            }
        }

        while (t1!=t2){
            t1=t1.next;
            t2=t2.next;
        }

        return t1;

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