1. 程式人生 > 其它 >【leetcode】連結串列相關題目思路總結(更新中)

【leetcode】連結串列相關題目思路總結(更新中)

技術標籤:連結串列指標資料結構java面試

簡單題

206. 反轉連結串列

劍指 Offer 24. 反轉連結串列

https://leetcode-cn.com/problems/reverse-linked-list/https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/submissions/

題目描述:
反轉連結串列。

解題思路:
藉助幾個臨時指標。一個prev,一個curr,一個temp,然後不斷做賦值互換即可。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* curr = head;
        ListNode* temp;
        while(curr) {
            temp = curr->next;
            curr->next = prev;
            prev = curr;
            curr = temp;
        }
        return prev;
    }
};

21. 合併兩個有序連結串列

https://leetcode-cn.com/problems/merge-two-sorted-lists/

題目描述:
合併兩個有序連結串列。

解題思路:
比較兩個連結串列的頭指標的val大小,然後不斷串聯起來。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode rst(0);
        ListNode* temp = &rst;
        while(l1 && l2) {
            if (l1->val < l2->val) {
                temp->next = l1;
                l1 = l1->next;
            } else {
                temp->next = l2;
                l2 = l2->next;
            }
            temp = temp->next;
        }
        if (l1) {
            temp->next = l1;
        }
        if (l2) {
            temp->next = l2;
        }
        return rst.next;
    }
};

面試題 02.07. 連結串列相交

劍指 Offer 52. 兩個連結串列的第一個公共節點

160. 相交連結串列

https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

題目描述:
兩個長度不同的連結串列,從某個節點開始相交,後面的都是一樣的。

解題思路:
先將兩個連結串列長度對齊,然後遍歷比較,找到的第一個一樣的就是所求。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int len_list1 = 0;
        int len_list2 = 0;
        ListNode* tempA;
        ListNode* tempB;
        tempA = headA;
        while(tempA) {
            tempA = tempA->next;
            len_list1 += 1;
        }
        tempB = headB;
        while(tempB) {
            tempB = tempB->next;
            len_list2 += 1;
        }

        tempA = headA;
        tempB = headB;
        while (len_list2 < len_list1) {
            tempA = tempA->next;
            len_list1 -= 1;
        }
        while(len_list1 < len_list2) {
            tempB = tempB->next;
            len_list2 -= 1;
        }

        for (int i = 0; i < len_list1; i++) {
            if (tempA == tempB) {
                return tempA;
            }
            tempA = tempA->next;
            tempB = tempB->next;
        }
        return nullptr;
    }
};

面試題 02.01. 移除重複節點

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

題目描述:
將連結串列中的重複元素刪除,並不改變連結順序。

解題思路:
方法一,使用雜湊表,時間空間複雜度均為O(n)。
方法二,迴圈遍歷,時間複雜度O(n^2),空間O(1)。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
        set<int> data;
        ListNode* temp = head;
        ListNode* temp2 = new ListNode(0);
        ListNode* rst = temp2; 
        while(temp) {
            if (data.find(temp->val) == data.end()) {
                data.insert(temp->val);
                temp2->next = temp;
                temp2 = temp2->next;
            }
            temp = temp->next;
        }
        temp2->next = nullptr;
        return rst->next;
    }
};

劍指 Offer 22. 連結串列中倒數第k個節點

面試題 02.02. 返回倒數第k個節點

https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/https://leetcode-cn.com/problems/kth-node-from-end-of-list-lcci/

題目描述:
找出連結串列的倒數第k個節點。

解題思路:
用雙指標,一個比另外一個快k步。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* fast;
        ListNode* slow;
        int i = 0;
        fast = head;
        slow = head;
        while(fast && i < k) {
            fast = fast->next;
            i++;
        }
        if (i < k) {
            return nullptr;
        }
        while(fast) {
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }
};

234. 迴文連結串列

面試題 02.06. 迴文連結串列

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

題目描述:
判斷連結串列是否對稱。

