(7+1)大排序详解(学数据结构怎么能不学排序) ✈️

98 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第N天,点击查看活动详情 🚀1. 直接插入排序 基本思想:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

在我们的日常生活中,也有用到插入排序的时候,就例如打扑克:

动图演示:

实现思路:

在待排序的元素中,假设前n-1个元素已有序,现将第n个元素插入到前面已经排好的序列中,使得前n个元素有序。按照此法对所有元素进行插入,直到整个序列有序。  但我们并不能确定待排元素中究竟哪一部分是有序的,所以我们一开始只能认为第一个元素是有序的,依次将其后面的元素插入到这个有序序列中来,直到整个序列有序为止。

🚁代码展示: void InsertSort(int* a, int n) { for (int i = 0; i < n - 1; ++i)//为防止x越界,需要使 i < n-1 { 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; //代码执行到此位置有两种情况: //1.待插入元素找到应插入位置(break跳出循环到此) //2.待插入元素比当前有序序列中的所有元素都小(while循环结束后到此) } }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 直接插入排序特性:

元素集合越接近有序,直接插入排序算法的时间效率越高 时间复杂度:O(N^2) 空间复杂度:O(1) 稳定性:稳定 🚀2. 希尔排序 基本思想:

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。即使序列相对有序。 当到达=1时,所有记录在统一组内排好序。

例如:

动图演示:

实现思路:

希尔排序的目的是先让序列相对有序,然后再进行插入排序,先将序列分成距离相等的小组进行排序,让序列实现相对有序

🚁代码展示: void ShellSort(int* a, int n) { int gap = n; while (gap > 1) {

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 希尔排序特性:

希尔排序是对直接插入排序的优化。 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样再进行直接插入排序可以提高效率,从而达到优化。 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。 稳定性:不稳定。 🚀3. 选择排序 基本思想:

第一次从序列中选出最小(最大)的一个元素,存放在序列的起始(末尾)位置,然后遍历比较选出次小(次大)的一个元素,存放在下一个位置,重复这样的步骤直到全部待排序的数据元素排完 。

动图演示:

实现思路:

在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素,将它与这组元素中的最后一个(第一个)元素交换 在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素 我们可以在第一遍遍历中选出最大以及最小值,然后将最小值与起始位置交换,将最大值与末尾位置交换,从而提升效率。 注意事项:

当最大值刚好在第一个位置的情况:

交换了最小值之后,最大值就被交换到了min的位置 在每次交换了最小值之后应该判断一下最大值是否在起始位置,如果在需要将max赋值为min。

🚁代码展示: void SelectSort(int* a, int n) { assert(a); int begin = 0, end = n - 1; while (begin < end) { int mini = begin, maxi = begin; for (int i = begin + 1; i <= end; ++i) { if (a[i] < a[mini]) mini = i;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 选择排序的特性:

直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用 时间复杂度:O(N^2) 空间复杂度:O(1) 稳定性:不稳定 🚀4. 堆排序 基本思想:

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。 需要掌握两种算法:1.向下调整算法 2.向上调整算法 算法学习:

向下调整算法 : 左右子树必须是一个堆,才能进行调整

向上调整算法:

动图演示:

实现思路:

🚁代码展示: //向上调整算法 void AdjustUp(HPDataType* a, size_t child) { size_t parent = (child - 1) / 2; while (child > 0) { //if (a[child] > a[parent]) //大根堆 if (a[child] < a[parent]) //小根堆 { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } }

//向下调整算法 void AdjustDown(HPDataType* a, size_t size, size_t root) { int parent = root; int child = 2 * parent + 1; while (child < size) { //1、确保child的下标对应的值最小,即取左右孩子较小那个 if (child + 1 < size && a[child + 1] < a[child]) //得确保右孩子存在 { child++; //此时右孩子小 } //2、如果孩子小于父亲则交换,并继续往下调整 if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = 2 * parent + 1; } else { break; } } }

//堆排序 void HeapSort(int*a,int n) { }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 堆排序的特性:

堆排序使用堆来选数,效率就高了很多。 2. 时间复杂度:O(N*logN) 3. 空间复杂度:O(1) 4. 稳定性:不稳定

🚀5. 冒泡排序 基本思想:

冒泡排序是我们在C语言学习过程就要求掌握的排序方法,就是两两元素相比,前一个比后一个大就交换,直到将最大的元素交换到末尾位置,遍历n-1次就可以把序列排好。

动图演示:

实现思路:

两两相比选最大

🚁代码实现 void BubbleSort(int* a, int n) { assert(a); for (int j = 0; j < n - 1; ++j { int exchange = 0; for (int i = 1; i < n - j; ++i) { if (a[i - 1] > a[i]) { Swap(&a[i - 1], &a[i]); exchange = 1; } }

if (exchange == 0) break; } } }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 冒泡排序的特性:

冒泡排序是一种非常容易理解的排序 时间复杂度:O(N^2) 空间复杂度:O(1) 稳定性:稳定 🚀6. 快速排序(王者) 基本思想:

任取待排序元素序列中的某元素作为基准值(key),按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

✈️6.1 hoare 实现思路:

选定一个基准值key,一般选定最左边或者最右边(方便)。 确定两个指针begin 和end 分别从开头和结尾向中间遍历数组。 如果选最右边为key,那么begin指针先走,如果遇到大于key的数就停下来。 然后end再走,遇到小于基准值的数就停下来。 交换begin和end指针对应位置的值。 重复以上步骤,直到left = right ,最后将key与left(right)位置的值交换。 动图演示:

细节拆解: