快速排序
是原址排序
思路是,找一个参考元素,然后把小于参考元素的放左边,大于参考元素的放右边。再递归调用。
再进行之前可以先把数组随机化一次,可以达到平均时间复杂度O(nlogn)
这里有两种思路:一开始是,对数组a[left,,,right],参考元素选right(或left),然后i从left位置开始,向右搜索,找到小于参考元素reference的数,index记录当前插空的位置,把找到的数与index的位置换,最后全遍历一遍以后,index左边就全是小于参考元素的,index向右是大于参考元素的(不包括最右的参考元素),再把最右边参考元素放到Index上就完了
另一种,用两个index,分别左右开始搜索,左边搜索到第一个不符合的数(即大于参考元素的数),右边也搜索到不符合的数(小于参考元素的),交换,这样优化了调用swap的次数。
//数组随机化快速排序
#include<iostream>
#include<cstdlib>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include<windows.h>
using namespace std;
void Swap(int &a,int &b);
void FastSort(int a[],int left, int right);//快速排序
int RandFast(int left,int right);//产生left到right的随机数(包括left,right)
int RandomPartition(int a[],int left,int right);//子数组随机化原址重排
int Partition(int a[],int left,int right); //数组left到right部分进行大小重排,并返回参考元素的索引位置
void FastSortBook(int a[],int left ,int right);//书上的快速排序算法,调用上面的Partition方法
#define N 100
int main()
{
int a[N]= {1,2,3,4,5,6,7,8,9,10};
int i;
unsigned int seed;
time_t seconds_start,seconds_end;
seconds_start = time(NULL);
seed = int(seconds_start);
srand(seed);
for(i=0;i<N;i++)
{
//cout<<"input number"<<i<<endl;
// cin>>a[i];
a[i] = rand();
}
cout<<"start"<<endl;
FastSortBook(a,0,N-1);
for(i=0;i<50;i++)
{
cout<<"a"<<i<<"="<<a[i]<<endl;
}
seconds_end = time(NULL);
cout<<"time="<<seconds_end-seconds_start<<endl;
}
inline void Swap(int &a,int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int RandFast(int left,int right)//left,right是索引,所以应该产生left到right包括left,right的随机数
{
srand((unsigned)time(NULL));
return rand()%(right-left+1)+left;
}
int RandomPartition(int a[],int left,int right)//随机选取元素进行子数组原址重排
{
int i = RandFast(left,right);
Swap(a[i],a[right]);
return Partition(a,left,right);
}
int Partition(int a[],int left,int right)//子数组原址重排,把下面的粘贴上来的的
{
int reference = a[right];
int index_left = left,index_right = right;
while(index_left!= index_right)
{
while(index_left!=index_right)//两个索引没到一起的话
{
if(a[index_left]>reference) break;//找找到了应该往右放的数
index_left++;//没找到就继续往右搜索
}
while(index_left!=index_right)
{
if(a[index_right]<reference) break;
index_right--;
}
Swap(a[index_left],a[index_right]);
}
Swap(a[index_left],a[right]);
return index_left;//返回最后这个参考元素的索引
}
void FastSortBook(int a[],int left ,int right)
{
if(left<right)
{
int q = RandomPartition(a,left,right);
FastSortBook(a,left,q-1);
FastSortBook(a,q+1,right);
}
return;
}
void FastSort(int a[],int left, int right)//a是数组,left,right是数组的索引,即0和n-1 ,这一版是用两个索引,同时找到两侧不符合左小右大的情况数,然后换一次,优化了交换的次数。
{
if(left >= right)
return;
//在这里新加入的随机化
int i = RandFast(left,right);
Swap(a[i],a[right]);
int reference = a[right];//把右边的做参考的话,最后需左边是大于参考,右边是小于参考
int index_left = left,index_right = right;
i = 0;
while(index_left!= index_right)
{
while(index_left!=index_right)//两个索引没到一起的话
{
if(a[index_left]>reference) break;//找找到了应该往右放的数
index_left++;//没找到就继续往右搜索
}
//cout<<"left="<<index_left<<endl;
while(index_left!=index_right)
{
if(a[index_right]<reference) break;
index_right--;
}
// cout<<"right="<<index_right<<endl;
//找到两个位置都不对的数
Swap(a[index_left],a[index_right]);
}
//最后左右指针碰到一起的时候,因为先移动的左边指针,所以最后是左边指针指到了 右边大于参考的数组所以把参考跟最后这个碰到的数换一下
Swap(a[index_left],a[right]);
FastSort(a,left,index_left-1);
FastSort(a,index_left+1,right);
}
/*
void FastSort(int a[],int left, int right)//a是数组,left,right是数组的索引,即0和n-1,这个版本是每次找到一个小于参考元素的,然后从左遍历一遍,调用swap从左开始填空,
{
if(left >= right)
return;
//这里新加入的随机化
int i = RandFast(left,right);
Swap(a[i],a[right]);
int reference = a[right];
int index = left;
i = 0;
for(i=left;i<right;i++)
{
if(a[i]<reference)
{
Swap(a[index],a[i]);
index++;
}
}
Swap(a[index],a[right]);
FastSort(a,left,index-1);
FastSort(a,index+1,right);
}
*/
归并排序
归并排序也是分治法的思路,假设两个小部分已经排序完了,再根据这两个子数组,从里面一个个取小的数,填到一个数组里即可。所以归并排序不能原址,需要一个额外的空间暂存数组。
思路就是,两个子数组已经排好序了(分别是left到middl与middle+1到right,两个子数组),每次从里面各取一个最小的数比一下,选小的放到最后的数组里即可,这里可能会有一个先取完,所以就定义一个专门的GetNum方法,
//归并排序
#include <iostream>
#include <stdlib.h>
#include <time.h>
#define N 50
using namespace std;
void MergeSort(int a[],int left,int right,int b[]);//定义middle,调用自身排好left到middle,middle+1到right,再调用Merge利用子数组排好a,b是一开始送进去当临时空间的数组,后来改动态内存就没有用了
int GetNum(int a[],int left,int right);//从子数组里面获得当前的小的数,如果子数组取完了就返回int最大数
void Merge(int a[],int left,int middle,int right,int b[]);//用left到middle,middle+1到right,两个子数组,排好a,b同上,没用了
int main()
{
int a[N]={5,4,3,2,1,6} ,i;
int b[N];
int n=N;
srand((unsigned)time(NULL));
for(i=0;i<n;i++)
{
a[i] = rand();
}
MergeSort(a,0,n-1,b);
for(i=0;i<n;i++)
{
cout<<a[i]<<endl;
}
}
void MergeSort(int a[],int left,int right,int b[])
{
if(left>=right) return;
int middle = (left+right)/2;
MergeSort(a,left,middle,b);//把左右组都排序了
MergeSort(a,middle+1,right,b);
Merge(a,left,middle,right,b);//左右都是排序好的再排序。
}
void Merge(int a[],int left,int middle,int right,int b[])
{
int n1 = middle-left;
int n2 = right-middle;
int index_left = left,index_right = middle+1;
int num_left,num_right;
int i;
/*for(i=left;i<left+(right-left+1);i++)//一开始的b当临时数组,没用了
{
b[i]=a[i];
}*/
int *store = new int[right-left+1];
for(i=0;i<(right-left+1);i++)//a拷贝到store里面,取出来重排到a里面
{
store[i]=a[i+left];
}
for(i=left;i<left+(right-left+1);i++)
{
num_left = GetNum(store,index_left-left,middle-left);//这送进去是store数组,所以索引从0开始,与a的索引i不一样了,
num_right = GetNum(store,index_right-left,right-left);
if(num_left<num_right)
{
a[i] = num_left;
index_left++;
}
else
{
a[i] = num_right;
index_right++;
}
cout<<"i="<<i<<" "<<"ai="<<a[i]<<endl;
}
delete [] store;
}
int GetNum(int a[],int left, int right)//right用最右边的索引,这样就需要left>right
{
if(left>right) return 2147483647;
else return a[left];
}