代码题(14)— 合并有序链表、数组、合并K个排序链表
1、21. 合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
(1)非递归方法:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2){ if(l1 == nullptr) return l2; if(l2 == nullptr) return l1; ListNode* res = new ListNode(-1); ListNode* head = res; while(l1 != nullptr && l2 != nullptr) { if(l1->val <= l2->val) { head->next = l1; l1 = l1->next; } else { head->next = l2; l2 = l2->next; } head = head->next;// 注意此处指针的移动 } head->next = l1 ? l1 : l2;//将剩下的部分接过来 return res->next; } };
(2)递归方法:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { if(l1 == nullptr) return l2; if(l2 == nullptr) return l1; ListNode * res = nullptr; if(l1->val < l2->val) { res = l1; res->next = mergeTwoLists(l1->next, l2); } if(l1->val >= l2->val) { res = l2; res->next = mergeTwoLists(l1, l2->next); } return res; } };
2、88. 合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
- 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
- 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3 输出: [1,2,2,3,5,6]
class Solution { public: void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) { int len = m+n-1; int i = m-1; int j = n-1; while(i>=0 && j>=0) { nums1[len--] = (nums1[i] > nums2[j]) ? nums1[i--] : nums2[j--]; } while(j>=0) nums1[len--] = nums2[j--]; } };
3、23. 合并K个排序链表
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1->1->2->3->4->4->5->6
这道题增加了难度,变成合并k个有序链表了,但是不管合并几个,基本还是要两两合并。那么我们首先考虑的方法是能不能利用之前那道题的解法来解答此题。答案是肯定的,但是需要修改,怎么修改呢,最先想到的就是两两合并,就是前两个先合并,合并好了再跟第三个,然后第四个直到第k个。这样的思路是对的,但是效率不高,没法通过OJ,所以我们只能换一种思路,这里就需要用到分治法 Divide and Conquer Approach。简单来说就是不停的对半划分,比如k个链表先划分为合并两个k/2个链表的任务,再不停的往下划分,直到划分成只有一个或两个链表的任务,开始合并。举个例子来说比如合并6个链表,那么按照分治法,我们首先分别合并1和4,2和5,3和6。这样下一次只需合并3个链表,我们再合并1和3,最后和2合并就可以了。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { if(lists.empty()) return nullptr; int n = lists.size(); while(n>1) { int k = (n+1)/2; for(int i=0;i<n/2;++i) { lists[i] = mergeTwo(lists[i],lists[i+k]); } n = k; } return lists[0]; } ListNode* mergeTwo(ListNode* l1, ListNode* l2) { if(l1 == nullptr) return l2; if(l2 == nullptr) return l1; ListNode* head = nullptr; if(l1->val <= l2->val) { head = l1; head->next = mergeTwo(l1->next,l2); } else { head = l2; head->next = mergeTwo(l1,l2->next); } return head; } };