解題思路:
方法一,將資料複製到陣列中,然後雙指標。
方法二,將後半部分連結串列反轉,然後再遍歷判斷,之後再將後半部分恢復。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if (!head) {
            return true;
        }
        ListNode* fast;
        ListNode* slow;
        ListNode* prev;
        fast = head;
        slow = head;
        while(fast) {
            fast = fast->next;
            if (fast) {
                fast = fast->next;
            }
            prev = slow;
            slow = slow->next;
        }

        ListNode* save_ptr = reverseList(slow);
        ListNode* rev_ptr = save_ptr;
        ListNode* ptr = head;
        while(rev_ptr && ptr) {
            if (rev_ptr->val != ptr->val) {
                return false;
            }
            rev_ptr = rev_ptr->next;
            ptr = ptr->next;
        }

        ListNode* now_ptr = reverseList(save_ptr);
        prev->next = now_ptr;
        return true;
    }

    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* curr = head;
        while(curr) {
            ListNode* temp = curr->next;
            curr->next = prev;
            prev = curr;
            curr = temp;
        }
        return prev;
    }
};

面試題 02.03. 刪除中間節點

237. 刪除連結串列中的節點

https://leetcode-cn.com/problems/delete-middle-node-lcci/https://leetcode-cn.com/problems/delete-node-in-a-linked-list/

題目描述:
刪除連結串列中的某個節點。

解題思路:
假設連結串列為A->B->C->D,刪除B,將B變為C即可,改變B的next,val。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val;
        node->next = node->next->next;
    }
};

83. 刪除排序連結串列中的重複元素

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

題目描述:
刪除連結串列中的重複元素。

解題思路:
判斷當前節點和下一個節點的值是否相等,相等則將當前節點的next域直接指向下下個節點(就是丟掉下個節點)。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* rst = head;
        while(head && head->next) {
            if (head->val == head->next->val) {
                head->next = head->next->next;
            } else {
                head = head->next;
            }
        }
        return rst;
    }
};

劍指 Offer 06. 從尾到頭列印連結串列

https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/

題目描述:
逆序列印連結串列值。

解題思路:
藉助棧搞定。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        stack<int> data;
        vector<int> rst;
        while(head) {
            data.push(head->val);
            head = head->next;
        }
        while(!data.empty()) {
            rst.push_back(data.top());
            data.pop();
        }
        return rst;
    }
};

203. 移除連結串列元素

劍指 Offer 18. 刪除連結串列的節點

https://leetcode-cn.com/problems/remove-linked-list-elements/https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/

題目描述:
刪除連結串列中和給定值相同的元素。

解題思路:
因為頭結點也可能會被刪除,所以應當設定一個哨兵指標。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* pre = new ListNode(0);
        ListNode* save = pre;
        pre->next = head;
        while(pre->next) {
            if (pre->next->val == val) {
                pre->next = pre->next->next; 
            } else {
                pre = pre->next;
            }
        }
        return save->next;
    }
};

1474. 刪除連結串列 M 個節點之後的 N 個節點

https://leetcode-cn.com/problems/delete-n-nodes-after-m-nodes-of-a-linked-list/

題目描述:
先保留M個,再刪掉N個,再保留M個,再刪掉N個,依次類推。

解題思路:
用哨兵節點,然後不斷迴圈處理,直到連結串列末尾。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* deleteNodes(ListNode* head, int m, int n) {
        ListNode* pre = new ListNode(0);
        pre->next = head;
        ListNode* save = pre;
        while(pre->next) {
            int temp_m = m;
            int temp_n = n;
            while(pre->next && temp_m > 0) {
                pre = pre->next;
                temp_m -= 1;
            }
            while(pre->next && temp_n > 0) {
                pre->next = pre->next->next;
                temp_n -= 1;
            }
        }
        return save->next;
    }
};

876. 連結串列的中間結點

https://leetcode-cn.com/problems/middle-of-the-linked-list/

題目描述:
求連結串列最中間的節點,如果有兩個,則選擇後面一個。

解題思路:
採用快慢指標法最好。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* fast = new ListNode(0);
        ListNode* slow = new ListNode(0);
        fast->next = slow->next = head;
        while(fast->next) {
            fast = fast->next;
            if (fast->next) {
                fast = fast->next;
                slow = slow->next;
            }
        }
        return slow->next;
    }
};

