Content
023 Merge k Sorted Lists(H)
Description
题意为将给定的k个有序单链表合并为一个有序链表。
Analysis & Solution
本题目可以从两个角度思考:
(1)逐个合并
将每个链表看作一个整体,从而将原问题拆分为更小的子问题,即每两个链表进行合并,就可以用到昨天的题目21. Merge Two Sorted Lists。
朴素的想法是从第一个到第K个进行遍历,先合并L1和L2,将合并结果与L3合并,以此类推,直到第K个链表完成合并。时间负责度为
O
(
n
K
)
O(nK)
O(nK)。
进一步可以用到分治的思想,类比归并排序算法,对K个链表进行二分,依次合并,时间复杂度就可以降低到
O
(
n
l
o
g
K
)
O(nlogK)
O(nlogK)。
(2)同时合并
用K个指针分别指向K个链表,所有链表同时进行合并。每次所有指针中指向值最小的作为新节点,其指针后移。可以很自然地联想到用优先队列解决。K个链表指向的节点构成优先队列,值最小的出队,出队节点后相邻节点入队。
Code
//依次合并
ListNode* mergeKLists(vector<ListNode*>& lists)
{
int N = lists.size();
if(N == 0) return nullptr;
ListNode* res = lists[0];
for(int i = 1; i < N; i++)
{
res = mergeTwoLists(res, lists[i]);
}
return res;
}
//分治:归并
ListNode* mergeKLists(vector<ListNode*>& lists)
{
int N = lists.size();
if(N == 0) return nullptr;
if(N == 1) return lists[0];
if(N == 2) return mergeTwoLists(lists[0], lists[1]);
int mid = N / 2;
vector<ListNode*> leftLists(lists.begin(), lists.begin() + mid);
vector<ListNode*> rightLists(lists.begin() + mid, lists.end());
return mergeTwoLists(mergeKLists(leftLists), mergeKLists(rightLists));
}
//优先队列
struct cmp
{
bool operator()(ListNode* a, ListNode* b)
{
return a->val > b->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists)
{
priority_queue<ListNode*, vector<ListNode*>, cmp> p_q;
for(auto elem: lists)
if(elem) p_q.push(elem);
ListNode dummy(-1);
ListNode *p = &dummy;
while(!p_q.empty())
{
ListNode *top = p_q.top();
p_q.pop();
p->next = top;
p = p->next;
if(top->next) p_q.push(top->next);
}
return dummy.next;
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
if(l1 == nullptr && l2 == nullptr) return nullptr;
ListNode *l3 = new ListNode(-1);
ListNode *l3_head = l3;
while(l1 != nullptr && l2 != nullptr)
{
if(l1->val < l2->val)
{
l3->next = l1;
l3 = l3->next;
l1 = l1->next;
}
else
{
l3->next = l2;
l3 = l3->next;
l2 = l2->next;
}
}
l3->next = (l1 == nullptr ? l2 : l1);
return l3_head->next;
}
时间复杂度:依次合并的复杂度为 O ( n K ) O(nK) O(nK),分治和优先队列均为 O ( n l o g K ) O(nlogK) O(nlogK)。
026 Remove Duplicates from Sorted Array(E)
Description
题意为用in-place的方法(不创建新数组)去除数组中的重复元素(仅保留一个),返回处理后的数组长度。
Analysis & Solution
本题思路较简单,对数组遍历一次,发现相同元素删除即可。也可以用两个快慢指针来解决。
Code
int removeDuplicates(vector<int>& nums)
{
int N = nums.size();
if(N < 2) return N;
int j = 0;
for(int i = 0; i < N - 1; i++)
{
if(nums[i] == nums[i+1])
{
nums.erase(nums.begin() + i);
i--;
N--;
}
}
return nums.size();
}
时间复杂度:仅对数组扫描一次,因此复杂度为 O ( n ) O(n) O(n)。
033 Search in Rotated Sorted Array(M)
Description
题意为在一个旋转有序数组中搜索给定target的位置。
Analysis & Solution
很自然可以想到用分治(二分法)解决。但是与有序数组不同,旋转有序数组像是被分为了两部分,要增加一些条件判断,从而确定搜索范围如何更新。
设当前搜索范围为[left, right],令mid=(left+right)/2。若nums[mid]<nums[0],说明mid位置右侧数组有序,否则说明mid位置左侧数组有序。进而根据target和nums[mid],nums[0]的大小关系确定新的搜索范围,具体参考代码。
Code
int search(vector<int>& nums, int target)
{
int N = nums.size();
if(N == 1) return nums[0] == target ? 0 : -1;
int left = 0, right = N - 1;
while(left < right)
{
int mid = (left + right) / 2;
if(nums[mid] == target) return mid;
if(nums[mid] < nums[0])
{
if(nums[mid] < target && target < nums[0]) left = mid + 1;
else right = mid - 1;
}
else
{
if(nums[mid] > target && target >= nums[0]) right = mid - 1;
else left = mid + 1;
}
}
return nums[left] == target ? left : -1;
}
时间复杂度:使用二分法,时间复杂度为 O ( l o g n ) O(logn) O(logn)。