1. 左右指针法实现快排
方法一:左右指针
算法思想:
- 先选出数组最后元素作为key。
- 左边的指针从左开始找到大于等于key的值,右边的指针从右边开始找到小于key的值,在做交换,使得大于等于key的值放在数组的右边,小于key的值放在数组的左边。
- 注意最后的时候,要把最后的key值换到左边指针的下一个位置,保证了key值右边的数比key大,左边的数比key小。
- 借助递归,不断的缩小区间,使得小区间有序,最后就会整体有序
总结出来就是:左边找大,右边找小。
代码实现:
int Partition1(vector<int>& nums, int left, int right)
{
int l = left;
int r = right;
int key = nums[right]; // 选出最右边的数作为key值
while (l < r)
{
while (l < r && nums[r] >= key) // r只停留在比key值小的数
{
r--;
}
while (l < r && nums[l] < key) // l只停留在 >= key值的位置
{
l++;
}
if (l < r) // 如果此时的 l < r 就会把 小于key的值交换到左边, 大于等于key的值交换到右边
{
swap(nums[l++], nums[r--]); // 交换完之后l++ 、 r--
}
}
swap(nums[l+1], nums[right]);
return l + 1;
}
void QuickSort(vector<int>& nums, int left, int right)
{
if (left >= right)
return;
int mid = Partition1(nums, left, right); // 选出边界,在继续递归,使得小区间有序
QuickSort(nums, left, mid - 1); // [left, mid - 1] 使有序
QuickSort(nums, mid + 1, right); // [mid + 1, right] 使有序
}
int main()
{
vector<int> nums = { 6, 4, 5, 4, 6 };
QuickSort(nums, 0, nums.size() - 1);
for (auto e : nums)
{
cout << e << " ";
}
cout << endl;
system("pause");
}
结果:
方法二:前后指针
算法思想:
- 选出数组最右边的数作为key值
- 也是两个指针fast和slow,快慢指针。快指针fast一直往前走,慢指针slow维护着从left开始到slow全是小于key值的区间。即【left,slow】都是小于key的值
- 快指针fast找小于key值位置,与慢指针slow的下一个位置进行交换。使得小于key值在数组的左边,大于key值的数在数组右边
- 最后交换key值到slow的下一个位置,保证左边的值比key值小,右边的值比key值大
- 通过递归,使得小区间有序,到大区间有序
代码实现:
int Partition2(vector<int>& nums, int left, int right)
{
int key = nums[right];
int slow = left - 1; //刚开始的时候为left的前一个位置
for (int fast = left; fast < right; ++fast) //fast一直往前走,去找小于等于key的数
{
if (nums[fast] < key) // 如果fast指针指向的值比key值小,则slow指针就可以向前扩一个,在将slow位置与fast位置交换
{
slow = slow + 1;
swap(nums[slow], nums[fast]);
}
}
//slow向前扩一下,slow + 1最后将key值交换到slow+1的位置,保证key值左边的比它下,右边大于等于它
swap(nums[slow + 1], nums[right]);
return slow + 1;
}
void QuickSort(vector<int>& nums, int left, int right)
{
if (left >= right)
return;
int mid = Partition2(nums, left, right);
QuickSort(nums, left, mid - 1);
QuickSort(nums, mid + 1, right);
}
int main()
{
vector<int> nums = { 6, 3,8,1,7,1,6 };
QuickSort(nums, 0, nums.size() - 1);
for (auto e : nums)
{
cout << e << " ";
}
cout << endl;
system("pause");
}
结果:
2. 挖坑法实现快排
算法思想:
- 我们选择数组中最右边的元素作为坑(hole),先将这个值保存为key。
- left指针找大于key的数,假设找到x,用x先把坑(hole)填上,此时x位置留下一个坑
- right指针找小于key的数,假设找到y,则用y将上一次x的坑填上
- 步骤2,3循环往复,最后肯定还有一个坑没有填充,我们要把最开始选定的hole填到坑的位置。
代码实现:
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int PartSort(int* a, int left, int right)
{
int key = a[right]; //选定最后一个值作为坑,保存坑的值
while (left < right)
{
while (left < right && a[left] <= key) //找到大于key的数
{
left++;
}
a[right] = a[left]; //此时填充上一次的坑
while (left < right && a[right] >= key) // 找到小于key的数
{
right--;
}
a[left] = a[right]; // 填坑的过程
}
a[right] = key; // 将刚开始保存的key填到最后一个坑上
return left;
}
void QuickSort(int*a, int left, int right)
{
if (left >= right)
return;
int mid = PartSort(a, left, right);
QuickSort(a, 0, mid - 1);
QuickSort(a, mid + 1, right);
}
void Print(int *a, int n)
{
for (int i = 0; i < n ; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
int a[] = { 6,2,1,3,4,5,6};
int sz = sizeof(a) / sizeof(a[0]);
QuickSort(a, 0, sz - 1);
Print(a,sz);
system("pause");
}
结果:
3. 前后指针法实现快排
算法思想:
- 刚开始的话 : prev处于cur的前一个位置,也就是cur下标-1的位置
- 移动的过程 : prev永远指着比key值大的数的前一个位置 ;cur一直向前遍历找到比key值小的数
- cur没有遇到比key大的数,不交换。
- cur遇到比key大的数, 将cur对应的数据与prev对应的数据进行交换
整体的过程就是: 推着大的数据往前走,把小的翻到左边
代码实现:
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int PartSort(int* a, int left, int right)
{
int key = a[right];
int prev = left - 1;
int cur = left;
while (cur < right)
{
if (a[cur] < key && ++prev != cur)
{
swap(&a[prev], &a[cur]);
}
cur++;
}
++prev;
swap(&a[prev], &a[right]);
return prev;
}
void QuickSort(int*a, int left, int right)
{
if (left >= right)
return;
int mid = PartSort(a, left, right);
QuickSort(a, 0, mid - 1);
QuickSort(a, mid + 1, right);
}
void Print(int *a, int n)
{
for (int i = 0; i < n ; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
int a[] = { 3, 8, 2, 4 };
int sz = sizeof(a) / sizeof(a[0]);
QuickSort(a, 0, sz - 1);
Print(a,sz);
system("pause");
}
结果: