分治法
示例1(23. 合并K个升序链表)
采用归并排序的思想
/*
归并的过程
0 1 2 3 4
merge(0,4)
l = 0 r = 4
mid = 2
merge(0,2) merge(3,4)
l = 0 r = 2 l = 3 r = 4
mid = 1 mid = 3
merge(0,1) merge(2,2) merge(3,3) merge(4,4)
l = 0 r = 1 l = 2 r = 2 return 3 return 4
mid = 0 return 2
merge(0,0) merge(1,1)
return 0 return 1
*/
思路
利用分治思想,先将指针数组递归到单个指针,然后两两合并,合并的时候就是合并两个有序链表问题(21之前写过),然后再往上4个4个合并,依此向上,直到合并为一个链表
(归并排序的图,代表的就是分治法的整体思路)
代码
记住下标的使用很重要(分治法的下标都可以这样设定,不用再因为下标问题烦恼):
第一次传入时 l 指向数组第一个元素,r 指向数组最后一个元素
mid = ( l + r )/2
l为第1有序区的第1个元素, mid为第1有序区的最后1个元素
mid+1为第2有序区第1个元素,r指向最后一个元素
merge函数里
就是对于l == r 和 l < r做判断,
即可保证传入 下面两个调用merge函数时,数组一定不是空数组
因为只要数组长度>=2, 下面两次调用merge时就一定是一边一个,而数组长度为1和为空,都直接返回了,
因此可以保证传入 下面两个调用merge函数时,数组一定不是空数组
也可以把判断为空,放在主函数中,只判断一次,之后都不会为空
因为为1就直接返回了,为2及以上,就会各自分到一个或者多个,不会为空了
class Solution {
public:
ListNode* mergeTwoLists(ListNode* a, ListNode* b)
{
ListNode* res = new ListNode();
ListNode* head = res;
// 合并两个链表时,不需要判断指针为空,因为merge函数中已经对空和单个元素做过处理,不会将空指针传给mergeTwoLists
// if ((!a) || (!b)) //有一个为空或者两个都为空时
// return a ? a : b; //如果a不是空,就返回a,如果a是空,就返回b
while(a&&b)
{
if(a->val <= b->val)
{
res->next = a;
res = res->next;
a = a->next;
}
else
{
res->next = b;
res = res->next;
b = b->next;
}
}
// 包括了两者都为空和一方为空的情况
res ->next = a ? a :b;
return head->next;
}
ListNode* merge(vector<ListNode*>& lists,int l,int r)
{
// l为第1有序区的第1个元素, mid为第1有序区的最后1个元素
if(l == r) return lists[l];
if(l > r) return nullptr; // 当传入的是空数组时,则lists.size() - 1 = -1,
int mid = (l+r)/2;
// mid+1为第2有序区第1个元素,r指向最后一个元素
return mergeTwoLists(merge(lists,l,mid), merge(lists,mid+1,r));
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
return merge(lists,0,lists.size()-1);
}
};
示例2:归并排序
C++归并排序
这个代码也很清晰,就是看这个代码,让我确定了下标如何使用
#include<iostream>
using namespace std;
void Merge(int arr[],int low,int mid,int high){
//low为第1有序区的第1个元素,i指向第1个元素, mid为第1有序区的最后1个元素
int i=low,j=mid+1,k=0; //mid+1为第2有序区第1个元素,j指向第1个元素
int *temp=new(nothrow) int[high-low+1]; //temp数组暂存合并的有序序列
if(!temp){ //内存分配失败
cout<<"error";
return;
}
while(i<=mid&&j<=high){
if(arr[i]<=arr[j]) //较小的先存入temp中
temp[k++]=arr[i++];
else
temp[k++]=arr[j++];
}
while(i<=mid)//若比较完之后,第一个有序区仍有剩余,则直接复制到t数组中
temp[k++]=arr[i++];
while(j<=high)//同上
temp[k++]=arr[j++];
for(i=low,k=0;i<=high;i++,k++)//将排好序的存回arr中low到high这区间
arr[i]=temp[k];
delete []temp;//删除指针,由于指向的是数组,必须用delete []
}
//用递归应用二路归并函数实现排序——分治法
void MergeSort(int arr[],int low,int high){
if(low<high){
int mid=(low+high)/2;
MergeSort(arr,low,mid);
MergeSort(arr,mid+1,high);
Merge(arr,low,mid,high);
}
}
int main(){
int a[10]={5,1,9,3,7,4,8,6,2,0};
MergeSort(a,0,9);
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
return 0;
}