插入排序
直接插入排序
元素集合越接近有序,直接插入排序的时间效率越高
时间复杂度:O(N^2) 最坏情况:逆序(每次挪动的数据都比上一个小)
O(N) 最好情况:顺序
空间复杂度:O(1)
性能稳定
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void InsertSort(int* a,int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
a[end + 1] = tmp;//若数据最小,则此时end=-1,出循环
}
}
}
void SortPrint(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
int a[] = { 2,45,54,34,23,4,5,6,57 };
int n = sizeof(a) / sizeof(int);
InsertSort(&a, n);
SortPrint(&a, n);
}
希尔排序( 缩小增量排序 )
先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
希尔排序: 时间复杂度 O(N^1.3)
1.预排序: 让数组更接近有序
2.插入排序
gap越大,大的可以越快跳到后面,小的数可以越快跳到前面
gap越小,跳的越慢,但是越接近有序,当gap==1相当于插入排序就有序了
多组并走
int gap = 3;
for (int i = 0; i < n - gap; i++)//范围防止越界访问
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
一组一组地排序 (一组一组单独排,共排gap组)
int gap = 3;
for (int j = 0; j < gap; j++)
{
for (int i = j; i < n - gap; i += gap)//范围防止越界访问
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void ShellSort()
{
int a[] = { 10,200,10,65,25,65,84,25 };
int n = sizeof(a) / sizeof(int);
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
int gap = n;
while (gap > 1)
{
//+1保证最后一个gap一定是1
//gap>1时是预排序
//gap==1时是插入排序
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; i++)//范围防止越界访问
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
int main()
{
ShellSort();
}
选择排序
选择排序
选择排序 加强版:选出最大和最小的数排到左右两边
时间复杂度: O(N^2) (好,坏情况)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//选择排序
void SelectInsert()
{
int a[] = { 10,200,10,65,25,65,84,25 };
int n = sizeof(a) / sizeof(int);
int begin = 0;
int end = n - 1;
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
while (begin < end)
{
int mn = begin;
int mx = end;
for (int i = begin + 1; i <= end; i++)
{
if (a[i] < a[mn])
{
mn = i;
}
if (a[i] > a[mx])
{
mx = i;
}
}
swap(&a[begin], &a[mn]);
if (begin == mx)
{
mx = mn;
}
swap(&a[end], &a[mx]);
--end;
++begin;
}
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
int main()
{
SelectInsert();
}
交换排序
冒泡排序
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2) 最坏情况O(N) 最好情况(已经有序)
3. 空间复杂度:O(1)
4. 稳定性:稳定
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void BubbleSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)//n-1次排序,确定后n-1个位置
{
int exchange = 0;
for (int j = 0; j < n - 1 - i; j++)//进行一趟的交换,由于一次交换能确定一个最大位
//置,故每次的最大位置不同
{
if (a[j] > a[j + 1])
{
swap(&a[j], &a[j + 1]);
exchange = 1;
}
}
if (exchange == 0) break;
}
}
void SortPrint(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
int a[] = { 2,45,54,34,23,4,5,6,57 };
int n = sizeof(a) / sizeof(int);
BubbleSort(&a, n);
SortPrint(&a, n);
}
快速排序(重点)
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度: O(N*logN)
hoare版
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//快排序
void QuickSort(int* a, int left, int right)
{
// 区间只有一个值或者不存在就是最小子问题
if (left >= right)
return;
int begin = left, end = right;
int keyi = left;
while (left < right)
{
// right先走,找小
while (left < right && a[right] >= a[keyi])
{
--right;
}
// left再走,找大
while (left < right && a[left] <= a[keyi])
{
++left;
}
swap(&a[left], &a[right]);
}
swap(&a[left], &a[keyi]);
keyi = left;
// [begin, keyi-1]keyi[keyi+1, end]
QuickSort(a, begin, keyi - 1);
QuickSort(a, keyi + 1, end);
}
int main()
{
int a[] = { 2,3,495,56,7,85,5,43,3,21 };
int left = 0;
int right = sizeof(a) / sizeof(int) - 1;
QuickSort(&a, left, right);
for (int i = 0; i < sizeof(a)/sizeof(int); i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
快排在有序情况下会有栈溢出的风险(递归深度太深)
避免在有序情况下,效率退化
1.随机选key
2.三数去中
在相同数较多的情况下,快排占优势
三数去中(解决栈溢出问题)
递归到小的子区间时,可以考虑使用插入排序(小区间的优化)
// 避免有序情况下,效率退化
// 1、随机选key
// 2、三数取中
int GetMidi(int* a, int left, int right)
{
int midi = (left + right) / 2;
// left midi right
if (a[left] < a[midi])
{
if (a[midi] < a[right])
{
return midi;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else // a[left] > a[midi]
{
if (a[midi] > a[right])
{
return midi;
}
else if (a[left] < a[right])
{
return left;
}
else
{
return right;
}
}
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
// 小区间优化,不再递归分割排序,减少递归的次数
if ((right - left + 1) < 10)
{
InsertSort(a+left, right - left + 1);
}
else
{
// 三数取中
int midi = GetMidi(a, left, right);
Swap(&a[left], &a[midi]);
int keyi = left;
int begin = left, end = right;
while (begin < end)
{
// 右边找小
while (begin < end && a[end] >= a[keyi])
{
--end;
}
// 左边找大
while (begin < end && a[begin] <= a[keyi])
{
++begin;
}
Swap(&a[begin], &a[end]);
}
Swap(&a[keyi], &a[begin]);
keyi = begin;
// [left, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
}
L和R相遇点与key交换的原因(相遇点的值一定比key小[前提:key在最左边])
左边做key,右边先走,可以保证相遇位置比key小
相遇场景分析:
L遇R:R先走,停下来,R停下来的条件是遇到比key小的值,R停的位置比key小,L没有找到比key大的,遇到R就停下来了
R遇L:R先走,找小,没有找到比key小的,直接跟L相遇了,L停留的位置是上一轮交换的位置,上一轮交换,把比key小的值换到了L的位置
挖坑法(效率没有提升,但是思路更清晰,考虑点简单)
前后指针法