快速排序,归并排序

快速排序

是原址排序

思路是,找一个参考元素,然后把小于参考元素的放左边,大于参考元素的放右边。再递归调用。

再进行之前可以先把数组随机化一次,可以达到平均时间复杂度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];
}

猜你喜欢

转载自blog.csdn.net/zhangzhi2ma/article/details/82558913