文章目录
1.交换排序
- 冒泡排序
- 快速排序
2.冒泡排序
1)基本思想
一次确定一个最大值或者最小值,两两比较,将最大值或者最小值交换到最右边或者最左边,N个元素需要N-1次排序
【该图片转载博主一像素】
2)分析
冒泡排序最好情况的时间复杂度为:O(n)最坏情况为O(n2)
空间复杂度为O(1)
稳定
3)与插入排序的比较
如果差一点点有序,插入排序会更好;因为冒泡是更严格的排序,冒泡在有序后,还要在冒一次,才知道他已经有序,才停止冒泡。但插入排序只需要走一遍,加挪动一次就可以搞定。
4)代码
void BubbleSort(int* num, int len)
{
if (num == NULL || len <= 0)
return;
//确定循环躺数
for (int i = 0; i < len - 1; i++)
{
//确定比较次数
for (int j = 0; j < len - 1 - i; j++)
{
if (num[j]>num[j + 1])
Swap(&num[j], &num[j + 1]);
}
}
}
2.快速排序(冒泡排序的改进)
1)基本思想
任取待排序元素序列中某元素作为基准值,按照基准值待排序集合分割为两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程
【该图转载博主一像素】
2)分析
快排的最坏场景:待排序列是有序的,左右子序列数有一个为空,二叉树深度为n,则时间复杂度为O(nn),空间复杂度为O(n)。
最好场景:每次选的数都是序列最中间的数字,二叉树深度为logN,时间复杂度为O(NlogN ).空间复杂度为O(logN)
不稳定的
3)代码
void QuickSort(int* num, int left, int right)
{
if (num == NULL)
return;
//递归出口
if (left >= right)
return;
//按照基准值将待排序区间划分为两个子区间
int div = PartSort1(num, left, right);
//子问题排序左子区间
QuickSort(num, left, div - 1);
//子问题排序右子区间
QuickSort(num, div + 1, right);
}
将区间按照基准值划分为左右两半部分的常见方式有:三种实现
左右指针法
int PartSort3(int* num, int begin, int end)
{
int key = num[end];
int last = end;
while (begin < end)
{
//1.左边找到大于基准值的元素
while ((begin < end) && (num[begin] <= key))
begin++;
//2.右边找到小于基准值的元素
while ((begin < end) && (num[end] >= key))
end--;
//3.交换两个值
Swap(&num[begin], &num[end]);
}
//两个下标走到一块的时候,把基准值交换过来
Swap(&num[begin], &num[last]);
//返回基准值的位置
return begin;
}
挖坑法
int PartSort2(int* num, int begin, int end)
{
int key = num[end];//把基准值拿出来(挖一个坑)
while (begin < end)
{
//1.左边找到大于基准值的元素,并放入坑里
while ((begin < end) && (num[begin] <= key))
begin++;
num[end] = num[begin];
//2.右边找到小于基准值的元素,并放入坑里
while ((begin < end) && (num[end] >= key))
end--;
num[begin] = num[end];
}
//3.把拿出来基准值放入坑里
num[begin] = key;
//返回基准值的位置
return begin;
}
前后指针
int PartSort3(int* num, int begin, int end)
{
int prev = begin - 1;
int cur = begin;
int key = num[end];
while (cur < end)
{
//++prev=cur说明这个元素时第一个元素或者这是一段连续的小于基准的序列
if ((num[cur] < key) && (++prev != cur))
Swap(&num[cur], &num[prev]);
cur++;
}
//把基准元素放在所有小于基准元素的紧邻后边,可以达到基准的左边小于基准,右边大于基准
Swap(&num[++prev], &num[end]);
return prev;
}
4)快排的优化
三数取中法优化
int GetMidKey(int* num, int begin, int end)
{
assert(num);
int mid = begin + (end - begin) / 2;
if (num[begin] < num[mid])
{
if (num[mid] < num[end])
return mid;
else
{
if (num[begin]>num[end])
return begin;
else
return end;
}
}
else
{
if (num[begin] < num[end])
return begin;
else
{
if (num[mid]>num[end])
return mid;
else
return end;
}
}
}
int PartSort3(int* num, int begin, int end)
{
//三数取中优化
int index = GetMidKey(num, begin, end);
//和要选的基准交换
Swap(&num[index], &num[end]);
int key = num[end];
int last = end;
while (begin < end)
{
//1.左边找到大于基准值的元素
while ((begin < end) && (num[begin] <= key))
begin++;
//2.右边找到小于基准值的元素
while ((begin < end) && (num[end] >= key))
end--;
//3.交换两个值
Swap(&num[begin], &num[end]);
}
//两个下标走到一块的时候,把基准值交换过来
Swap(&num[begin], &num[last]);
//返回基准值的位置
return begin;
}
底层区间优化
void QuickSort1(int* num, int left, int right)
{
if (num == NULL)
return;
//递归出口
if (left >= right)
return;
//小区间优化(替换掉后边几层的递归)
if (right - left + 1 < 10)
InsertSort(num, right - left + 1);
int div = PartSort1(num, left, right);
QuickSort1(num, left, div - 1);
QuickSort1(num, div + 1, right);
}
将递归快排改为循环快排
栈的相关函数:https://blog.csdn.net/weixin_41892460/article/details/82973851
void QuickSortNonR(int* num, int left, int right)
{
if (num == NULL || right <= left)
return;
Stack st;
StackInit(&st);
//先将整个区间压栈
StackPush(&st, left);
StackPush(&st, right);
while (StackEmpty(&st) != 0)
{
//取栈顶并且出栈
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
//先划分主区间,固定好一个基准
int div = PartSort1(num, begin, end);
//如果左子序列还有大于1个元素,继续压栈
if (begin < div - 1)
{
StackPush(&st, begin);
StackPush(&st, div - 1);
}
//如果右子序列还有大于1个元素,继续压栈
if (div + 1 < end)
{
StackPush(&st, div + 1);
StackPush(&st, end);
}
}
StackDestroy(&st);
}