在学习快速排序之前 首先 我们需要明白什么是快速排序呢?它的逻辑是怎样的?
下面代码配有注释 可以边看边思考
#include<stdio.h>
#include<assert.h>
void Swap(int* p1, int* p2)
{
int tmp = * p1;
*p1 = *p2;
*p2 = tmp;
}
void Print(int* arr, int n)
{
assert(arr);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int GetMidIndex(int* arr, int left, int right)
{
int mid = left + ((right - left) >> 1);
//arr[left]>arr[mid]
if (arr[left] > arr[mid])
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[right] > arr[left])
{
return left;
}
else
{
return right;
}
}
//arr[left]<arr[mid]
else
{
if (arr[right] > arr[mid])
{
return mid;
}
else if (arr[right] < arr[left])
{
return left;
}
else
{
return right;
}
}
}
//三数取中 —— 三数取中的意义在哪呢 避免arr[keyi]值过大 降低快排的效率
//快排就是确定一个值(尽可能将数组由大到小(由小到大)平分的值 就是中间值)
//通过左右寻找比arr[keyi]大 或 小的值进行交换 将大的值放到一侧将小的值放到另一侧
//最终确定 arr[keyi]的真正位置
//再利用递归 将keyi 两侧的区间进行 处理
//怎样处理的呢 也是和上面一样的操作
//将区间中的arr[keyi]放到它该去的位置 如此反复 将所有区间中的数都放在该呆的位置 再不断的返回排好的区间
// 8 ,5,2,6 ,4, 9,7,3, 1
// left mid right
// 三数取中 进行交换
// 4 5 2 6 8 9 7 3 1
// left right
// keyi
//寻找大小值进行交换 右边先走 然后左边再走 找到比keyi小的 和大的就进行交换 接着找 循环结束之后
//keyi两侧一侧是大的 一侧是小的
//4 5 2 6 8 9 7 3 1
//keyi
// left right
//
//4 1 2 6 8 9 7 3 5
//keyi
// left right
//
// 4 1 2 6 8 9 7 3 5
// keyi
// right
// left
//
// 4 1 2 3 8 9 7 6 5 一次交换
// keyi
// right
// left
// 4 1 2 3 8 9 7 6 5 一次交换
// keyi
// right
// left (3<= 4 没进入if语句 所以left没++)跳出循环 进行交换 arr[keyi] 和 arr[left]
//
// 3 1 2 4 8 9 7 6 5 一次交换
// keyi
// right
// left (这样4就位于它该呆的位置)
// 3 1 2 | 4 | 8 9 7 6 5 一次交换
// keyi
// right
// left
//
// [left ,keyi - 1] keyi(最终Partion返回的是left下标 keyi接受) [keyi+1,left]
//
//hoare
//[left,right]
//快排的最初设计
int Partion1(int* arr, int left, int right)
{
// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
//三数取中 —— 三数取中的意义在哪呢 避免arr[keyi]值过大 降低快排的效率
int min = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[min]);
int keyi = left;
while (left < right)
{
if (left <= right && arr[right] < arr[keyi])
right--;
if (left <= right && arr[left] > arr[keyi])
left++;
Swap(&arr[right], &arr[left]);
}
Swap(&arr[keyi], &arr[left]);
return left;
}
//因为快排的最初设计有许多人可能没理解 所以就有大佬 设计了下面的“挖坑法”
//字面上理解就是将一个数字挖掉 该位置变成了一个空位置 谁都可以来这个坑位 而且还不会影响数组中的元素个数
//当然被挖掉的数字不会被丢掉 被丢掉那还怎么排 它会保存在另一个位置 等到有新的坑位再将它放进去
//如果前面理解清楚了 这个就很好理解 第一个被挖掉的数字 就相当于最初设计快排思想 里面的 arr[keyi]
//它是最后一个放进去的
//这个挖坑法大家可以通过上面的例子去画一画这个函数的运行逻辑
//挖坑法
int Partion2(int* arr, int left, int right)
{
int min = GetMidIndex(arr, left, right);
Swap(&arr[left], &arr[min]);
int key = arr[left];
int pivot = left;
while (left < right)
{
while (left < right && arr[right] >= key)
--right;
arr[pivot] = arr[right];
pivot = right;
while (left < right && arr[left] <= key)
++left;
arr[pivot] = arr[left];
pivot = left;
}
//最后将刚开始存在key里面的值放在它该待在的位置
arr[pivot] = key;
return pivot;//这两种写法都可以 最后一定是返回pivot或left 前提是右边先走
//return right; //试了一下返回这两种都可以
//return left;
}
//还有种快排思想 相比于前两种 这种方法确实不同 它是通过快慢指针去寻找大于或小于 目标值(arr[keyi])
//例子演示
//8,5,2,6,4,9,7,3,1
//三数取中处理之后
//4, 5,2,6,8,9,7,3,1
//keyi cur
//prev
//
//4, 5, 2,6,8,9,7,3,1
//keyi cur (没进去最里面while) 只进行了cur++ 又因为左边先为假 所以不进行右边的条件判断你 prev就没有加
//prev
//
//4, 5, 2,6,8,9,7,3,1
//keyi cur
// prev(满足条件 进入while循环 进行Swap交换)
//
//4, 2, 5, 6,8,9,7,3,1
//keyi cur
// prev
// 省略中间的cur++ 步骤
//4, 2, 5, 6, 8,9,7, 3, 1
//keyi cur
// prev
// 交换
//4, 2, 3, 6, 8,9,7, 5, 1
//keyi cur
// prev
//
//4, 2, 3, 6, 8,9,7, 5, 1
//keyi cur
// prev
// 交换
//4, 2, 3, 1, 8,9,7, 5, 6
//keyi cur
// prev
// 最后将arr[prev] 和 arr[keyi]交换
//1, 2, 3, 4, 8,9,7, 5, 6
//keyi cur
// prev
//[left, prev-1] prev [ prev+1 ,right] 最终只能返回prev
//
//
//快慢指针
int Partion3(int* arr, int left, int right)
{
//三数取中
int min = GetMidIndex(arr, left, right);
Swap(&arr[min], &arr[left]);
int keyi = left;
int prev = left;
int cur = prev + 1;
while (cur <= right)
{
while (arr[keyi] >= arr[cur] && ++prev != cur)
Swap(&arr[prev], &arr[cur]);
cur++;
}
Swap(&arr[keyi], &arr[prev]);
return prev;
}
//快速选择排序
//递归版本
void QuickSort(int* arr, int left, int right)
{
//终止条件
if (left >= right)
return;
else
{
//int keyi = Partion1(arr, left, right);
int keyi = Partion2(arr, left, right);
//int keyi = Partion3(arr, left, right);
//[left,keyi-1] keyi [keyi+1,right]
QuickSort(arr, left, keyi - 1);
QuickSort(arr, keyi + 1, right);
}
}
int main()
{
int arr[] = { 8,5,2,6,4,9,7,3,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0, right = sz - 1;
Print(arr, sz);
QuickSort(arr, left, right);
Print(arr, sz);
以上代码有什么 问题 留言私信~