1290. 二進位制連結串列轉整數

https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer/

題目描述:
用連結串列表示二進位制的每位數字,要求轉成10進位制。

解題思路:
模擬題,遍歷一遍,一邊遍歷一邊轉即可。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int getDecimalValue(ListNode* head) {
        int sum = 0;
        while(head) {
            sum = sum*2 + head->val;
            head = head->next;
        }
        return sum;
    }
};

141. 環形連結串列

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

題目描述:
判斷一個連結串列是否有環。

解題思路:
快慢指標法,如果快慢指標跑著跑著跑到同一個節點了,那麼必然是有環。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* fast = new ListNode(0);
        ListNode* slow = new ListNode(0);
        fast->next = head;
        slow->next = head;
        while(fast->next) {
            fast = fast->next;
            if (fast == slow) {
                return true;
            }
            if (fast->next) {
                fast = fast->next;
                slow = slow->next;
            }
        }
        return false;
    }
};

中等題

1669. 合併兩個連結串列

https://leetcode-cn.com/problems/merge-in-between-linked-lists/

題目描述:
將其中一個連結串列刪去一段,然後接上另外一個連結串列。

解題思路:
把握關鍵節點,兩個連線處的左右兩個節點,要加以儲存,然後連線即可。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
        ListNode* pre = new ListNode(0);
        pre->next = list1;
        ListNode* save = pre;
        ListNode* left;
        ListNode* right;
        int i = 0;
        while(pre->next) {
            if (i == a) {
                left = pre;
            }
            if (i == b) {
                right = pre->next->next;
            }
            i += 1;
            pre = pre->next;
        }
        left->next = list2;
        pre = new ListNode(0);
        pre->next = list2;
        while(pre->next) {
            pre = pre->next;
        }
        pre->next = right;
        return save->next;
    }
};

92. 反轉連結串列 II

https://leetcode-cn.com/problems/reverse-linked-list-ii/

題目描述:
反轉從位置 m 到 n 的連結串列。請使用一趟掃描完成反轉。

解題思路:
除了m和n位置的節點外,m-1和n+1位置的節點也要儲存,這樣的話,就退化為了簡單版本的反轉連結串列,最後接起來就可以了。
注意m-1為空的情況,即從第一個節點開始逆置。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        int change_len = n - m + 1; // 需要逆序的長度
        ListNode* result = head; // 最終返回
        // ... pre_head head ... 
        ListNode* pre_head = NULL; 
        while(head && --m) {
            pre_head = head;
            head = head->next;
        }
        // ... modify_list_tail head ...
        ListNode* modify_list_tail = head;
        ListNode* new_head = NULL;
        // head - next - -
        // new_head - - -
        while(head && change_len) {
            ListNode* next = head->next;
            head->next = new_head;
            new_head = head;
            head = next;
            change_len--;
        }
        modify_list_tail->next = head;
        if (pre_head) {
            // 並非從第一個節點開始逆序
            pre_head->next = new_head;
        } else {
            result = new_head;
        }
        return result;
    }
};

142. 環形連結串列 II

面試題 02.08. 環路檢測

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

題目描述:
給定一個連結串列,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null。

解題思路:
這道題和環形連結串列的簡單題思路大體相同,只不過最後要返回入環點。
有兩種方法,一是直接用set,二是用快慢指標。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* meet = NULL;
        while(fast) {
            fast = fast->next;
            slow = slow->next;
            if (!fast) {
                return NULL;
            }
            fast = fast->next;
            if (fast == slow) {
                meet = fast;
                break;
            }
        }
        if (!meet) {
            return NULL;
        }
        while (head && meet) {
            if (head == meet) {
                return meet;
            }
            head = head->next;
            meet = meet->next;
        }
        return NULL;
    }
};

86. 分隔連結串列

面試題 02.04. 分割連結串列

https://leetcode-cn.com/problems/partition-list/https://leetcode-cn.com/problems/partition-list-lcci/

題目描述:
給你一個連結串列和一個特定值 x ,請你對連結串列進行分隔,使得所有小於 x 的節點都出現在大於或等於 x 的節點之前。
你應當保留兩個分割槽中每個節點的初始相對位置。

