踏踏实实地打好算法基础(2)—插入排序和希尔排序算法

插入排序

插入排序虽然没有冒泡排序和选择排序那么简单直观,不过其原理也不算难懂,打过扑克人应该都会秒懂。

基本思想

每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。

插入排序算法

  • 通过一个指针从左向右移动
  • 每次移动将进入数字与排好序的数字比较,然后插入合适的位置
  • 不断重复上面步骤,直到把最后一个数据插入合适的位置,就完成了对原有数组从小到大的排序

insertion_sort_001.png

  • 将数组分为已排序和未排序两个部分
  • 元素 2 为划分为已排序部分
  • 然后每一次迭代都将未排序的一个元素插入到已排序的适当位置
  • 当 6 进入已排序区域从右到左一个一个对比,直到 6 放置适当位置

insertion_sort_002.png

  • 然后 5 进入到已排序区域
  • 5 和 6 进行对比,5 小于 6 所以将 5 和 6 交换位置

insertion_sort_003.png

  • 接下里 5 与 2 进行比较,5 大于 2 所以就不再交换位置停留到当前位置
  • 然后将 1 进入到已排序区域

insertion_sort_005.png

insertion_sort_006.png

  • 当 1 元素从未排序进入已排序区域后,通过从右到左一个一个元素对比交换直到 1 来到其应该位置。

代码实现

class Solution:
    def insertionArraySor(self, nums):
        n = len(nums)
        for i in range(1, n):
            j = i
            while nums[j-1] > nums[j] and j > 0:
                nums[j-1], nums[j] = nums[j],nums[j-1]
                j -= 1

        return nums
复制代码
  • 首先初始化 j=i
  • 然后 while nums[j-1] > nums[j] and j > 0 然后将插入数据与已排序区域一个一个元素进行对比

希尔排序(shell sort)

希尔排序是直接插入排序算法的优化改进版本,或者缩小增量排序。是法因 D.L.Shell 于 1959 年提出而得名的算法

  • 希尔排序是非稳定排序算法,

基本思想

设待排序列有 n 个元素,取一个整数 gap(gap<n)作为间隔,将全部元素分为 gap 个子序列,所有距离为 gap 的元素放在同一个子序列中。对于每一个子序列中分别采用直接插入排序。然后缩小间隔 gap 例如取 gap = gap/2 重复上述的子序列划分和排序工作。

希尔排序一般流程

  • 将有 n 个元素的数组分成 gap = n//2 n/gap 个数字序列,第 1 个数据和第 n/2+1 个数据为一对
  • 一次循环使每一个序列对排好顺序,在每一个子序列中分别采用直接插入排序
  • 然后缩小间隔 gap,将 gap = gap/2, 然后变为 n/gap 个子序列,重复上述的子序列划分和排序工作
  • 不断重复上述过程,随着序列减少最后变为一个,也就完成了整个排序

shell_sort_001.png

shell_sort_002.png

  • 首先每间隔(gap) 3 取一个数字组成一组来进行插入排序
  • 经过这一次迭代后我们该排完序的大致有了一些模样,较大数字已经位于序列的右侧,较小数字位于序列左侧
  • 这个就是希尔排序的魅力,因为原有插入排序每次交换幅度比较小

shell_sort_005.png

shell_sort_006.png

代码实现

    def sellArraySort(self, nums):
        n = len(nums)
        gap = n // 2
        while gap > 0:
            for i in range(gap,n):
                anchor = nums[i]
                j = i
                while j>=gap and nums[j-gap] > anchor:
                    nums[j] = nums[j - gap]
                    j -= gap
                nums[j] = anchor
            gap = gap // 2
        return nums
复制代码
  • range(gap,n) 如果 gap=3 则从 3 开始到数组的末尾

shell_sort_007.png

  • 首先我们定义两个指针 i 和 j 其中,i 取值从 gap 到 n 取值
  • 首先 i 和 j 都指向当前数字,然后比较 j-gap 位置值和当前 anchor值大小,j-gap 位置值大于当前值这对这两个值进行交换(如下图)

shell_sort_008.png

这张图对代码给出图解方式说明代码含义

有关 gap 的选择

  • 最初 Shell 提出取 gap = n/2 gap = gap/2, 直到 gap = 1
  • Knuth 提出取 gap = gap/3 + 1
  • 还有人提起都取奇数比较好
  • 也有人提出各 gap 互为质为好

总结回顾

  • 数据大致有序时最快O(n)O(n)

希尔排序的原理

  • 开始时 gap 的值比较大,子序列中的元素较少,排序速度较快
  • 随着排序进展, gap 值逐渐变小,子序列中元素个数变多,但是由于前面工作的基础,大多数元素已经基本有序,所以排序速度依然很快
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享