1. 程式人生 > 其它 >演算法連結串列筆試題(一)

演算法連結串列筆試題(一)

1、移除重複節點

https://leetcode-cn.com/problems/remove-duplicate-node-lcci/

編寫程式碼,移除未排序連結串列中的重複節點。保留最開始出現的節點。

示例1:

輸入:[1, 2, 3, 3, 2, 1]
輸出:[1, 2, 3]
示例2:

輸入:[1, 1, 1, 1, 2]
輸出:[1, 2]
提示:

連結串列長度在[0, 20000]範圍內。
連結串列元素在[0, 20000]範圍內。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 
*/ class Solution { public ListNode removeDuplicateNodes(ListNode head) { if(head == null){ return null; } Set<Integer> set = new HashSet<>(); ListNode cur = head; set.add(cur.val); while (cur.next != null){ if(set.add(cur.next.val)){ cur
= cur.next; }else{ cur.next = cur.next.next; } } return head; } }

解析:

雜湊表法:對給定的連結串列進行一次遍歷,並用一個雜湊集合(HashSet)來儲存所有出現過的節點。在雜湊集合中儲存連結串列元素的值,方便直接使用等號進行比較。

2、迴文連結串列

https://leetcode-cn.com/problems/palindrome-linked-list-lcci/

編寫一個函式,檢查輸入的連結串列是否是迴文的。

示例 1:

輸入: 1->2
輸出: false

示例 2:

輸入: 1->2->2->1
輸出: true

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        List<Integer> list = new ArrayList<>();
        ListNode cur = head;
        while(cur != null){
            list.add(cur.val);
            cur = cur.next;
        }

        int front = 0;
        int backend = list.size()-1;
        while(front<backend){
            if(!list.get(front).equals(list.get(backend))){
                return false;
            }
            front++;
            backend--;
        }
        return true;
    }
}

解析:

一共為兩個步驟:

1、複製連結串列值到陣列列表中。
2、使用雙指標法判斷是否為迴文。

第一步,我們需要遍歷連結串列將值複製到陣列列表中。我們用 currentNode 指向當前節點。每次迭代向陣列新增 currentNode.val,並更新 currentNode = currentNode.next,當 currentNode = null 時停止迴圈。

最好使用雙指標法來檢查是否為迴文。我們在起點放置一個指標,在結尾放置一個指標,每一次迭代判斷兩個指標指向的元素是否相同,若不同,返回 false;相同則將兩個指標向內移動,並繼續判斷,直到兩個指標相遇。

3、連結串列相交

https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/

給你兩個單鏈表的頭節點headA 和 headB ,請你找出並返回兩個單鏈表相交的起始節點。如果兩個連結串列沒有交點,返回 null 。

圖示兩個連結串列在節點 c1 開始相交:

題目資料 保證 整個鏈式結構中不存在環。

注意,函式返回結果後,連結串列必須 保持其原始結構 。

示例 1:

輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
輸出:Intersected at '8'
解釋:相交節點的值為 8 (注意,如果兩個連結串列相交則不能為 0)。
從各自的表頭開始算起,連結串列 A 為 [4,1,8,4,5],連結串列 B 為 [5,0,1,8,4,5]。
在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
示例2:

輸入:intersectVal= 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Intersected at '2'
解釋:相交節點的值為 2 (注意,如果兩個連結串列相交則不能為 0)。
從各自的表頭開始算起,連結串列 A 為 [0,9,1,2,4],連結串列 B 為 [3,2,4]。
在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
示例3:

輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
輸出:null
解釋:從各自的表頭開始算起,連結串列 A 為 [2,6,4],連結串列 B 為 [1,5]。
由於這兩個連結串列不相交,所以 intersectVal 必須為 0,而 skipA 和 skipB 可以是任意值。
這兩個連結串列不相交,因此返回 null 。

提示:

listA 中節點數目為 m
listB 中節點數目為 n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 沒有交點,intersectVal 為 0
如果 listA 和 listB 有交點,intersectVal == listA[skipA + 1] == listB[skipB + 1]

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int aLength = 0;
        int bLength = 0;
        ListNode tmpA = headA;
        ListNode tmpB = headB;
        while(tmpA != null){
            tmpA = tmpA.next;
            aLength++;
        }
        while(tmpB != null){
            tmpB = tmpB.next;
            bLength++;
        }
        int grep = Math.abs(aLength - bLength);
     // 這裡可以交換兩個連結串列,讓tmpA始終是較長的那個; tmpA
= aLength > bLength ? headA : headB; tmpB = aLength > bLength ? headB : headA; while(grep > 0){ tmpA = tmpA.next; grep--; } while(tmpA != null){ if(tmpA == tmpB){ return tmpA; } tmpA = tmpA.next; tmpB = tmpB.next; } return null; } }

解析:

這個題要找到相同的節點,其實就是理解相同的含義。
如果兩個節點相同,那麼這兩個節點之後的節點也肯定都相同;
那麼意味著如果有相同的節點,從相同的節點往後長度一定相同。
所以只需要讓兩個連結串列長度對齊,讓長的連結串列先走一個長度差,再一起往後遍歷,遇到相同的就結束。

4、環路檢測

https://leetcode-cn.com/problems/linked-list-cycle-lcci/

給定一個連結串列,如果它是有環連結串列,實現一個演算法返回環路的開頭節點。若環不存在,請返回 null。

如果連結串列中有某個節點,可以通過連續跟蹤 next 指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,我們使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該連結串列中沒有環。注意:pos 不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。

示例 1:

輸入:head = [3,2,0,-4], pos = 1
輸出:tail connects to node index 1
解釋:連結串列中有一個環,其尾部連線到第二個節點。
示例 2:

輸入:head = [1,2], pos = 0
輸出:tail connects to node index 0
解釋:連結串列中有一個環,其尾部連線到第一個節點。
示例 3:

輸入:head = [1], pos = -1
輸出:no cycle
解釋:連結串列中沒有環。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode pos = head;
        Set<ListNode> set = new HashSet<>();
        while(pos != null){
            if(set.contains(pos)){
                return pos;
            }else{
                set.add(pos);
            }
            pos = pos.next;
        }
        return null;
    }
}

解析:

  遍歷連結串列中的每個節點,並將它記錄下來;一旦遇到了此前遍歷過的節點,就可以判定連結串列中存在環。藉助雜湊表可以很方便地實現