排序在面试中属于高频考点,需要熟练掌握
本篇内容学习插入排序和选择排序
目录
一.排序的概念
排序
:将一组集合中的数据按照从小到大(升序)或从大到小(降序)进行组织即可。
稳定性
:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即
在原序列中,r[ i ]=r[ j ],且r[ i ]在r[ j ]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的
;否则称为不稳定的。
例如:
2
1 3 4
2
排序好之后 1
2
2
3 4 (稳定的) 1
2
2
3 4(不稳定)
内部排序
:数据元素全部放在内存中的排序。
外部排序
:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的
排序
二、插入排序
1.直接插入排序
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
// 时间复杂度:O(N^2)
![](https://i-blog.csdnimg.cn/blog_migrate/1bdbfd284aa558735a41db6de392517f.png)
在插入排序总过程搬移元素总数: 1+ 2+3+... + n-1 = (1+ n- 1)*(n-1) / 2 ≈ n^2
// 空间复杂度:O(1)
// 稳定性:稳定
排序之后相同的数字前后次序不会发生改变
// 应用场景:适合数据接近有序 或者 数据量少
void InsertSort(int array[], int size)
{
for (int i = 1; i < size; ++i) // 依次获取到array数组中的每个元素进行插入
{
// 单个元素的插入过程
int key = array[i];
int end = i - 1;
// 找待插入元素在array中的位置
while (end >= 0 && key < array[end])
{
array[end + 1] = array[end];
end--;
}
// 插入key
array[end + 1] = key;
}
}
2.希尔排序( 缩小增量排序 )
面试中遇到的数据集合:数据都比较杂乱,或者数据量都比较大;面试官说在该场景下仍需要你使用插入排序的思想
希尔排序法又称缩小增量法。是希尔想出来的
希尔排序法的基本思想是:
先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序
接近有序:小的元素尽量靠前,大的元素尽量靠后,不大不小尽量在中间
如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/08e0bd1d1db2fdfb20f8b3c816760843.png)
gap = 5 意思就是隔5个一组,如9和4一组比较排序
gap = 2 意思就是隔2个一组,如 4 2 5 8 5
这样可以慢慢接近有序,当gap = 2处理完毕后,结果还不是有序的
此时将gap= 1设置为1,就是整体的插入排序
// 时间复杂度:O(N^1.25)~O(1.6N^1.25)
// 空间复杂度:O(1)
// 稳定:不稳定
// 应用场景:数据杂乱或者数据量较大时
// 空间复杂度:O(1)
// 稳定:不稳定
// 应用场景:数据杂乱或者数据量较大时
void ShellSort(int array[], int size)
{
int gap = size;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = gap; i < size; ++i) // 依次获取到array数组中的每个元素进行插入
{
// 单个元素的插入过程
int key = array[i];
int end = i - gap;
// 找待插入元素在array中的位置
while (end >= 0 && key < array[end])
{
// 往后搬移到当前分组的下一个位置
array[end + gap] = array[end];
end -= gap; // 获取当前分组的前一个位置的元素
}
// 插入key
array[end + gap] = key;
}
//gap -= 1;
}
}
三、选择排序
基本思想
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
1.直接选择排序
在元素集合
array[i]--array[n-1]
中选择关键码最大
(
小
)
的数据元素若它不是这组元素中的最后一个(
第一个
)
元素,则将它与这组元素中的最后一个(第一个)元素交换在剩余的array[i]--array[n-2]
(
array[i+1]--array[n-1]
)集合中,重复上述步骤,直到集合剩余
1
个元素
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:不稳定
// 应用场景:
// 空间复杂度:O(1)
// 稳定性:不稳定
// 应用场景:
void SelectSort(int array[], int size)
{
for (int i = 0; i < size - 1; ++i) // 控制循环的趟数
{
// 单趟选择的过程
// 只需将区间中的元素遍历一遍,找到最大元素所在的位置
int maxPos = 0;
for (int j = 1; j < size - i; ++j)
{
if (array[j] > array[maxPos])
{
maxPos = j;
}
}
// 因为排升序:最大元素应该将去放到区间的最后一个位置
// 但是最后一个位置也是有数据的,此处只能将maxPos位置的最大数据和区间最后一个位置的数据进行交换
if (maxPos != size - i - 1)
{
Swap(&array[maxPos], &array[size - 1 - i]);
}
}
}
选择排序缺陷:1.进行了一些重复性的比较
2.每次遍历只排一个
2.选择排序优化
直接选择排序中:单趟找到最大元素位置,然后将最大元素放到区间末尾,一趟只能排好一次元素
优化:一次遍历将最大元素往区间末尾放,最小元素放在起始位置,一次排好两个元素
// 选择的趟数减少了一半,但是元素的比较次数实际并没有减少
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:不稳定
void SelectSortOP(int array[], int size)
{
int begin = 0;
int end = size - 1;
while (begin < end)
{
int maxPos = begin;
int minPos = begin;
int index = begin + 1;
// 在[begin, end]前中找到最大最小元素的位置
while (index <= end)
{
if (array[index] > array[maxPos])
maxPos = index;
if (array[index] < array[minPos])
minPos = index;
++index;
}
// 将最大的元素往区间末尾存放
if (maxPos != end)
{
Swap(&array[maxPos], &array[end]);
}
// 如果最小元素如果恰巧在end位置,上面的交换结束之后
// 最小的元素的位置就发生了改变,此时必须要及时更新minPos
if (minPos == end)
minPos = maxPos;
// 将最小的元素往区间起始位置存放
if (minPos != begin)
{
Swap(&array[minPos], &array[begin]);
}
begin++;
end--;
}
}
3.堆排序
堆排序
(Heapsort)
是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。
需要注意的是排升序要建大堆,排降序建小堆