排序专项_快排_合并排序
1. 合并两个有序链表
题目描述
https://leetcode-cn.com/problems/merge-two-sorted-lists/
解题思路
思路1:迭代:两个for遍历两个链表,比较val的值,逐个指向较小的数。
- 依次比较两条链表的第i个,第j个元素的大小,将新建一个工作节点prev上一次比较大小时的前一个节点,指向比较后较小的那个数对应的节点。
思路2:递归
- 比较两条链表的当前节点的大小,记录第一次比较时较小的节点为头节点返回。并使较小的节点指向为递归此函数(较小节点->next, 另一个节点):下一次的递归任务。
源代码
/**
* 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)
{
return l2;
}
if(!l2)
{
return l1;
}
ListNode* head = new ListNode(-1);
// 迭代是需要一个工作节点的,这里定义一个指针等于另外一个指针(同一类型的指针)
ListNode* prev = head;
while(l1 && l2)
{
if(l1->val <= l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
prev->next = l1 ? l1 : l2;
return head->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)
{
return l2;
}
if(!l2)
{
return l1;
}
if(l1->val <= l2->val)
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l2->next, l1);
return l2;
}
}
};
题型分析
- 合并排序题,可使用递归和迭代的方法解决,递归的方法时间复杂度低很多。
下次遇到此类题我要注意的地方
易 | 中 | 难 | |
---|---|---|---|
紧 | 1. 递归的思想是下一次的任务与这一次的任务相同,只不过改变了参数值而已。2. 迭代的思想可能要定义一个工作指针作为记录。 | ||
般 | |||
必 |
- 定义一个工作指针与头指针相等。
- 头指针的next即指向链表的第一个节点。
时间、空间复杂度
思路1:
sf:O(m+n)
kf:O©
思路2:
sf:O(m+n)
kf:O(m+n),递归调用函数
此类题模板代码
- 迭代
ListNode* head = new ListNode(-1);
// 迭代是需要一个工作结点的,这里定义一个指针等于另外一个指针(同一类型的指针)
ListNode* prev = head;
while(l1 && l2)
{
if(l1->val <= l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
prev->next = l1 ? l1 : l2;
- 递归
if(l1->val <= l2->val)
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l2->next, l1);
return l2;
}
启发性或普适性
- 递归的思想是相同的任务,不同的参数。
- 迭代的思想是工作指针的遍历。
总结
合并排序 = 递归(相同的任务,不同的参数)/迭代(工作指针的遍历)
2. 颜色分类
题目描述
https://leetcode-cn.com/problems/sort-colors/
解题思路
思路1:有一点快排的思想,分别定义0的右边界和2的左边界,遍历数组的值,当遇到2时与2的左边界交换;当遇到0时与0的左边界交换;遇到1时则i++。
- 定义好0的右边界和2的左边界。while循环直到i>j。
- 当遇到2时与2的左边界交换;当遇到0时与0的左边界交换;遇到1时则i++。
源代码
class Solution {
public:
void sortColors(vector<int>& nums) {
int len = nums.size();
// 0的右边界
int le = 0;
int ri = len - 1;
int i = 0;
while(i <= ri)
{
if(nums[i] == 0)
{
int tmp = nums[i];
nums[i++] = nums[le];
nums[le++] = tmp;
}
else if(nums[i] == 2)
{
int tmp = nums[i];
nums[i] = nums[ri];
nums[ri--] = tmp;
}
else{
i++;
}
}
}
};
题型分析
- 快排的本质:使用一个递归函数,从数组的左边的数a[i]开始,将数组的数放在一个合适的位置(从左遍历比a[i]大的数,从右遍历比a[i]小的数,交换);将a[i]放置到合适的位置后,a[i]左边的数小于a[i],右边的数大于a[i],再将左边也使用快排,直到i>j。
下次遇到此类题我要注意的地方
易 | 中 | 难 | |
---|---|---|---|
紧 | 1. 快排的本质:将基准数(range)左边的数放到合适的位置,然后递归快排这个位置左边和右边。 | ||
般 | |||
必 |
- 注意在遍历到2时,遍历的索引不需要+1,因为可能交换过来的数还没有放置到正确的位置。遍历到0时,遍历的索引可以+1,因为是从数组的0位置开始遍历的所以左边的a[le]元素就是有序的了。
时间、空间复杂度
思路1:
sf:O(n)
kf:O(1)
思路2:
sf:O()
kf:O()
扫描二维码关注公众号,回复:
11705423 查看本文章
此类题模板代码
- 快速排序代码。
int le = 0;
int ri = len - 1;
int i = 0;
while(i <= ri)
{
if(nums[i] == 0)
{
int tmp = nums[i];
nums[i++] = nums[le];
nums[le++] = tmp;
}
else if(nums[i] == 2)
{
int tmp = nums[i];
nums[i] = nums[ri];
nums[ri--] = tmp;
}
else{
i++;
}
}
启发性或普适性
- 快速排序的递归本质:不断将一个数放在一个区间的合适的位置。
总结
颜色分类 = 三色旗问题 + 快排(区间的左右边界+放置到合适的位置)
题目描述
解题思路
思路1:
源代码
题型分析
下次遇到此类题我要注意的地方
易 | 中 | 难 | |
---|---|---|---|
紧 | |||
般 | |||
必 |
时间、空间复杂度
思路1:
sf:O()
kf:O()
思路2:
sf:O()
kf:O()
此类题模板代码