解題思路:
模擬法,使用兩個臨時連結串列,分別儲存比x大的,和比x小的,最後接起來就行。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode less_head(0);
        ListNode more_head(0);
        ListNode* less_ptr = &less_head;
        ListNode* more_ptr = &more_head;
        while(head) {
            if (head->val < x) {
                less_ptr->next = head;
                less_ptr = less_ptr->next;
            } else {
                more_ptr->next = head;
                more_ptr = more_ptr->next;
            }
            head = head->next;
        }
        less_ptr->next = more_head.next;
        more_ptr->next = NULL;
        return less_head.next;
    }
};

138. 複製帶隨機指標的連結串列

劍指 Offer 35. 複雜連結串列的複製

https://leetcode-cn.com/problems/copy-list-with-random-pointer/https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/

題目描述:
請實現 copyRandomList 函式,複製一個複雜連結串列。在複雜連結串列中,每個節點除了有一個 next 指標指向下一個節點,還有一個 random 指標指向連結串列中的任意節點或者 null。

解題思路:
儲存節點地址-序號的對映(map),序號-節點地址的對映(vector),遍歷兩遍,就完成了連結串列複製。注意其中random指標為空的情形加判斷條件。

程式碼:

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*, int> node_map;
        vector<Node*> node_vec;
        Node* ptr = head;
        int i = 0;
        while(ptr) {
            node_vec.push_back(new Node(ptr->val));
            node_map[ptr] = i;
            i++;
            ptr = ptr->next;
        }
        node_vec.push_back(0);
        ptr = head;
        i = 0;
        while(ptr) {
            node_vec[i]->next = node_vec[i+1];
            if (ptr->random) {
                node_vec[i]->random = node_vec[node_map[ptr->random]];
            }
            ptr = ptr->next;
            i++;
        }
        return node_vec[0];
    }
};

困難題

23. 合併K個升序連結串列

https://leetcode-cn.com/problems/merge-k-sorted-lists/

題目描述:
給你一個連結串列陣列,每個連結串列都已經按升序排列。
請你將所有連結串列合併到一個升序連結串列中,返回合併後的連結串列。

解題思路:
方法一:暴力解法,依次合併連結串列,(n+n)+(n+2n)+...+[n+(k-1)n],時間複雜度為O(nk^2),空間複雜度O(1)。
方法二:先放到vector裡,排序,然後再構造連結串列,knlog(kn)+kn,時間複雜度O(knlogkn),空間複雜度O(kn)。
方法三:兩兩合併,分治法。2nk/2+4nk/4+8nk/8+...,時間複雜度O(knlogk),空間複雜度O(logk),遞迴用到的棧空間。
方法四:使用優先順序佇列,始終維護k個數,每次取出最小值後,將其後的節點插入。時間複雜度O(kn*logk),空間複雜度O(k)。

程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.size() == 0) {
            return NULL;
        }
        if (lists.size() == 1) {
            return lists[0];
        }
        if (lists.size() == 2) {
            return mergeTwoLists(lists[0], lists[1]);
        }
        int mid = lists.size() / 2;
        vector<ListNode*> lists_vec1;
        vector<ListNode*> lists_vec2;
        for (int i = 0; i < mid; i++) {
            lists_vec1.push_back(lists[i]);
        }
        for (int i = mid; i < lists.size(); i++) {
            lists_vec2.push_back(lists[i]);
        }
        ListNode* lists1 = mergeKLists(lists_vec1);
        ListNode* lists2 = mergeKLists(lists_vec2);
        return mergeTwoLists(lists1, lists2);
    }

    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode temp_head(0);
        ListNode* pre = &temp_head;
        while(list1 && list2) {
            if (list1->val < list2->val) {
                pre->next = list1;
                list1 = list1->next;
            } else {
                pre->next = list2;
                list2 = list2->next;
            }
            pre = pre->next;
        }
        if (list1) {
            pre->next = list1;
        }
        if (list2) {
            pre->next = list2;
        }
        return temp_head.next;
    }
};