/*
快速排序
基本思想
选定每次排序的基准数据 在剩下的位置将小于基准值的数据放在基准值得左边,大于基准值的数据放到基准值的右边
一次划分之后 如果此基准值的左右两边仍存在大于两个数据 则继续划分排序 直至每个数字都有序
递归实现Quick_Sort1(int *arr,int len):快排一次划分的时间复杂度为O(logn) 最坏就是在有序条件下 时间复杂度为O(n^2)
整体时间复杂度为O(n*logn)
非递归实现Quick_Sort2(int *arr,int len)
快排三种实现方式
第一种就是选取第一个元素或者最后一个元素作为基准 代码是Quick_Sort1 和Quick_Sort2
这样的坏处是如果所给序列是有许多的 那么时间复杂度太大 选取的基准点就失去了意义 并不能称作是基准值了
第二种随机取值法
绝大多数情况下保证了时间复杂度最好情况是O(logn) 但是最坏情况下 假如整个数组的数字都相等 那么时间复杂度还是能达到O(n^2)
第三种 三数取中
找到low high mid = low + ((high - low) >> 1);//计算数组中间的元素的下标
取这三个下标对应的最小值 保证arr[low]是这三个数之间的第二大值 然后又可以和普通划分函数一样
*/
void swap(int *a,int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//第一种划分函数:以第一个数字为基准值进行一次划分 最后返回基准的下标
int partion(int *arr,int low,int high)
{
if(arr == NULL )
return -1;
int tmp = arr[low];
while(low < high)
{
while((low < high) && arr[high] >= tmp)
{
high--;
}
if(low == high)//如果遇到比基准值小的 放到low的位置
{
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;
}
//第二种划分函数:随机选取基准划分
int part(int *arr,int low,int high)
{
srand((unsigned)time(NULL));
//产生随机位置
int pivotPos = rand()%(high - low) + low;
//将此随机位置的值与low位置的值交换 又可以和普通划分函数一样
swap(arr[pivotPos],arr[low]);
int tmp = arr[low];
while(low < high)
{
while((low < high) && arr[high] >= tmp )
{
high--;
}
if(low == high)//如果遇到比基准值小的 放到low的位置
{
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;
}
//第三种划分函数
int Select_Mid(int *arr,int low,int high)
{
//int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标
int mid = low + ((high - low) >> 1);
//使用三数取中法选择枢轴
if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]
{
swap(arr[mid],arr[high]);
}
if (arr[low] > arr[high])//目标: arr[low] <= arr[high]
{
swap(arr[low],arr[high]);
}
if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]
{
swap(arr[mid],arr[low]);
}
return arr[low];
}
//三数取中的一次划分
int part1(int *arr,int low,int high)
{
int tmp = Select_Mid(arr,low,high);
while(low < high)
{
while((low < high) && arr[high] >= tmp )
{
high--;
}
if(low == high)//如果遇到比基准值小的 放到low的位置
{
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;
}
static void Quick(int *arr,int start,int end)
{
int par = part1(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)
{
if(arr == NULL ||len < 0)
return;
for(int i = 0 ;i < len - 1 ;++i)
{
Quick(arr,0,len-1);
}
}
快排非递归实现
//用栈保存每一次的一对low和high区间 空间复杂度为O(n*logn)
void Quick_Sort2(int *arr,int len)
{
if( arr == NULL ||len < 0)
return ;
int low = 0;
int high = len - 1;
int par = partion(arr,low,high);
stack<int> st;
if(par > low + 1)
{
st.push(low);
st.push(par-1);
}
if(par < high - 1)
{
st.push(par+1);
st.push(high);
}
while(!st.empty())
{
int high = st.top();st.pop();
int low = st.top();st.pop();
int par = partion(arr,low,high);
if(par > low + 1)
{
st.push(low);
st.push(par-1);
}
if(par < high - 1)
{
st.push(par+1);
st.push(high);
}
}
}
/*
快排优化一
在low和high之间的举例为10以内时,可以选择直接插入排序 节省时间
*/
//快排优化一 在划分到适合的长度进行直接插入排序
void insert_sort(int *arr,int low,int high)
{
if(arr == NULL || low < 0 || high < 0)
return ;
int tmp = 0;
int j;
for(int i = low+1;i < high; i++)
{
tmp = arr[i];
for(j = i-1;j >= 0;j--)
{
if(arr[j] > tmp)
{
arr[j+1] = arr[j];
}
else
{
break;
}
}
arr[j+1] = tmp;
}
}
static void Quick1(int *arr,int start,int end)
{
int par = part1(arr,start,end);
if(end - start + 1 < 10)
{
insert_sort(arr,start,end);
}
else
{
if(par > start + 1)
{
Quick(arr,start,par-1);
}
if(par < end -1)
{
Quick(arr,par+1,end);
}
}
}
void Quick_Sort3(int *arr,int len)
{
if(arr == NULL ||len < 0)
return;
for(int i = 0 ;i < len - 1 ;++i)
{
Quick(arr,0,len-1);
}
}
/*
快排优化二
在一次划分之后 将与基准值相等的元素聚集在一起不再进行下一次的划分操作 可以减少迭代查找次数
具体操作将与基准值相等的数放到数组的两端 一次划分完成后 将这些数字挪回基准值得范围 具体过程如下图所示
*/
//快排优化二 一次划分后将基准值调至靠近基准值周围 与基准值相等的数字不再参加下一次划分
void QuickSort(int *arr,int low,int high)
{
int first = low;
int last = high;
int left = low;
int right = high;
int leftLen = 0;
int rightLen = 0;
//一次分割
int key = Select_Mid(arr,low,high);//使用三数取中法选择枢轴
while(low < high)
{
while(high > low && arr[high] >= key)
{
if (arr[high] == key)//处理相等元素
{
swap(&arr[right],&arr[high]);
right--;
rightLen++;
}
high--;
}
arr[low] = arr[high];
while(high > low && arr[low] <= key)
{
if (arr[low] == key)
{
swap(&arr[left],&arr[low]);
left++;
leftLen++;
}
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
//一次快排结束
//把与枢轴key相同的元素移到枢轴最终位置周围
int i = low - 1;
int j = first;
while(j < left && arr[i] != key)
{
swap(&arr[i],&arr[j]);
i--;
j++;
}
i = low + 1;
j = last;
while(j > right && arr[i] != key)
{
swap(&arr[i],&arr[j]);
i++;
j--;
}
//如果左右两端都还有超过两个数据 那么就需要进行划分排序
if(low-1 > first+1)
{
QuickSort(arr,first,low - 1 - leftLen);
}
if(low+1 < last-1)
{
QuickSort(arr,low + 1 + rightLen,last);
}
}