快速排序算法及其改进算法实现

      快速排序算法不稳定。

      快速排序算法在大多数的计算机上运行得都比其他排序算法快,而且排序算法消耗资源少。就平均时间而言快排是所有内部排序中最好的一个。对于已经排好的数组,最速排序有最坏时间复杂度为o(n^2)。当数组长度很小时,快排往往比其他排序方法要慢。

      快排的代码,关键是parition算法。

void QuickSort(int arr[],int low,int high)
{
	if(low<high){
		int pivotPos = partion(arr,low,high);
		QuickSort(arr,low,pivotPos-1);
		QuickSort(arr,pivotPos+1,high);
	}
}


Paition的三种实现方法

(1)arr[low]的第一个元素为pivot,设两个指针lessIndex=low(该索引之前的数比pivot小,初始值为low),另一个i从low+1开始遍历数组,找到一个比pivot小的数,lessIndex+1,然后交换到前面来。

快速排序算法及其改进算法实现_第1张图片


int partion1int arr[],int low,int high)
{
	if(!arr||low<0||low>high)
		throw new exception("NULL Array");
	//left第一个元素被当做pivot
	int pivot = arr[low];
	int lessIndex = low;

	for(int i=low+1;i<=high;++i){
		if(arr[i]<pivot){
			lessIndex++;
			if(lessIndex!=i){
				int temp = arr[lessIndex];
				arr[lessIndex] = arr[i];
				arr[i] = temp;
			}
		}	
	}
	//这一步破坏了稳定性
	arr[low] = arr[lessIndex];
	arr[lessIndex] = pivot;

	return lessIndex;
}

(2)i = low+1从左向右遍历找到一个比pivot大的数停止,然后等待high从右往左遍历找到一个pivot小的数,两者交换,然后继续寻找。

快速排序算法及其改进算法实现_第2张图片

int partion2(int arr[],int low,int high)
{
	if(!arr||low<0||low>high)
		throw new exception("NULL Array");
	//left第一个元素被当做pivot
	int pivot = arr[low];
    int i = low+1;
	int j = high;
	if(low<high){
		for(;;){
			while(i<=j&&arr[i]<pivot)++i;
			while(i<=j&&arr[j]>pivot)--j;
			if(i<j){
				swap(arr[i],arr[j]);
				++i;
				--j;
			}
			else
				break;
		}
		arr[low] = arr[j];
		arr[j] = pivot;
	}
	
	return j;
	
}


(3)将第一个元素arr[low]提出来作pivot,然后从j = high向前搜索第一个比pivot小的元素架设为arr[k],该元素放在arr[low]的位置。因为arr[low]已经保存pivot覆盖页没关系,于是arr[k]又可以被覆盖了,从前往后搜索比pivot大的元素放到arr[k]。一直进行下去直到i=j。

int partion3(int arr[],int low,int high)
{
	if(!arr||low<0||low>high)
		throw new exception("NULL Array");
	//left第一个元素被当做pivot
	int pivot = arr[low];
	int i = low;
	int j = high;


	while(i<j){
		while(i<j&&arr[j]>pivot)j--;
		if(i<j){
			arr[i] = arr[j];
			i++;
		}
		while(i<j&&arr[i]<pivot)i++;
		if(i<j){
			arr[j] = arr[i];
			j--;
		}
	}
    arr[i] = pivot;
	return i;

}

快排的非递归算法

     这里我贴一个非递归的代码:http://blog.csdn.net/wdzxl198/article/details/11999191

#include<iostream>
#include<stack>

using namespace std;

int Partition(int array[],int lhs,int rhs)
{
	int x = array[rhs];
	int i = lhs - 1;

	for(int j=lhs;j<=rhs-1;j++)
	{
		if(array[j] <= x)
		{
			++i;
			std::swap(array[i],array[j]);
		}
	}
	std::swap(array[i+1],array[rhs]);
	return i+1;
}

void QuickSort(int *arr,int left,int right)
{
	stack<int> st;
	if(left < right)
	{
		int mid = Partition(arr,left,right);
		if(left < mid-1)
		{
			st.push(left);
			st.push(mid-1);
		}
		if(mid+1 < right)
		{
			st.push(mid+1);
			st.push(right);
		}

		while(!st.empty())
		{
			int q = st.top();
			st.pop();
			int p = st.top();
			st.pop();

			mid = Partition(arr,p,q);
			if(p < mid-1)
			{
				st.push(p);
				st.push(mid-1);
			}
			if(mid+1 < q)
			{
				st.push(mid+1);
				st.push(q);
			}       
		}
	}
	else
		return;
}

