leetcode 链表处理集锦

来源:互联网 发布:淘宝客服信息发布出去 编辑:程序博客网 时间:2024/06/15 15:19

1. 对给定的链表进行排序,要求复杂度为O(nlgn)。

算法思想是分治,这样可以达到O(nlgn)。实现时写一个经典getMid函数,然后写合并函数,合并函数可以考虑使用一个多余辅助。

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:ListNode *sortList(ListNode *head) {if (head == NULL || head->next==NULL) return head;ListNode* mid = getMid(head);return merge(sortList(head), sortList(mid));}private:ListNode *getMid(ListNode* head){ListNode*  res = head;if (head && head->next){ListNode *fast = head->next, *slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;}res = slow->next;slow->next = NULL;}return res;}ListNode* merge(ListNode* p1, ListNode* p2){ListNode* dummy = new ListNode(0);ListNode* cur = dummy;while (p1 && p2){if (p1->val < p2->val){cur->next = p1;p1 = p1->next;}else{cur->next = p2;p2 = p2->next;}cur = cur->next;}cur->next = p1 ? p1 : p2;return dummy->next;}};

2. reorder-list。将链表按照如下要求的顺序输出。具体描述如下:

Given a singly linked list LL0L1→…→Ln-1Ln,
reorder it to: L0LnL1Ln-1L2Ln-2→…

You must do this in-place without altering the nodes' values.

For example,
Given{1,2,3,4}, reorder it to{1,4,2,3}.

根据题意,这道题首先是截取链表的后半段,然后逆置,再逐个合并。截取后半段使用经典的getMid函数。反转可以直接while循环实现。合并时加一个辅助项和一个标志变量,可以逐个地合并起来。

class Solution {public:void reorderList(ListNode *head) {ListNode* mid = getMid(head);reverse(mid);merge(head, mid);}private:void merge(ListNode*& p1, ListNode* p2){if (p2 == NULL) return;ListNode* dummy = new ListNode(0);ListNode* cur = dummy;bool flag = true;while (p1 && p2){if (flag){cur->next = p1;p1 = p1->next;}else{cur->next = p2;p2 = p2->next;}cur = cur->next;flag = !flag;}cur->next = p2;p1 = dummy->next;return;}ListNode* getMid(ListNode* head){ListNode* res = head;if (head == NULL || head->next == NULL) return NULL;ListNode *slow = head, *fast = head->next;while (fast->next && fast->next->next){//这样可以保证l2长1fast = fast->next->next;slow = slow->next;}res = slow->next;slow->next = NULL;return res;}void reverse(ListNode*& head){if (head == NULL) return;ListNode* cur = NULL;while (head && head->next){ListNode* tmp = head->next;head->next = cur;cur = head;head = tmp;}head->next = cur;return;}};


3. 判断链表是否有环。

使用快慢指针可以判断链表是否有环。

class Solution {public:bool hasCycle(ListNode *head) {if (head == NULL) return false;ListNode *fast = head, *slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow)return true;}return false;}};

4. 判断链表是否有环,如果有,找出环的起始点。

在快慢指针的基础上,如果相遇则有环,相遇时将fast指针从头部走起,当fast与slow指针再次相遇时,环的起始点就找到了。

class Solution {public:ListNode *detectCycle(ListNode *head) {if (head == NULL) return NULL;ListNode *fast = head, *slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow){fast = head;while (fast != slow){fast = fast->next;slow = slow->next;}return fast;}}return NULL;}};

5. 链表(带随机指针)的拷贝。
两种思路:第一种借助数据结构hash_map。先把节点创建出来,然后再根据hash_map设置其random指针。

第二种思路是:每创建一个节点,将该节点插入到源节点的后边,然后再设置随机指针,最后再断开成为新的链表,实现拷贝。

以下是第二种思路的代码:

/** * Definition for singly-linked list with a random pointer. * struct RandomListNode { *     int label; *     RandomListNode *next, *random; *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {} * }; */class Solution {public:RandomListNode *copyRandomList(RandomListNode *head) {RandomListNode *res=head;if (head == NULL) return res;RandomListNode *cur = head;while (cur){RandomListNode* tmp = new RandomListNode(cur->label);tmp->next = cur->next;cur->next = tmp;cur = tmp->next;}cur = head;while (cur){if (cur->random)cur->next->random = cur->random->next;cur = cur->next->next;}cur = head;res = head->next;while (cur){RandomListNode* p1 = cur->next->next;if (p1){cur->next->next = p1->next;}cur->next = p1;cur = p1;}return res;}};

