欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

改进版快速排序:针对部分有序列的策略与优化技巧" - 随机选枢轴:当数据部分有序时,传统快速排序通过固定枢轴可能导致效率低下。为此,我们采用随机选取枢轴的方法,代码如下: ```c int SelectPivotRandom(int arr[], int low, int high) { srand(time(0)); int pivotPos = (rand() % (high - low)) + low; swap(arr[pivotPos], arr[low]); return arr[low]; } ``` - 优化小数组交换:针对小且部分有序的数组,快速排序不如插入排序高效。因此,当待排序序列长度小于等于10时,我们会切换至插入排序: ```c #define MAX_LEN 10 void quicksort_optimized(int *arr, int left, int right) { int length = right - left; if (l

最编程 2024-02-22 12:59:28
...


快速排序的过程图:

快速排序的完整代码(递归):

#include<iostream>
using namespace std;
int partition(int *arr,int left,int right)
{
	int temp=arr[left];
	while(left<right)//直达left和right重合的时候,才找到合适的位置
	{
		//先从后往前找比基准小的
		while(left<right  &&  arr[right]>=temp)//当right的值大于temp的值的时候才执行
		        //等号一定得写,因为可能会出现,保存的temp元素和数据中的元素一样的,不写会出现死循环的现象
		{
			right--;
		}
		arr[left]=arr[right];//当right的值小于temp的值的时候执行
		//从前往后找,找比基准大的
		while(left<right  && arr[left] <=temp)//当left的值小于temp的值的时候执行
		{
			left++;
		}
		arr[right]=arr[left];//当left的值大于temp的时候执行
	}
	arr[left]=temp;//此时的left和right在同一个位置,此时为合适的位置,把temp的值给left
	return left;//此时返回的值是temp合适的位置,即小于它的在它的左边,大于它的在它的右边
}
 void quick(int *arr,int left,int right)
{
	if(left<right)
	{
		int pivot=partition(arr,left,right);
		quick(arr,left,pivot-1);
		quick(arr,pivot+1,right);
	}
}
void quick_sort(int *arr,int len)
{
	quick(arr,0,len-1);
}
int main()
{
	int arr[]={9,5,7,10,45,12};
	int len=sizeof(arr)/sizeof(arr[0]);
	quick_sort(arr,len);
	for(int k=0;k<len;++k)
	{
		cout<<arr[k]<<"  ";
	}
	cout<<endl;
}

快速排序的完整代码(非递归):

#include<stack>
#include<iostream>
using namespace std;
 
int partition(int *arr,int left,int right)
{
	int temp=arr[left];//基准
	while(left<right)
	{
		//先从后往前找比基准小的
		while(left<right && temp<=arr[right])
		{
			right--;
		}
		arr[left]=arr[right];
		//从前往后找比基准大的
		while(left<right  && temp>=arr[left])
		{
			left++;
		}
		arr[right]=arr[left];
	}
	arr[left]=temp;
	return left;
}
//其实就是用栈保存每一个待排序子串的首尾元素的下标
void q_sort(int *arr,int left,int right )
{
	stack<int>  st;
	int pos=0;
    st.push (left);
	st.push (right);
	while(!st.empty())
	{
		right=st.top();
		st.pop();
		left=st.top();
		st.pop ();//5,6,8,2,9,4,1,3,45,89,65
		pos=partition(arr,left,right);
		if(pos+1<right)//先入基准右边的,如果基准右边只有一个元素的时候,就不用入了
		{
			st.push (pos+1);
			st.push (right);
		}
		if(pos-1>left)//再入基准左边的,如果基准左边只有一个元素的时候,就不用入了
		{   
			st.push (left);
			st.push (pos-1);
			
		}
	}
}
 
void quick_sort(int *arr,int len)
{
	q_sort(arr,0,len-1);
}
int main()
{
	int arr[]={5,6,8,2,9,4,1,3,45,89,65};
	int len=sizeof(arr)/sizeof(arr[0]);
	quick_sort(arr,len);
	for(int i=0;i<len;i++)
	{
		cout<<arr[i]<<"  ";
	}
	cout<<endl;
}

快速排序的各种优化:
优化一:三数取中法,解决数据基本有序的(就是找到数组中最小下标,最大下标,中间下标的数字,进行比较,把中间大的数组放在最左边)

代码:

//*************************************************************************
void swap(int *arr,int left,int right)
{
int temp;
temp=arr[left];
arr[left]=arr[right];
arr[right]=temp;	
}
//***************************************************************************
int partition(int *arr,int left,int right)
{

//***************************
//e.g:9,1,5,8,3,7,4,6,2
int m=left+(right-left)/2;//找到中间的数字的下标
if(arr[left]>arr[right])//最左大于最右的时候,交换左右
{
swap(arr,left,right);
}
if(arr[m]>arr[right])//如果中间的>right ,交换
{
swap(arr,m,right);
}
if(arr[m]>arr[left])//如果中间的>left,交换
{
swap(arr,m,right);
}
//经过交换之后low变为3
//****************************
int temp=arr[left];//基准
while(left<right)//知道left和right重合的时候,才找到合适的位置
{ //从后向前找到比小的数字
while(left<right && arr[right]>=temp)//当right的值大于temp的值的时候才执行
{
right--;
}
arr[left]=arr[right];//当right的值小于temp的值的时候执行
while(left<right && arr[left] <= temp)//从前往后找到比基准大的数字
{
left++;
}
arr[right]=arr[left];//当left的值大于temp的时候执行
}
arr[left]=temp;//此时的left和right在同一个位置,此时为合适的位置,把temp的值给left
return left;//此时返回的值是temp合适的位置,即小于它的在它的左边,大于它的在它的右边
}


优化二:

随机选取基准

引入的原因:在待排序列是部分有序时,固定选取枢轴使快排效率底下,要缓解这种情况,就引入了随机选取枢轴

思想:取待排序列中任意一个元素作为基准

/*随机选择枢轴的位置,区间在low和high之间*/  
int SelectPivotRandom(int arr[],int low,int high)  
{  
  //产生枢轴的位置  
   srand((unsigned)time(NULL));  
  int pivotPos = rand()%(high - low) + low;   
  //把枢轴位置的元素和low位置元素互换,此时可以和普通的快排一样调用划分函数  
  swap(arr[pivotPos],arr[low]);  
   return arr[low];  
}  

优化三:优化小数组的交换,就是为了解决大才小用问题

原因:对于很小和部分有序的数组,快排不如插排好。当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,此时可以使用插排而不是快排

截止范围:待排序序列长度N = 10,虽然在5~20之间任一截止范围都有可能产生类似的结果,这种做法也避免了一些有害的退化情形。

#define  max_len  10
void quick(int *arr,int left,int right)
{
    int length=right-left;
    if(length>max_len )
    {
        int pivot=partition(arr,left,right);
        quick(arr,left,pivot-1);
        quick(arr,pivot+1,right);
    }
    else
    {
    //用插入排序
    }
}

 

优化四:

 在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割

举例:

待排序序列 1 4 6 7 6 6 7 6 8 6

三数取中选取枢轴:下标为4的数6

转换后,待分割序列:6 4 6 7 1 6 7 6 8 6

             枢轴key:6

推荐阅读