#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include <stdio.h>
#include<assert.h>
//交换
void swap(int *a,int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//三数取中(时间复杂度可以认为是O(N*logN),几乎不会出现最坏情况,大大减小了递归调用深度,有效提高算法效率;)
int getmidindex(int *a,int begin, int end)
{
int mid = (end - begin + 1) / 2;//中间指针
if (a[begin] < a[mid])
{
if (a[mid] < a[end])
{
return mid;
}
else if (a[begin] > a[end])
{
return begin;
}
else
{
return end;
}
}
else//a[begin]>=a[mid]
{
if (a[begin]<a[end])
{
return begin;
}
else if (a[mid]>a[end])
{
return mid;
}
else
{
return end;
}
}
}
//直接插入排序
void InsertSort(int* a, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
//当i=sz-2时,end也等于sz-2,tmp等于sz-1,代表所有的数都已加入数组,并开始排序最后一个数;
int end = i;//下标从0开始,代表刚开始数组只有一个数,然后用第二个数与第一个数比较;
int tmp = a[end + 1];//保留后一位数据;
//当end指针移动到第一个元素(下标为0),并且该元素小于tmp,end--等于-1,跳出循环后,把tmp(要进行排序的值)放到第一位;
while (end >= 0)
{
if (tmp < a[end])
{
a[end + 1] = a[end];//如果要插入的数比a[end]小,就把a[end]往后移一位;
end--;//索引往前挪一位;
}
else
{
break;
}
}
a[end + 1] = tmp;//直到遇到比tmp小的数,把他刚好放到这个数后面;
}
}
//hoare
int QuickSort1(int *a,int begin,int end)
{
//三数取中,把中间的值和第一个值交换,防止有序数组;
int mid = getmidindex(a, begin, end);
swap(&a[begin], &a[mid]);
int left = begin;//左指针
int right = end;//右指针
int keyi = left;//选取数组左边第一个数作为基准值,right指针先走,直到找到一个小于基准值的值,或者一把子和left相等;
//每次定位好一个值;
while (left < right)
{
//找小于key的;
while (left < right && a[right] >= a[keyi])//left<right防止数组越界,当left=right的时候,指针不再进行移动;带等于号,防止遇和key相等的值,right指针不动了,造成死循环;
{
right--;
}
//找大于key的;
while (left < right && a[left] <= a[keyi])
{
left++;
}
//交换大于key的值和小于key的,相当于把小数放到左边,大数放到右边;
swap(&a[left], &a[right]);
}
//当left==right,左右指针相遇,跳出大循环,就交换key和a[left]
swap(&a[left], &a[keyi]);
//现在基准值在a[left];
keyi = left;
return keyi;
}
//挖坑法
int QuickSort2(int* a, int begin, int end)
{
int left = begin;
int right = end;
int key = a[left];//把左边第一个值(基准值)保留到key中;
int hole = left;//第一个坑;
while (left<right)
{
while(left<right&&a[right]>=key)
{
right--;
}
a[hole] = a[right];
hole = right;
while(left < right && a[left] <= key)
{
left++;
}
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
//前后指针法
int QuickSort3(int* a, int begin, int end)
{
int prev = begin;
int cur = begin + 1;
int keyi = begin;
//单趟
//前后指针(cur找小)
while (cur <= end)
{
//防止出现cur到达数组最后一个,明明比a[keyi]大,却仍然进行交换;
//当a[cur]小于a[keyi],才进行++prev;
if (a[cur] < a[keyi] && ++prev != cur)//++prev如果等于cur,说明cur指向一个小于a[keyi]的数,但是prev和cur紧邻,相当于自己和自己交换没有必要!
{
swap(&a[prev], &a[cur]);
}
cur++;//无论什么情况,cur都得往后走;
}
swap(&a[prev], &a[keyi]);//这里的prev一定指向的是小于a[keyi];
keyi = prev;
return keyi;//返回一个已经确定好的值的索引;
int prev = begin;
int cur = begin + 1;
int keyi = begin;
while (cur<=end)
{
if (a[cur] < a[keyi]&&++prev!=cur)
swap(&a[cur],&a[prev]);
cur++;
}
swap(&a[keyi],&a[prev]);
keyi = prev;
return keyi;
}
//快速排序(递归)
//1.QuickSort1->hoare法
//2.QuickSort2->挖坑法
//3.QuickSort3->前后指针法
void QuickSort(int *a, int begin,int end)
{
//递归返回条件;
if (begin>=end)
return;
//为防止序列有序,每次都找最左边或者最右边(时间复杂度相当于O(n^2))||最后剩余一小部分数并没有必要使用递归(会导致算法复杂度变大很多);
//小区间用直接插入排序,减少递归调用次数;
if ((end - begin + 1) < 5)
{
InsertSort(a+begin, end-begin+1);
}
else
{
int keyi = QuickSort3(a,begin,end);
//递归调用
//如果a[left]和a[right]在最左边或者最右边相遇的话,那么将不存在左区间或右区间;
QuickSort(a, begin, keyi - 1);//左区间
QuickSort(a, keyi + 1, end);//右区间
}
}
int main()
{
int arr[] = {55,77,33,44,22,11,99,77,66,88};
int sz = sizeof(arr) / sizeof(arr[0]);
//快速排序;
int begin = 0;//左指针
int end = sz - 1;//右指针
QuickSort(arr,begin,end);
//打印检测
for (int i=0;i<sz;i++)
{
printf("%d ",arr[i]);
}
return 0;
}