【0基础入门数据结构与算法】—— 选择排序、插入排序、冒泡排序

1. 说明

这三个是经典的计算机排序算法,也是众多计算机算法入门课。

其中 插入排序 、冒泡排序 在最好情况下的时间复杂度为O(n),然而这个选择排序嘛…但是我们还是优先讲解选择排序吧

现在让我带领大家看看怎么实现的吧!


2. 选择排序

额…不怎么用吧,怎么搞都是O(n^2)

2.1. 实现过程

现在我们假定要按照从小到大排序:

假设当前值为最小,记录当前位置。随后遍历这个元素之后的所有元素并更新最小的标定点。遍历完毕交换当前元素与标定点的的数据。

选择排序的动画演示

2.2. 代码片段

func SelectionSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ { //其实这里是n还是n-1并没有太大差距,但是更严谨来说,当选择排序只剩下一个数的时候已经没得选择了。
        minIndex := i
        for j := i + 1; j < n; j++ {
            if arr[j] < arr[minIndex] {//整个过程中就是在记录一个当前最小值所在的位置。
                minIndex = j
            }
        }
        arr[i], arr[minIndex] = arr[minIndex], arr[i]//最后将最小值那个位置的数据与当前初始位置交换。
    }
}

2.3. 代码解释

  1. 创建一个标定点,然后与之后所有的数据比较,如果右找到更小的数据则更新这个最小的小标位置。
  2. 当已经比对完后将最小的数据与当前位置的数据进行交换。
  3. 此时每一轮进行比较的过程中,都只是想找到一个最小值,而我们有无法得知剩下的数组是否有序所以依然还是O(n^2)的时间复杂度。

3. 插入排序

在数据量小的时候往往能有不错的效率,常用于快排的子过程。是这3个排序算法中最好的了。

3.1. 实现过程

现在我们假定要按照从小到大排序:
取出当前位置的数据,依次与前面的数据比较大小,如果当前位置的数据比前一个数据还要小,则把数据大的往后移动。最后将正确的数据插入该空位。
在理解插入排序这个过程往往可以理解成玩牌的时候,整理牌的思路。

插入排序的动画演示

3.2. 代码片段

func InsertionSort(arr []int) {
    n := len(arr)
    for i := 1; i < n; i++ { //因为第一数没有插入的必要了,所有排序的数组在[1...n)这个区间当中
        e := arr[i]//取出需要插入的数据(手中的牌)
        j := i
        for ; j > 0 && e < arr[j-1]; j-- {//如果前面的数据(手牌)有比较大的则将它往后挪。
            arr[j] = arr[j-1]
        }
        arr[j] = e //我可以说 j 这个位置就是我应该放牌的地方
    }
}

3.3. 代码解释

  1. 这里我们是通过与前面的进行比较,所以我们需要操作的的数在 [1…n) 之间(一个数没什么好比较的,所有抛弃了下标为0的情况)。
  2. 我们用一个临时变量记录当前想要排序的数字,这样子就将当前位置空了出来,这样子就有足够的空间来将比较大的数字往后面移动。可以想象一下我们在玩牌的时候,假定你手里现在有3张牌 [1,3,2]。想要整理手牌的时候我们把2这张抽了出来,之后将3往后挪了一下再把2插入手中。
  3. 在j这个循环体中就是在将一些数字往后移动,等到确定之后才执行插入操作。

4. 冒泡排序

虽然优化后能达到时间复杂度O(n),但是其排序过程中交换频繁所以导致整体性能还是劣于插入排序算法,所以还是插入排序牛皮一点咯。

4.1. 实现过程

假定我们要从小到大排序:
两两比较,将一个目标数(最大或最小)数往后移动。你可以将它理解成挤牙膏的过程,最后一边多一边就少了。

冒泡排序动画演示

4.2. 代码片段-常规写法

func BubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ { //把大的数挪到最后,其实就是从小到大排序
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
}

4.3. 代码片段-优化后

func BubbleSort(arr []int) {
    flag := false
    n := len(arr)
    for i := 0; i < n-1; i++ {
        flag = false
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                flag = true
            }
        }
        if !flag {//整个冒泡过程中没有交换元素,代表着这整个数组已经是有序的了。
            break
        }
    }
}

4.4. 代码解释

  1. 首先我们可以不看这个flag标记,简单理解一下代码。
  2. 关于边界的问题,冒泡排序在最后只剩两个元素的时候只需要排序依次,所以总的需要排序的数字的n-1个 对应下标在 [0…n-1)
  3. 这里我们每次一都将相邻元素比较大的数往后移动,所以整个排序过程中其实是优先排序较大的数。这样子尾部就是最大的数了,在下一轮循环中我们就只需要排序 [0…n-1-i) 个了。
  4. 加入一个标记参数 falg 对整个排序过程记录是否有个交换记录,如果没有交换的历史代表此时数组是有序的。这个过程也可以用来判断一个数组是否有序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值