🌟 思想:
直接插入排序是一种简单的插入排序法,思想是是把待排序的数据按照下标从小到大,依次插入到一个已经排好的序列中,直至全部插入,得到一个新的有序序列。例如:我们玩扑克牌的时候,每次摸进一张的新的牌我们会将其插入到合适的位置。
思路: 我们假设第一个数据有序,从第二个元素开始进行插入(最开始把前面第一个数据看作一个有序的区间),从后向前依次寻找合适位置,每次插入的时候如果当前位置不合适将当前位置向后移动一位。
实现:
我们可以思考一下极端情况:
- 假设数据为 , 为 ,,所以 为 循环结束,执行 ,相当于 而当前位置本来就是 ,不会有影响。
- 假设数据为 , 为 ,,当前 将数据向后移动直至 ,当 循环结束, 相当于 ,所以最终结果为 。
总结:
- 元素结合越接近有序,直接插入排序算法的时间效率就越高。
- 最坏时间复杂度:
- 接近有序或已经有序时间复杂度:
- 空间复杂度:
🌟 思想:
希尔排序又称缩小增量法。希尔排序的基本思想是:先选定一个 (整数),把待排序中的数据分成 组( 距离的为同一组),并对每一组内的的数据进行插入排序。
假设 将下面的 个数分成 组。 、、分别进行插入排序。当 不为 时都是预排序,当 时是插入排序,因为当数据接近有序的时候,插入排序的效率很高。
1️⃣ 组希尔排序: 这是希尔排序的 组的一种写法,按上面的图来说,这样的写法是先走完红色组,再走蓝色组……
2️⃣ 组希尔排序:第二种在上一种写法上进行了优化,原来是一组走完再走下一组,现在是一组一组间交替的去插入排序。
实现:当 的时候都是预排序(是为了让数据更接近有序,因为直接插入排序当数据接近有序的时候是 ),而这里 是为了最后一次让 。 当 越大的时候大的数会越快的到后面,但是数组越不接近有序。当 越小的时候,数组越接近有序。当 就是直接插入排序。
总结:
- 时间复杂度 ~
- 空间复杂度:
- 希尔排序是插入排序的优化
🌟 思想:
每一次从待排序的数据种选出最小或者最大的元素下标,存放在当前序列的起始位置,直到排序结束。
1️⃣ 实现:
2️⃣ 优化: 我们可以同时选出两个数一个最大一个最小,把最小的组交换到当前区间的左边,最大的交换到区间的右边。
实现:
总结:
- 选择排序比较好理解,但是效率很低,实际种很少使用。
- 时间复杂度
🌟 思想:
堆排序是指用堆这种数据结构所设计的一种排序思想,它是选择排序的一种,它是用堆来进行选数。升序需要建大堆,降序需要建小堆。
拿升序来举例,因为大堆的堆顶是最大的数,此时我们可以让堆顶和末尾元素交换。再让堆顶的元素向下调整(只不过此时向下调整,我们先让数组的长度减 ,因为最大的数已经到末尾的位置了,不必算入堆内)
实现(大堆举例):
实现(大堆举例):
实现:
总结:
- 堆排序的时间复杂度
- 向上调整算法建堆的时间复杂度是 ,向下调整算法建堆的时间复杂度是 ,但是向下调整算法建堆的前提是:当前左右子树必须是堆。所以向下调整算法要从非叶子节点开始向下调整,最后一个非叶子节点就是最后一个元素的父节点 。
🌟 思想:
冒泡排序是交换排序的一种。所谓交换就是根据序列中两个记录下标值的比较结果来对这一对数据进行交换,当第一躺冒泡排序结束后,若是升序会把最大的数冒到最后末尾,以此类推。
实现:
优化:当数组为有序的时候,我们用 来记录,若当前这一趟一次都没有交换,则数组已经有序。
总结:
- 冒泡排序是非常经典的排序
- 时间复杂度
🌟 思想:
快速排序是 Hoare于1962年提出的一种二叉树结构的交换排序方法。基本思想是:从待排序序列中选一个 (通常为最左边或者最右边),按照 把待排序序列分成两个子序列,左序列中的元素都 ,右序列的元素都 ,然后左右序列重复该过程,直到所有元素都排列在对应的位置上。
思路和结论:假设 为最左边的数,那么就要先从右边走,再走左边。假设 为最右边的数,那么就要从左边先走,再走右边。这样做的目的是:当 与 相遇结束的时候,让 位置的元素与当前相遇位置交换,而当前相遇位置一定能保证比 要小(或大)。 第一次快速排序相当于处理好了第一个区间,因为 找到了合适的位置左面的数都比 小,右面的数都比 要大,此时只需要让左右区间重复上面的过程。左右区间划分为了 。递归处理即可,当左右区间只剩一个数()或者区间不存在()的时候为递归结束条件。
实现:
💬 思考问题:
-
和 这里为什么要使用 ,不使用 可以吗?结论是不可以。
- 为什么右边和左边走的时候要加上 ?
实现:
-
第一次交换 、第二次交换 、循环结束相遇位置与 位置交换 最终结果。
-
第一次挖坑 、第二次 、第三次 、第四次 、第五次 、循环结束把当前坑位填入 最终结果是 。
思路: 定义一个 和 , 找比 要小的数,若找到则 和 当前下标的元素进行交换,这样做有一种把小的数翻到前面来,大的往后推。而 后面的数都要比 大,最终 越界循环结束,再把 位置和 位置的元素进行交换。
实现:
🌟 快排优化
💬 为什么要优化?
因为 会影响快排的效率,如果每次的 都是中间那个值那么每次都是二分,若每次的 都是最小的,最坏情况下快排的时间复杂度 。最大的问题是会引起 栈溢出。
思路: 第一个数和中间还有最后的数,选不是最大也不是最小的数。三数取中主要体现在数据已经有序的情况下。
实现:
思路: 若只剩最后 个数,还使用快速排序的递归就有点不太划算了。所以当区间较小的时候,就不再使用递归继续划分小区间。考虑直接用其他排序对小区间处理,而希尔排序和堆排序对于数据量小的时候也不太优,所以在简单排序中,插入排序适应性最强。 小区间优化体现在减少递归的调用次数。
优化实现:
🌟 快排非递归
非递归实现:
总结:
- 时间复杂度
- 空间复杂度
🌟 思想:
归并排序是建立在归并操作上的一种有效的排序算法,是分治的一个典型的应用。将两个有序的子序列归并得到完全有序的序列。归并排序要借助额外的空间。
递归实现:
🌟 归并非递归
非递归实现: