十大排序之交换排序
文章目录
- 十大排序之交换排序
-
- 一、交换排序
-
- [1.1 冒泡排序](#1.1 冒泡排序)
- [1.1.1 冒泡排序时间复杂度](#1.1.1 冒泡排序时间复杂度)
- 1.2快速排序(快排)
-
- [1.2.1 Hoare 版本排序法](#1.2.1 Hoare 版本排序法)
- [1.2.2 前后指针法](#1.2.2 前后指针法)
- [1.2.3 挖坑法](#1.2.3 挖坑法)
- [1.2.4 快排的时间复杂度](#1.2.4 快排的时间复杂度)
一、交换排序
1.1 冒泡排序
冒泡排序是一种最基础的排序方式,因为每一个元素都可以像小气泡一样,根据自身的大小像数组的一侧进行移动

代码实现:
c
void Swap(int* x, int* y)
{
int tmp;
tmp = *x; *x = *y; *y = tmp;
}
void Bubblesort(int* a, int n)
{
int flag = 0;
for (int j = 0; j < n; j++)
{
for (int i = 1; i < n-j; i++)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
flag = 1;
}
}
if (flag == 0)
break;
}
}
1.1.1 冒泡排序时间复杂度
时间复杂度:O( N 2 N^2 N2)
在实际情况中由于冒泡排序效率低下,常常只具有教学意义,没有实际应用意义。
1.2快速排序(快排)
快速排序是Hoare于1962年提出的⼀种**⼆叉树结构** 的交换排序⽅法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩于基准值,右⼦序列中所有元素均⼤于基准值 ,然后最左右⼦序列重复该过程 ,直到所有元素都排列在相应位置上为⽌。
快排基本框架
c
void QuickSort(int* a,int left,int right)
{
if(left>=right)
return;
int meet= _QuickSort(a,left,right);
QuickSort(a,left,meet-1);
QuickSort(a,meet+1,right);
}
1.2.1 Hoare 版本排序法


算法思路
- 创建左右指针,确定基准值
- 从右向左找出比基准值小的数据,从左向右找出比基准值大的数值,左右数值交换,进入下一个循环
问题一:怎么就一定确定left和right相遇后所在的数值比key值小?
c
int _HoareQuickSort(int* a,int left,int right)
{
int begin=left;
int keyi=left;
int end=right;
while(left<right)
{
while(left<rigth&&a[left]<a[keyi])
left++;
while(left<rigth&&a[right]>a[keyi])
right--;
Swap(&a[left],&a[right]);
}
Swap(&a[keyi],&a[right]);
return right;
}
问题一解答:
问题2:key值怎么选才能最好的?
key值选不好可能会出现如下情况:
如果数组元素个数过大,会导致右边递归层次过深,可能会有栈溢出的风险为避免出现这种情况,我们采取三数取中法进行选key值
c
int Getmid(int* a,int left,int right)
{
int mid=(left+right)/2;
if(a[left]>a[right])
{
if(a[mid]>a[left])
{
return left;
}
else if(a[mid]<a[right])
return right;
else
return mid;
}
if(a[left]<a[right])
{
if(a[mid]>a[right])
{
return right;
}
else if(a[mid]<a[left])
return left;
else
return mid;
}
}
将三数取中法融入到_HoareQuickSort排序中
c
int _HoareQuickSort(int* a,int left,int right)
{
int begin=left;
//三数取中法取keyi
int midi=Getmid(a,left,right);
Swap(&a[midi],&a[left]);
int keyi=left;
//
int end=right;
while(begin<end)
{
//右边先走,右边找小
while(begin<end&&a[end]>a[keyi])
--end;
//左边找大
while(begin<end&&a[left]<a[keyi])
++begin;
Swap(&a[begin],&a[end]);
}
Swap(&a[keyi],&a[begin]);
return begin;
}
1.2.2 前后指针法
动图图解 :

- 优点:不用在进行判断左边先走还是右边先走
- 思路:1.先选择一个key值,prev指针开始指向序列开头,cur指针指向prev的下一个,2.cur先走,当cur指针指向的数小于key值,停下来,prev后移一位,交换两个数,当cur指向的值小于key,cur++,prev++;3. 重复以上步骤,直到cur越界,prev当前指向的位置与key互换,依次递归
代码如下:
c
int _PointerQuickSort(int* a,int left,int right)
{
//三数取中选key值
int midi=Getmid(a,left,right);
Swap(&a[midi],&a[left]);
int keyi=left;
int prev=left;
int cur=prev+1;
while(cur<=right)
{
if(a[cur]<a[keyi]&&++prev!=cur)//避免prev和cur指向一个位置,自己和自己交换
{
Swap(&a[prev],&a[cur]);
}
cur++;
}
Swap(&a[keyi],&a[prev]);
return prev;
}
1.2.3 挖坑法
图解:

- 思路:开始的时候key值先储存起来,key值所在位置是坑,右边先走找小,互换坑位,再左边走找大互换坑位,左右相遇,将key值放在相遇的位置。
- 优点:左边是坑,一定是右边先走;右边是坑,一定是左边先走;不用纠结谁先走的问题
代码示例:
c
int HoleQuickSort(int* a,int left,int right)
{
//三数取中选key值
int midi=Getmid(a,left,right);
Swap(&a[midi],&a[left]);
int keyi=left;
int key=a[keyi];
int hole=left;
int begin=left,end=right;
while(begin<end)
{
while(begin<end&&a[end]>key)
end--;
//填坑
a[hole]=a[end];
hole=end;
while(begin<end&&a[begin]<key)
begin++;
//填坑
a[hole]=a[begin];
hole=begin;
}
a[hole]=key;
return hole;
}
1.2.4 快排的时间复杂度
👌快速排序的递归方法和二叉树的向上调整类似,时间复杂度是O(N* l o g 2 N log_2N log2N)
三种递归方法的时间复杂度是一样的,只不过理解方式不同。
👌空间复杂度:O( l o g 2 N log_2N log2N)
👌稳定性:不稳定

😎下一节预告:插入排序(直接插入,希尔排序)

