思路:
1、先从数列中取出一个数作为基准数。
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3、再对左右区间重复第二步,直到各区间只有一个数。
例如:
将low指向头,high指向尾,将基准值放在tmp中,一次快排过程,如图:
源代码:
int Partion(int *arr, int low, int high)//第一次快排
{
int tmp = arr[high];//选high作为基准
while (low < high)
{
while (low < high && arr[low] <= tmp)
{
low++;
}
if (low >= high)//high和low相遇
{
break;
}
else
{
arr[high] = arr[low];
}
while (low < high && arr[high] >= tmp)
{
high--;
}
if (low >= high)//high和low相遇
{
break;
}
else
{
arr[low] = arr[high];
}
}
arr[high] = tmp;//arr[low] = tmp;
return high;
}
快排递归调用:
void Quick(int *arr, int start, int end)
{
int par = Partion(arr, start,end);
if (par > start+1)
{
Quick(arr,start,par-1);
}
if (par < end - 1)
{
Quick(arr,par+1,end);
}
}
void Quick_Sort1(int *arr, int len)//快速排序递归
{
Quick(arr,0,len-1);
}
快排非递归调用:
快排非递归调用是用栈来保存每次low和high的值,low先入栈,high就先出栈。
void Quick_Sort(int *arr, int len)//快速排序非递归
{
int lenstack = (int)(log10((double)len) / log10((double)2));
int *stack = (int *)malloc(sizeof(int)*2*lenstack);
assert(stack != NULL);
int top = 0;
int low = 0;
int high = len - 1;
int par = Partion(arr,low,high);
if (par > low + 1)
{
stack[top++] = low;//low先入栈
stack[top++] = par - 1;
}
if (par < high - 1)
{
stack[top++] = par + 1;//par + 1先入栈
stack[top++] = high;
}//第一次入栈
while (top > 0)
{
high = stack[--top];//high先出栈
low = stack[--top];//low后出栈
int par = Partion(arr, low, high);
if (par > low + 1)
{
stack[top++] = low;//low先入栈
stack[top++] = par - 1;
}
if (par < high - 1)
{
stack[top++] = par + 1;//par + 1先入栈
stack[top++] = high;
}
}
free(stack);
stack = NULL;
}
主函数:
int main()
{
int arr[] = {3,2,4,1,6,5,0,8,9,7};
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr,len);
Quick_Sort1(arr,len);
//Mearge_Sort(arr,len);
Show(arr, len);
return 0;
}
关于快排的优化,会在后面进行。。。。。。
今天学到了三种快排的优化(也不算是优化吧,)
第一种:随机去基准
这种方法增加了快排的实用性,不仅针对特殊的,也可以对常规的排序。
int Partion(int *arr, int low, int high)
{
int tmp = arr[low];//选基准
while (low < high)
{
while (low < high && tmp <= arr[high])
{
high--;
}
if (low >= high)
{
break;
}
else
{
arr[low] = arr[high];
}
while (low < high && arr[low] <= tmp)
{
low++;
}
if (low >= high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
//随机选取基准
void Swap(int *arr, int start, int end)
{
int tmp;
tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
}
void Quick(int *arr, int start, int end)
{
Swap(arr,start,rand()%(end-start)+start);//end = rand()%(end-start)+start随机选基准
int par = Partion(arr, start, end);
if (par > start + 1)
{
Quick(arr, start, par-1);
}
if (par < end - 1)
{
Quick(arr, par+1, end);
}
}
void Quick_Sort1(int *arr, int len)
{
Quick(arr, 0, len - 1);
}
void Show(int *arr, int len)
{
for (int i = 0;i < len;++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 2,5,3,2,5,5,7,5,6 };
//int arr[] = {9,8,7,6,5,4,3,2,1};
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
Quick_Sort1(arr, len);
Show(arr, len);
return 0;
}
第二种:三位取中法
选的基准更贴合实际,省去了很多麻烦,时间复杂度也减小了。
void Median_Of_Three(int *arr, int start, int mid, int end)//三数取中法
{
if (arr[mid] > arr[end])
{
Swap(arr, mid, end);
}
if (arr[mid] > arr[start])
{
Swap(arr, mid, start);
}
if (arr[start] > arr[end])
{
Swap(arr, start, end);
}
}
void Quick(int *arr, int start, int end)
{
Median_Of_Three(arr, start, start + (end - start) / 2, end);
int par = Partion(arr, start, end);
int left = par - 1;
int right = par + 1;
if (par > start + 1)
{
Quick(arr, start, par-1);
}
if (par < end - 1)
{
Quick(arr, par+1, end);
}
}
void Quick_Sort1(int *arr, int len)
{
Quick(arr, 0, len - 1);
}
void Show(int *arr, int len)
{
for (int i = 0;i < len;++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 2,5,3,2,5,5,7,5,6 };
//int arr[] = {9,8,7,6,5,4,3,2,1};
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
Quick_Sort1(arr, len);
Show(arr, len);
return 0;
}
第三种:基准聚焦法
这种方法才真正算是对快排的优化。当遇到与基准的值相同时,不用再对它进行比较,可将它靠近基准的旁边。
void Foucs_Nums(int *arr, int par, int *left, int *right, int start, int end)//遇到与基准一样的数字,把它们聚集到一起
void Foucs_Nums(int *arr, int par, int *left, int *right, int start, int end)//遇到与基准一样的数字,把它们聚集到一起
{
int Parleftindex = par - 1;
for (int i = par - 1;i >= start;i--)//2,5,3,2,5,5,7,5,6 从基准左边找
{
if (arr[i] == arr[par])
{
if (i != Parleftindex)
{
Swap(arr, i, Parleftindex);
Parleftindex--;
}
else
{
Parleftindex--;//Parleftindex右边的数是基准,不用交换
}
}
}
int Parrightindex = par + 1;
for (int j = par + 1;j <= end;j++)//从基准右边找
{
if (arr[j] == arr[par])
{
if (j != Parrightindex)
{
Swap(arr, j, Parrightindex);
Parrightindex++;
}
else
{
Parrightindex++;//Parrightindex左边的数是基准,不用交换
}
}
}
*left = Parleftindex;
*right = Parrightindex;
}
void Quick(int *arr, int start, int end)
{
//Swap(arr,start,rand()%(end-start)+start);//end = rand()%(end-start)+start
//Median_Of_Three(arr, start, start + (end - start) / 2, end);
int par = Partion(arr, start, end);
int left = par - 1;
int right = par + 1;
Foucs_Nums(arr, par, &left, &right, start, end);
if (par > start + 1)
{
Quick(arr, start, left);
}
if (par < end - 1)
{
Quick(arr, right, end);
}
}
void Quick_Sort1(int *arr, int len)
{
Quick(arr, 0, len - 1);
}
void Show(int *arr, int len)
{
for (int i = 0;i < len;++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 2,5,3,2,5,5,7,5,6 };
//int arr[] = {9,8,7,6,5,4,3,2,1};
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
Quick_Sort1(arr, len);
Show(arr, len);
return 0;
}
这就是我知道的关于快排的优化。