6. 把有序链表转换为高度平衡的二叉搜索树。

思路是从中点做根,进行转换,这样就需要geiMid函数,然后递归的转换即可。

class Solution {public:TreeNode *sortedListToBST(ListNode *head) {if (head == NULL) return NULL;        <span style="white-space:pre"></span>if(head->next == NULL) return new TreeNode(head->val);ListNode* mid = getMid(head);TreeNode* root = new TreeNode(mid->val);root->left = sortedListToBST(head);root->right = sortedListToBST(mid->next);return root;}private:ListNode * getMid(ListNode *head){if (head == NULL || head->next == NULL) return NULL;ListNode *slow = head, *fast = head->next;while (fast->next && fast->next->next){fast = fast->next->next;slow = slow->next;}ListNode* res = slow->next;slow->next = NULL;return res;}};

7. 把链表m到n之间的节点进行反转。

思路:可以采用头插法。从第m个节点开始,将点m到n依次插入当段链表的头部。

class Solution {public:ListNode *reverseBetween(ListNode *head, int m, int n) {if (head == NULL || m < 1 || m >= n) return head;ListNode dummy(0); dummy.next = head;ListNode* pre = &dummy, *cur = head;for (int i = 0; i < m - 1; i++)pre = pre->next;if (pre)cur = pre->next;//待插入的点是cur的下一个节点for (int i = m; i < n; i++){ListNode* tmp = cur->next;cur->next = tmp->next;tmp->next = pre->next;pre->next = tmp;//cur = cur->next;}return dummy.next;}};

8. 根据给定值分割链表,保持各个分割的相对顺序不变。

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given1->4->3->2->5->2and x = 3,
return1->2->2->4->3->5.

两种思路:第一种类似快排的写法,值小就插入小的链表的尾部,值大就越过不处理。采用for循环写法

第二种用两个头指针分别记录比x值大的链表和比x值小的链表。

以下代码是第一种思路:

class Solution {public:ListNode *partition(ListNode *head, int x) {ListNode tmpNode(0);ListNode* dummy = &tmpNode;dummy->next = head;ListNode* p = dummy;for (ListNode* cur = dummy; cur->next; ){if (cur->next->val < x){if (cur == p){p = p->next;cur = cur->next;}else{ListNode* tmp = cur->next;cur->next = tmp->next;tmp->next = p->next;p->next = tmp;p = tmp;}}else{cur = cur->next;}}return dummy->next;}};

9. 删除排序链表的中的重复元素,重复的只保存一份。

模仿数组,写一个for循环,如果跟前边的值一样,就删除。

class Solution {public:ListNode *deleteDuplicates(ListNode *head) {if (head == NULL) return head;for (ListNode* cur = head; cur->next;){if (cur->next->val == cur->val){ListNode* tmp = cur->next;cur->next = tmp->next;delete tmp;}else{cur = cur->next;}}return head;}};

10. 删除链表中的重复元素。如果出现重复,则全部删除不保留。

设置一个辅助项,然后遍历链表,如果cur->next 和cur->next->next的值相等,则把该值value记录下来,用一个while循环,只要值等于value的节点,删除。

class Solution {public:ListNode *deleteDuplicates(ListNode *head) {if (head == NULL || head->next == NULL) return head;ListNode tmpNode(0), *dummy = &tmpNode;dummy->next = head;ListNode* cur = dummy;for (cur; cur->next&&cur->next->next;){if (cur->next->val == cur->next->next->val){ListNode* tmp = cur->next;int val = tmp->val;while (tmp && tmp->val == val){ListNode* p = tmp;tmp = tmp->next;delete p;}cur->next = tmp;}else{cur = cur->next;}}//if (cur!=dummy && cur->next && cur->val == cur->next->val){//delete cur->next;//cur->next = NULL;//}return dummy->next;}};


11. 合并两个有序链表。

添加一个辅助项,然后直接实现合并。

class Solution {public:ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {if (l1 == NULL || l2 == NULL)return l1 ? l1 : l2;ListNode tmpNode(INT_MIN), *dummy = &tmpNode, *cur = dummy;while (l1 && l2){if (l1->val < l2->val){cur->next = l1;l1 = l1->next;cur = cur->next;}else{cur->next = l2;l2 = l2->next;cur = cur->next;}}cur->next = l1 ? l1 : l2;return dummy->next;}};

12. 把链表尾部的k个结点旋转到头部。

第一种思路:首先更新k(防止大于length),然后用距离为k个节点的slow 和 fast指针,断开并反转。

第二种思路:遍历到链表尾部,然后记录链表的长度,并把链表串成环,然后在len-k处进行断开即可。

第一种思路:

class Solution {public:ListNode *rotateRight(ListNode *head, int k) {if (head == NULL) return head;int len = getLength(head);k = k%len;if (k == 0)return head;ListNode *slow = head, *fast = head;while (fast && k--)fast = fast->next;while (fast->next){fast = fast->next;slow = slow->next;}ListNode* res = slow->next;slow->next = NULL;fast->next = head;return res;}private:int getLength(ListNode* head){int count = 0;while (head){count++;head = head->next;}return count;}};
第二种思路:

class Solution {public:ListNode *rotateRight(ListNode *head, int k) {if (head == NULL) return head;int len = 1;        ListNode* cur = head;        while(cur->next){            len++;            cur = cur->next;        }        cur->next = head;        k = k%len;        cur = head;        for(int i=0;i<len-k-1;i++){            cur = cur->next;        }        head = cur->next;        cur->next = NULL;return head;}};

13. 把链表中每k个一组,进行反转。不足k个不反转。

首先计算链表的长度length,然后只要满足length>=k就进行一轮反转,然后length -=k.更新好每组的首尾指针即可。

class Solution {public:ListNode *reverseKGroup(ListNode *head, int k) {int len = 0;ListNode* temp = head;while (temp){len++;temp = temp->next;}if (len < k)return head;ListNode dummy(0);temp = &dummy;while (len >= k){ListNode* cur = head;ListNode* pre = NULL;ListNode* nex = NULL;for (int i = 0; i < k; i++){nex = cur->next;cur->next = pre;pre = cur;cur = nex;}temp->next = pre;temp = head;head = cur;            len -= k;}temp->next = head;return dummy.next;}};

14. 链表中的节点两两反转。此题是上一题的子集,不过因为是两两反转,所以可以考虑用递归实现,代码简洁。

class Solution {public:ListNode *swapPairs(ListNode *head) {if (head == NULL || head->next == NULL)return head;ListNode* next = head->next;head->next = swapPairs(next->next);next->next = head;return next;}};

15. 合并k个有序链表。

合并k个有序链表或者k个有序数组的合并可以借助优先队列很巧妙的实现。先把各个链表的头节点加入优先队列,每次从头部取出当前层最小的节点,然后push一个最小的节点的下一个节点进入优先队列,如此循环。

class cmp{public:bool operator()(const ListNode* a, const ListNode* b){return a->val > b->val;}};class Solution {public:ListNode *mergeKLists(vector<ListNode *> &lists) {if (lists.empty()) return NULL;ListNode tmpNode(0), *dummy=&tmpNode, *cur = dummy;priority_queue<ListNode*, vector<ListNode*>, cmp> myqueue;for (int i = 0; i < lists.size(); i++){            if(lists[i])myqueue.push(lists[i]);}while (!myqueue.empty()){ListNode* tmp = myqueue.top();myqueue.pop();cur->next = tmp;            cur = tmp;if (tmp->next)myqueue.push(tmp->next);}return dummy->next;}};

16. 移除链表倒数第n个结点。

因为可能删除的节点是头节点,所以添加一个辅助节点,然后用一个距离为n的slow 和 fast指针,进行查找和删除。

class Solution {public:ListNode *removeNthFromEnd(ListNode *head, int n) {ListNode tmpNode(0), *dummy = &tmpNode;dummy->next = head;ListNode *slow = dummy, *fast = dummy;while (n--)fast = fast->next;while (fast->next){slow = slow->next;fast = fast->next;}ListNode* tmp = slow->next;slow->next = tmp->next;delete tmp;return dummy->next;}};

17. 两个用链表表示的数字相加。

设置一个辅助项,循环相加,注意处理进位即可。

class Solution {public:ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {if (l1 == NULL || l2 == NULL)return l1 ? l1 : l2;ListNode tmpNode(0), *dummy=&tmpNode, *cur=dummy;int carry = 0;while (l1 && l2){int val = l1->val + l2->val + carry;cur->next = new ListNode(val % 10);carry = val / 10;l1 = l1->next;l2 = l2->next;cur = cur->next;}ListNode* p = l1 ? l1 : l2;while(p){int val = p->val + carry;cur->next = new ListNode(val % 10);carry = val / 10;p = p->next;cur = cur->next;}if (carry){cur->next = new ListNode(carry);}return dummy->next;}};





















0 0