目录
1 排序的概念及其应用
1.1 排序的概念
1.2 排序的应用
排序可以应用在各种排名中(大学、品牌、成绩等),
1.3 常见的排序算法
我们现在已经学了冒泡排序O(N*N)和堆排序O(N*logN)。
接下来我们先讲直接插入排序。
2 直接插入排序
2.1 基本思想
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。
- end表示一个下标,它的范围是[0,n-2],也就是最多只能指向倒数第二个数。
- 下标[0,end]是有序区间,end+1的下标插入这段有序区间仍是有序的。
2.2 基本思路
- 先单趟再多趟,先局部再整体。
单趟
如上只是简单的一趟排序,并不确定是哪一趟。而我们要实现的是多趟排序,这样才能把一组数据排好。那么如何进行多趟呢?
- end不能是n-1,因为n-1的值是要和前面比较的,n个数的下标范围是[0,n-1]。
2.3 代码实现
//升序
void InsertSort(int* a, int n)
{
//[0,end]end+1插入
for (int i = 0; i < n - 1; i++)
{
int end=i;
int tmp = a[end + 1];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
打印代码
void PrintSort(int* a, int n)
{
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
- 提问:如果让i从1开始循环,可以吗?
- 提问:这个位置可以存储数据个数吗?
不可以,哨兵位的头节点也不可以存储数据个数。因为数据类型不一样。数据类型可能会是char/double等,无法记录个数。
2.4 时间复杂度
- 时间复杂度: O(N^2)
3 冒泡排序(回顾)
回顾戳👉冒泡排序法代码
3.1 思路分析
单趟
- 每一趟都是从第一个数开始,然后依次从前往后两两比较,这里我们用升序。只要前面的数大于后面的数,我们就交换两个数的位置。比较完一组以后++i,再依次后面的两个数。
void BubbleSort(int* a, int n)
{
for (int i = 1; i < n - j; i++)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
}
}
}
//或者i从0开始,要注意范围的改变
for (int i = 0; i < n-1; i++)
{
if (a[i] > a[i+1])
{
Swap(&a[i], &a[i+1]);
}
}
多趟
- 这里我们要控制的是结束位置。经过一趟排序后,最大的那个数已经排到最后面了,所以下一趟就不需要再与那个数比较了(改变i)。所以我们可以再写一个循环来控制多趟,j<n表示一共要实行n趟。
void BubbleSort(int* a, int n)
{
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]);
}
}
}
}
3.2 时间复杂度
- 时间复杂度:O(N^2)
- 最好情况——已经有序:O(N)。
那我们怎么才能看出来有没有进循环呢?
- 代码这样改
- 如果进循环了,exchange就改为true,没有进就仍旧为false。
void BubbleSort(int* a, int n)
{
for (int j = 0; j < n; j++)
{
bool exchange = false;
for (int i = 1; i < n - j; i++)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
exchange = true;
}
}
if (exchange == false)
break;
}
}
4 比较
冒泡排序和插入排序谁更优?
- 直接插入排序和冒泡排序的时间复杂度是一个等级的,但是直接插入排序效率更好。
- 因为冒泡排序一直都是等差数列,而插入排序只有在逆序的情况下才会使等差数列,如果后面的数比前面的数大,那么就不用交换了,直接看后面的那个数就可以。(排升序)