【leetcode】链表相关题目思路总结(更新中)

简单题

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;
    }
};
posted @ 2020-12-15 23:40  Yanqiang  阅读(166)  评论(0编辑  收藏  举报