分治法 —— 快速排序(递归,迭代,非递归)
快速排序
快速排序在所有排序中基本上速度是最快的啦
(前提是基准元素要选的好,没选好基准元素很可能变为冒泡排序)
快速排序算法思想是分治策略,但可以有多种手段来实现
1,快速排序之递归
其实快速排序之递归是传统的快速排序。为什么说是传统的呢? 大部分的快速排序无论是在教材上还是在一些博客上基本采用递归分治来实现。因为此方法比较好懂
递归快速排序一般都是两个游标 i 和 j , i在左,j在右
i 往右移发现a[i] 大于 pivot 进行交换,j往左移发现a[j]小于 pivot 进行交换。直到i不小于j才推出,此时第一轮循环完毕,左右两边基本有序。接着分治进行左右两个part使之有序,最终整个数组有序
废话不多说,直接上代码的啦!
大部分地方都有代码注释应该都容易理解的,哈哈哈。
Code 递归
#include <cstdio>
#include <iostream>
using namespace std;
//递归方式
//-----------------------------------------------------------------------
void Swap(int& a, int& b)//元素的交换,注意用引用,否则用指针
{
int temp = a;
a = b;
b = temp;
}
void QuickSort1(int s[],int low, int high)
{
if (low >= high) return ;//递归退出
int pivot = s[low];//元素选择不恰当,时间复杂度会加大
//当然这里仅仅只讲解快速排序的写法,基元素的选择也可以优化,此处不进行讲解演示
int i = low, j = high;
while (i < j)
{
while (s[j] > pivot) j--;
Swap(s[j], pivot);//j向左移发现有小于pivot的值,交换
while (s[i] < pivot) i++;
Swap(s[i], pivot);//i向右移发现有大于pivot的值,交换
}
QuickSort1(s, low, i - 1);//进入左边part再进行快速排序
QuickSort1(s, j + 1, high);//进入右边part再进行快速排序
}
//-----------------------------------------------------------
//QucikSort2在QuickSort1的基础上稍微优化一下,减少交换的次数
//每次交换a[i] 和 a[j] , 最后将a[i] 或 a[j] 与pivot交换即可
//大大减少交换次数
/*
void QuickSort2(int s[],int low, int high)
{
if (low >= high) return ;//递归退出
int pivot = s[low];
int i = low, j = high;
while (i < j)
{
while (i < j && s[j] > pivot) j--;
while (i < j && s[i] <= pivot) i++;
Swap(s[i], s[j]);//找到i和j 然后交换 a[i] 和 a[j]
}
Swap(s[low], s[i]);//最后和pivot交换
//Swap(pivot, s[i]);注意不要和pivot变量交换哟,那仅仅只是改变pivot的值
//我们需要改变pivot值得所在位置上的值,即是s[low] ,明白吗?
QuickSort2(s, low, i - 1);
QuickSort2(s, j + 1, high);
}
*/
//------------------------------------------------------------------------------
int main()
{
//int a[9] = {12,24,5,18,30,36,58,42,39};
int a[9] = {30, 24, 5, 58, 18, 36 , 12, 42, 39};
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
printf("\n\n-----------------------------------\n\n");
QuickSort1(a, 0, 8);
//QuickSort2(a, 0, 8);
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
return 0;
}
2,快速排序之迭代
仅用一个循环(for 或 while) 来完成快速排序中的基本有序,每次返回mid 值。此方法需要稍稍理解一下,没有递归快排好懂呀!但是我认为你可以看懂的,因为我讲的很清楚啦!
我们来看此段核心代码,只用一个循环来找出中间值,大大的提高程序的效率!!!
int Partition1(int r[], int low, int high)
{
int i, j, pivot = r[high];
for (j = low, i = low - 1; j < high; j++)
{//i 始终在j的后面,用于交换
if (r[j] < pivot)
{
i = i + 1;
Swap(r[i], r[j]);
}
}
Swap(r[i + 1], r[high]);
return i + 1;
}
以数组{30, 24,5, 58, 18, 36 , 12, 42, 39}为例
经过一次循环中间不断进行交换使之变为 {30, 24, 5, 18, 36 , 12, 39, 42, 58},在39左边数据都比39小,39右边数据比39大,基本有序!!!
此段核心代码需要结合例子画图理解,裸想有点难度的喲!下面的图片很好的阐述如何使一个数组基本有序,并返回i + 1, 即mid的值
Code 迭代
#include <cstdio>
#include <iostream>
using namespace std;
void Swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
//我们重点来看partition1
int Partition1(int r[], int low, int high)
{
int i, j, pivot = r[high];
for (j = low, i = low - 1; j < high; j++)
{
if (r[j] < pivot)
{
i = i + 1;
Swap(r[i], r[j]);
}
}
Swap(r[i + 1], r[high]);
return i + 1;
}
//----------------------------------------------------------
//这里采用pivot 为s[low] ,并使用一次循环使之数组基本有序并返回中间元素的位置
/*
int Partition2(int r[], int low, int high)
{
int i, j, pivot = r[low];
for (j = low + 1, i = low; j <= high; j++)
{
if (r[j] < pivot)
{
i = i + 1;
Swap(r[i], r[j]);
}
}
Swap(r[i], r[low]);
return i;
}*/
//------------------------------------------------------------------
void QuickSort(int R[], int low, int high)
{
int mid;
if (low < high)
{
mid = Partition1(R, low, high);
//Partition1函数用来返回基本有序后的数组的中间基元素的位置(下标)
QuickSort(R, low, mid - 1);//递归进入左边part
QuickSort(R, mid + 1, high);//递归进入右边part
}
}
int main()
{
//int a[9] = {12,24,5,18,30,36,58,42,39};
int a[9] = {30, 24,5, 58, 18, 36 , 12, 42, 39};
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
printf("\n\n-----------------------------------\n\n");
QuickSort(a, 0, 8);
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
return 0;
}
3,快速排序之非递归
其实非递归也很好实现的,学过数据结构的同学应该知道大部分的递归是可以用栈来表示的,快速排序的非递归的表示也是如此的。
主要采用一个栈来保存low(left) 和 high(right) ,不断进行入栈 和出栈操作即可实现!!!
Code 非递归
#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;
void Swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int Partition(int r[], int low, int high)
{
int i = low, j = high, pivot = r[low];
while (i < j)
{
while (i < j && r[j] > pivot)//i < j 防止重复的执行交换操作
j--;
if (i < j)
Swap(r[i++], r[j]);//左边右移一位
while (i < j && r[i] <= pivot)
i++;
if (i < j)
Swap(r[i], r[j--]);
}
return i;
}
void QuickSort3(int s[],int low, int high)
{
stack<int> st;//声明一个栈
st.push(low);
st.push(high);
//入栈
while (!st.empty())//栈不为空时
{
int right = st.top();st.pop();//出栈右边界点
int left = st.top();st.pop(); //出栈左边界点
int mid = Partition(s, left, right);
if (left < mid - 1)//左part的两个边界点入栈
{
st.push(left);
st.push(mid - 1);
}
if (right > mid + 1)//右part的两个边界点入栈,注意和左part的入栈顺序不一样
{
st.push(mid + 1);
st.push(right);
}
}
}
int main()
{
//int a[9] = {12,24,5,18,30,36,58,42,39};
int a[9] = {30, 24,5, 58, 18, 36 , 12, 42, 39};
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
printf("\n\n-----------------------------------\n\n");
QuickSort3(a, 0, 8);
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
return 0;
}
4,结语
快速排序总的来说采用的是分治策略,但实现的方式多种多样。此处给出三种实现的方式。事实上也是大同小异,掌握其中一种即可,但个人仍建议掌握三种呀!毕竟技多不压身呀,哈哈哈!
上一篇: React入门