1. 归并排序
base case:if(left>=right) return;
归并排序左半边,归并排序右半边,合并
void merge_sort(vector<int> &nums,vector<int> reg,int start,int end){
if(start>=end) return ;
int mid=(end-start)/2+start;
int start1=start,end1=mid;
int start2=mid+1,end2=end;
merge_sort(nums,reg,start1,end1);
merge_sort(nums,reg,start2,end2);//分治左、右半边
int t=start;
while(start1<=end1&&start2<=end2){
reg[t++]=nums[start1]>nums[start2]?nums[start1++]:nums[start2++];
}
while(start1<=end1)
reg[t++]=nums[start1++];
while(start2<=end2)
reg[t++]=nums[start2++];
while(start<=end)
nums[start]=reg[start++];//合并
}
2.k路归并排序、败者树
k路归并排序就是将路合并,可以通过增大k来减少外存的读写次数,从而减少归并时间,但是k增大,增大了比较的次数,通过败者树来确定k,从而不影响内部排序效率。
2.1败者树
如上所示,败者树是一颗完全二叉树,每个结点是一个选手,每个结点的父节点是比赛的结果的败者,数组b中的数据项发生了改变,需要重构败者树,重构的方式是将新进入选择树的结点与其父节点进行比赛,将败者存在父节点中,胜者再与上一级的父节点比赛,比赛沿着根节点的路径不断进行,直到ls[1]处。把败者存放在ls[1]处,胜者存放在ls[0]处。
败者树的数据项发生变化:
对于败者树,数组的长度和原始数组的长度相等。
代码:数组b用来存储原始数组,数组ls用来生成败者树。ls存储的是b的索引,即子节点败者的索引
注释:数组b[s]的父节点是ls[t],t=(s+len)/2;
思想:b[s]与b[ls[t]]比较,败者存储在ls[t]中,s作为胜者继续向上比较。如果s败了,s和ls[t]交换作为胜者向上比较,如果s胜了,作为胜者向上比较。
ls[t]的父节点是ls[t/2]。
最终使得ls[0]=s作为最终的胜者存储。
一言以蔽之:胜者不断和父节点比较。
过程:
- 原始数据后面添加一个-1,作为初始化。
- ls数组的长度和原始数组相同,初始值为-1所在的索引。
- 从最后一个叶子节点(原始数据的最后一个元素)开始调整。
- 调整的方式是逐步和父节点比较,失败就交换。最后得到胜者。
#include<iostream>
#include<vector>
using namespace std;
void swap(int& a,int& b){
int temp=a;
b=a;
a=temp;
}
void adjust(vector<int> &b,vector<int> &ls,int s,int len){
int t=(s+len)/2;
while(t>0){
if(b[s]>b[ls[t]]){
swap(s,ls[t]);
}
t/=2;
}
ls[0]=s;
}
vector<int> creatls(vector<int>& nums){
int len=nums.size();
vector<int> b(len+1,-1);//最后一个元素-1是为了初始化,即-1永不败,也就是-1所在的索引永远不会出现在ls中
for(int i=0;i<len;i++)
b[i]=nums[i];
vector<int> ls(len,len);//ls的长度和原始数据的长度相同,初始值为-1所在的索引
for(int i=len-1;i>=0;i--){//从最后一个叶子结点调整败者树
adjust(b,ls,i,len);
}
return ls;
}
int main()
{
vector<int> b={10,9,20,6,12};
vector<int> ls=creatls(b);
for(int i=0;i<ls.size();i++)
cout<<ls[i];
}
2.2 归并排序
- 不断取出s=ls[0],表示第s个数组的元素是当前最小的,存入结果中
- 判断第s个数组是否遍历完,如果遍历完,存入INT_MAX,否则存入下一位到b[s]
- adjust(b,ls,s,k),b[s]改变,调整失败树。
终止条件:b[s]==INT_MAX,表示所有的数据遍历完了,结束。
#include<iostream>
#include<vector>
using namespace std;
#define MAX INT_MAX
void swaq(int& a,int& b){
int temp=a;
a=b;
b=temp;
}
void adjust(vector<int>& b,vector<int>& ls,int s,int k){
int t=(s+k)/2;
while(t>0){
if(b[s]>b[ls[t]])
swaq(s,ls[t]);
t/=2;
}
ls[0]=s;
}
void creatls(vector<int>& b_,vector<int>& ls,int k){
vector<int> b(k+1,-1);
for(int i=0;i<b_.size();i++)
b[i]=b_[i];
for(int i=k-1;i>=0;i--)
adjust(b,ls,i,k);
}
void kmerge(vector<vector<int>>& nums,vector<int>& b,vector<int> &ls,int k){
vector<int> index(k,0);//index[i]表示第i个数组的下标
vector<int> res;
int s=ls[0];//s表示第几个数组
while(b[s]!=MAX){
for(int i=0;i<b.size();i++)
cout<<b[i]<<" " ;
cout<<endl;
res.push_back(nums[s][index[s]]);//按序到达
index[s]++;//第s个数组的下标
if(index[s]>=nums[s].size()){
b[s]=MAX;
}
else{
b[s]=nums[s][index[s]];
}
adjust(b,ls,s,k);
s=ls[0];
}
for(int i=0;i<res.size();i++)
cout<<res[i]<<" ";
}
int main(){
vector<vector<int>> nums{
vector<int>{1,3,6,9},
vector<int>{2,3,4,5},
vector<int>{3,5,5,6,12},
vector<int>{3,5,6,7},
vector<int>{3,4,6,6}
};
int k=5;
vector<int> b(k,0);
for(int i=0;i<b.size();i++)
b[i]=nums[i][0];
vector<int> ls(k,k);
creatls(b,ls,k);
kmerge(nums,b,ls,k);
return 0;
}
调整adjust是和父节点比较
创建creatls是从最后一个节点调整到根节点
k路归并kmerge是不断取出最小的元素。注意:放入元素就要调整,整体结束条件和单独数组结束条件。