int main()
{
	int a[10] ={3,7,6,4,0,2,9,8,1,5};  

	for(int i=0;i<10;i++)
		cout<<a[i]<<" ";
	cout<<endl; 
	cout<<"快速排序:";	
	QuickSort(a,0,9);


	for(int i=0;i<10;i++)
		cout<<a[i]<<" ";

	cout<<endl;
	system("PAUSE");
	return 0;
}

快排的局限性

    (1)快排是一个效率很高的排序算法,但是对于长度很小的序列,快排效率低。研究表明长度在5~25的数组,快排不如插入排序。

     (2)pivot选择不当,将导致树的不平衡,这样导致快排的时间复杂度为o(n^2);

     (3)但数组中有大量重复的元素,快排效率将非常之低。

快排的改进算法

 (1)在递归过程,当排序的子序列小于预定的值M时,采用插入插入排序

void QuickSort_Insert(int arr[],int low,int high)
{
	if(high-low<=M){
		InsertSort(arr,low,high)
	else
		int pivotPos = partion(arr,low,high);
		QuickSort(arr,low,pivotPos-1);//子序列采用快排
		QuickSort(arr,pivotPos+1,high);//子序列采用快排
	}
}

(2)在划分过程中,对于小规模子序列不进行排序直接跳过,这样快排之后得到一个整体上几乎完全排好的序列。然后再用快排。

void  __QuickSort_Insert(int arr[],int low,int high)
{
	if(high-low<=M)return;
	int pivotPos = partion(arr,low,high);
	__QuickSort(arr,low,pivotPos-1);
	__QuickSort(arr,pivotPos+1,high);

}
void HybridSort(int arr[],int low,int high)
{	
     __QuickSort(arr,low,high);
     InsertSort(arr,low,high)
}


(3)取arr[high],arr[mid],arr[left]中位数为pivot。将三者中中间大小的元素挪到left位置,取pivot = arr[left],最大的元素挪到中间。

int Median(int arr[],int len)
{
	if(!arr||len<=0)
		throw new exception("NULL Array");

	int left = 0;
	int right = len - 1;
	int mid = left + (right - left>>1);
	int minIndex = right;

	if(arr[minIndex]>arr[mid])minIndex = mid;
	if(arr[minIndex]>arr[left])minIndex = left;
	if(minIndex!=right)swap(arr[minIndex],arr[right]);
	if(arr[mid]<arr[left])swap(arr[left],arr[mid]);
    
	return left; 
}

(4)如果数组中重复元素多,就采用三路划分算法。


     这里出一个题目:以某个数为基准将一个数组分成三部分:第一部分表示小于该pivot,第二部分等于pivot,第三部分大于pivot,要得到三部分得区间范围。(其实相当于有个字符串“RRBBRGBG”转换成“RRRGGBBB”)

void ThreePartion(int arr[],int left,int right)
{
	if(!arr||right<=left)return;

	int less = left;//指向小与pivot的索引
	int greater = right;//指向大于pivot的元素
	int pivot = arr[left];
	int it = left;

	while(it<=greater){
		if(arr[it]==pivot)++it;
		else if(arr[it]<pivot){
			swap(arr[less],arr[it]);
			++it;
			++less;
		}
		else{
			swap(arr[greater],arr[it]);
			--greater;
		}

	}

}

      根据上面的思路,将快排填进去也就成了改进的快排了。

void QuickSort(int arr[],int left,int right)
{
	if(!arr||right<=left)return;

	int less = left;//指向小与pivot的索引
	int greater = right;//指向大于pivot的元素
	int pivot = arr[left];
	int it = left;

	while(it<=greater){
		if(arr[it]==pivot)++it;
		else if(arr[it]<pivot){
			swap(arr[less],arr[it]);
			++it;
			++less;
		}
		else{
			swap(arr[greater],arr[it]);
			--greater;
		}

	}
	QuickSort(arr,left,less-1);
	QuickSort(arr,greater+1,right);

}

你可能感兴趣的:(快速排序,改进快速排序)