鏈表題總結(以LeetCode爲主)

237. Delete Node in a Linked List
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.

Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.
給出一個方法,此方法能夠刪除指定的節點。
分析:本來打算從頭開始遍歷,找到此節點的前一個節點,然後p.next = p.next.next;就能把此節點刪除掉,但是發現此題目沒有頭節點,所以這個想法只能作廢。而且此方法的時間複雜度爲O(n)。所以值覆蓋法,看似是把當前節點刪除了,其實是把後一個節點的所有實例變量賦值給要刪除的節點,然後把後一個節點給從鏈表中刪除了,時間複雜度爲O(1)。

public class Solution {
    public void deleteNode(ListNode node) {
       if(node.next != null){
           node.val = node.next.val;
           node.next = node.next.next;
       }
    }
}

203. Remove Linked List Elements
Remove all elements from a linked list of integers that have value val.

Example
Given: 1 –> 2 –> 6 –> 3 –> 4 –> 5 –> 6, val = 6
Return: 1 –> 2 –> 3 –> 4 –> 5
刪除鏈表中指定值的節點。
分析:表頭跟表中的元素不是一個刪除法,表中的元素記錄它的前繼節點p,p.next = p.next.next;將要刪除的節點越過,便可以從鏈表中刪除此節點。而表頭的節點因爲沒有前繼節點,只能head = head.next;將頭節點往後移一個節點。所以此題分兩步進行。

public class Solution {
   public ListNode removeElements(ListNode head, int val) {
        //先處理表頭節點
        while(head != null && head.val == val) 
        head = head.next;
    ListNode p = head;
    //處理表中節點,設置pre爲要刪除節點的前繼
    for(ListNode pre = head; head!=null; head = head.next){
            if(head.val == val) pre.next = head.next;
            else  pre = head;
        }
        return  p;
    }
}

83. Remove Duplicates from Sorted List
Given a sorted linked list, delete all duplicates such that each element appear only once.

For example
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
分析:此題只要將相同的元素刪除,如果有和頭結點值相同的節點,只需要將後面的節點刪除即可,所以不用單獨考慮頭節點。

public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null) return null;
        for(ListNode pre = head,p = head.next; p!=null;p=p.next){
            if(pre.val == p.val) pre.next = p.next;
            else pre = p;
        }
        return head;
    }
}

234. Palindrome Linked List
Given a singly linked list, determine if it is a palindrome.
Follow up:
Could you do it in O(n) time and O(1) space?
給一個單鏈表,判斷此鏈表是否是迴文。並且能夠做到時間複雜度爲O(n),空間複雜度爲O(1)。
分析:迴文的定義就是將一串字符串反轉後,字符序列跟反轉前一樣。如12321等。所以首先想到的是,從頭遍歷鏈表,以前插法新建一個鏈表,newnode.next=head;head=newnode;這樣新建的鏈表就和以前的鏈表反序,然後遍歷判斷兩者是否相等,時間複雜度爲O(n),但空間複雜度爲O(n)。
此方法作廢,另闢蹊徑。細讀題目,發現題目並沒有要求保持原鏈表的數據結構,所以可以在原鏈表的數據結構上進行改變,判斷。將後半段進行反轉,前後兩段判斷是否相等即可。
首先讓我們認識一下原地反轉:
1、將每一個節點(處頭節點外)指望它的前繼。
2、調整鏈表的表頭和表尾。
代碼如下:

   public ListNode Rerseve(ListNode head){
        if(head == null) return null;
        ListNode pre = head;
        ListNode pse = head.next;
        while(pse != null){
            ListNode t = pse.next;
            pse.next = pre;
            pre = pse;
            pse = t;
        }
        head.next = null;
        return pre;
   }

其次,我們還需要找到後半段反轉的第一個節點。這裏就涉及到了快慢指針(slow),快指針(fast)每次走兩步,慢指針每次走一步,這樣快指針走到表尾的時候,慢指針正好走到表中。而且慢指針的下一個節點就是要反轉的後半段的第一個節點。
綜上,此題的解題代碼爲:

/**
 *原地反轉
 * 時間複雜度O(n),空間複雜度是O(1)
 */
public class Solution {
    public boolean isPalindrome(ListNode head) {
        boolean is_not = true;
        ListNode fast = head;
        ListNode slow = head;
        //鏈表爲空或者鏈表只有一個元素
        if(head==null || head.next ==null) return is_not ;
       //尋找反轉後半部分的第一個節點,同時還要保存此節點的前繼,
       //所以循環的終止條件不是fast!=null&&fast.next!=null
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //後半段反轉
        ListNode pReversed = slow.next;//要反轉的第一個節點
        ListNode pre = slow;//反轉節點前一個節點
        ListNode t;
        while(pReversed != null){
            t = pReversed.next;
            pReversed.next = pre;
            pre = pReversed;
            pReversed = t;
        }
        //重定向頭尾指針
        slow.next.next = null;
        slow.next = pre;
        //前半段和後半段比較
       for(ListNode p = slow.next ; p != null; p = p.next){
            if(head.val != p.val)
                return is_not = false;
            head = head.next;
        }
        return  is_not;
    }
} 

206. Reverse Linked List
Reverse a singly linked list.
上題判斷是否是迴文中已經講到了,自行翻閱。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return null;
        ListNode pre = head;
        for(ListNode p = head.next; p!= null; ){
            ListNode t = p.next;
            p.next = pre;
            pre = p;
            p = t;
        }
        head.next = null;
        return pre;
    }
}

160. Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins.
For example, the following two linked lists:
這裏寫圖片描述

Notes:
If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.
Your code should preferably run in O(n) time and use only O(1) memory.

判斷兩個鏈表是否相交,並且找到第一相交的節點。
分析:首先想到的是兩層for循環,尋找兩者的共同節點,如果有的話則返回此節點,沒有的話返回null。但是此方法的時間複雜度O(n^2),明顯的超時。
所以得想辦法縮減尋找共同節點的時間,經分析不難知道,如果兩個鏈表如果相交也肯定是後半部分相交,即將兩個鏈表尾對齊。分兩層循環,第一遍循環,找到兩個鏈表的表長之差length,讓相較較長的鏈表先行length;第二遍循環,兩個鏈表一起前進、比較,若找到兩個相同的節點,則表明兩個鏈表有相交的部分,返回此節點,若兩個鏈表到尾部還沒找到,說明兩個鏈表不想交,返回null。時間複雜度爲O(n)。

/**
 * 第一遍循環找到長度差N
 * 第二遍循環,長鏈表先走N步,然後同時移動,判斷是否有相同節點 *
*/
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode node = null;
        int lengthA = 0;
        int lengthB = 0;
        boolean is_find = false;
        ListNode startA = headA;
        ListNode startB = headB;
        while(startA != null && startB != null){
            startA = startA.next;
            startB = startB.next;
        }
        while(startA != null) {
            startA = startA.next;
            ++lengthA;
        }
        while(startB != null){
            startB = startB.next;
            ++lengthB;
        }
        //比較長的鏈表先行
        startA = headA;
        startB = headB;
        while( --lengthA >= 0) startA = startA.next;
        while( --lengthB >= 0) startB = startB.next;
        //開始比較兩個節點
        while(startA != null && startB != null){
            if(startA == startB)
                return startA;
            startA = startA.next;
            startB = startB.next;
        }
        return node;
    }

又做了一遍,發現這次的方法更簡單一些:

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
      ListNode p1 = headA, p2 = headB;
      int num = 0;
      while (p1 != null && p2 != null){
          p1 = p1.next;
          p2 = p2.next;
      }
      ListNode pheadA = headA, pheadB = headB;
      while (p1 != null && pheadA!=null){
          pheadA = pheadA.next;
          p1  = p1.next;
      }
      while (p2 != null && pheadB!=null){
          pheadB = pheadB.next;
          p2 = p2.next;
      }
      while (pheadA != null && pheadB!=null){
          if(pheadA == pheadB){
              return pheadA;
          }
          else{
              pheadA = pheadA.next;
              pheadB = pheadB.next;
          }
      }
      return null;
    }

141. Linked List Cycle
Given a linked list, determine if it has a cycle in it.

Follow up:
Can you solve it without using extra space?

判斷一個單鏈表是否有環,並且不借用額外的空間。
分析:依舊用到了鏈表的老朋友,快(fast)慢(slow)指針。如果有環快慢指針肯定會相遇。

public class Solution {
    public boolean hasCycle(ListNode head) {
       boolean is_has = false;
       if(head == null) return is_has;
       ListNode slow = head;
       ListNode fast = head;
       while(fast != null && fast.next != null){
           fast = fast.next.next;
           slow = slow.next;
           if(fast == slow)
               return is_has = true;
       }
       return is_has;
    }
}

142. Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

Subscribe to see which companies asked this question.
判斷一個鏈表是否有環,並找到環的第一個節點,即環的入口節點。
這篇博客將用快慢指針尋找環入口節點解釋的非常詳細
即從鏈表頭到環入口點等於(n-1)循環內環+相遇點到環入口點。所以這兒我只貼出我自己寫的代碼:

public class Solution {
public ListNode detectCycle(ListNode head) {
      if(head == null) return null;
      ListNode fast = head;
      ListNode slow = head;
      while(fast != null && fast.next != null){
          fast = fast.next.next;
          slow = slow.next;
          if(fast == slow){
             slow = head;
             while(true){
               if(slow == fast)
                   return slow;
              slow = slow.next;
              fast = fast.next;
             }
          }
      }
        return null;
    }
}

21. Merge Two Sorted Lists
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

分析:將兩個已排好序的鏈表連接成一個排好序的鏈表。
算法步驟:1、指針phead1和phead2初始化,分別指向l1和l2的第一個節點。
2、phead指向新建的頭結點。
3、當phead1和phead2均未到達相應的表尾時,則依次比較phead1和 phead2所指向的節點的值,從l1和l2中“摘取”節點值最小的節點插入到phead後面。
4、將非空表的剩餘段插入到phead所指的節點後。
5、返回head.next。

public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null && l2 == null) return null;
        ListNode phead1 = l1;
        ListNode phead2 = l2;
        ListNode head = new ListNode(-1);
        ListNode phead = head;
        while(phead1 != null && phead2 != null){
            if(phead1.val <= phead2.val){
                phead.next = phead1;
                phead = phead1;
                phead1 = phead1.next;
            }
            else {
                phead.next = phead2;
                phead = phead2;
                phead2 = phead2.next;
            }
        }
        while (phead1 != null) {
            phead.next = phead1;
            phead = phead1;
            phead1 = phead1.next;
        }
        while (phead2 != null){
            phead.next = phead2;
            phead = phead2;
            phead2 = phead2.next;
        }
        return head.next;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章