算法(4)-常见的排序算法

前言

说到算法,就不得不说一下排序了,相信很多人的算法是从排序开始的,哪怕是算法导论这本书,也是从排序开始讲的算法,所以在说完了之前的各种算法是思想之后,是时候真刀真枪的干活了。这一篇就说说算法中经常遇到的各种排序算法(代码使用kotlin实现)

  1. 冒泡排序
  2. 选择排序
  3. 插入排序
  4. 希尔排序
  5. 快速排序
  6. 归并排序

冒泡排序和选择排序

为什么把这两个放一起说呢,说到排序算法,冒泡排序和选择排序可谓是基础中的基础了,拿这两个热身是再好不过了

冒泡排序

定义

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”

具体实现

fun bubbleSort(arr: IntArray?) {
    if (arr == null || arr.size == 0)
        return
    for (i in 0 until arr.size - 1) {
        for (j in arr.size - 1 downTo i + 1) {
            if (arr[j] < arr[j - 1]) {
                swap(arr, j - 1, j)
            }
        }
    }
}

选择排序

定义

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法

具体实现

fun selectSort(arr: IntArray?) {
    if (arr == null || arr.size == 0)
        return
    var minIndex = 0
    for (i in 0 until arr.size - 1) { //只需要比较n-1次
        minIndex = i
        for (j in i + 1 until arr.size) { //从i+1开始比较,因为minIndex默认为i了,i就没必要比了。
            if (arr[j] < arr[minIndex]) {
                minIndex = j
            }
        }

        if (minIndex != i) { //如果minIndex不为i,说明找到了更小的值,交换之。
            swap(arr, i, minIndex)
        }
    }

}

冒泡排序和选择排序最标志性的特征就是双重for循环了,这也说明了,效率低的时候简直不忍直视,这里就不多说了

插入排序和希尔排序

说完了冒泡排序和选择排序,就接下来说说这两货了,插入排序和希尔排序,这两货的思想相差不大,甚至可以用一篇代码实现,自然就放一起了

插入排序

定义

插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。其实我们玩扑克牌时的排序,用的就是插入排序

具体实现

fun insertSort(arr: IntArray?) {
    if (arr == null || arr.size == 0)
        return
    for (i in 1 until arr.size) {

        var j = i
        val target = arr[i]

        //后移
        while (j >= 1 && target < arr[j - 1]) {
            arr[j] = arr[j - 1]
            j--
        }

        //插入
        arr[j] = target
    }

}

希尔排序

定义

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法


希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

具体实现

fun shellSort(arr: IntArray) {
    if (arr == null || arr.size == 0)
        return
    var step = arr.size / 2
    while (step > 0) {
        for (i in step until arr.size) {
            var j = i
            var target = arr[i]

            while (j >= step && target < arr[j - step]) {  //从后向前,找到比其小的数的位置
                arr[j] = arr[j - step]  //向后挪动
                j -= step
            }

            if (j != i)    //存在比其小的数
                arr[j] = target
        }
        step /= 2
    }
}

仔细看插入排序和希尔排序的代码,是不是发现两者很相似,如果将希尔排序中的step设置为1,那么它就是插入排序了,所以我说,这两者甚至可以用一篇代码实现

快速排序和归并排序

接下来再说说快速排序和归并排序,之前在分治法的文中提到了这两种排序算法,它们都是分治法思想的算法,所以这里也是放一起说明

快速排序

定义

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

具体实现

例如如图:


  1. 首先设置两个变量分别从头和尾进行索引
  2. 将第一个元素3存储起来temp
  3. 从尾向前索引,找到第一个比3小的元素,放入3的位置,尾部索引减1,然后再从头向尾索引,找到第一个比3大的元素9,放入0的位置,头部索引加1
  4. 重复2,3两步,知道两个索引变量在某次减1或者加1的时候相等时为一次快速排序,然后以这个相等的变量为分割,分别将它左边和右边的数组再次进行同样的快速排序操作,直到排序结束
  5. 例如这个数组,第二次从尾部索引找比3小的元素2,放入9的位置,然后从头部索引找比3大的元素,当头部索引和尾部索引都指向2时,虽然此时头部索引还没有找到比3大的元素,但是已经完成一次快速排序了,所以讲3放入此时索引指向的位置,这样一次快速排序就结束了,然后不断是循环即可
//快速排序    左闭右开
fun quickSort(arr: IntArray, left: Int, right: Int) {
    if (left >= right)
        return
    val pivotPos = partition(arr, left, right)
    quickSort(arr, left, pivotPos - 1)
    quickSort(arr, pivotPos + 1, right)
}
//一个循环
fun partition(arr: IntArray, left: Int, right: Int): Int {
    var left = left
    var right = right
    val pivotKey = arr[left]

    while (left < right) {
        while (left < right && arr[right] >= pivotKey)
            right--
        arr[left] = arr[right] //把小的移动到左边
        while (left < right && arr[left] <= pivotKey)
            left++
        arr[right] = arr[left] //把大的移动到右边
    }
    arr[left] = pivotKey //最后把pivot赋值到中间
    return left
}

归并排序

定义

归并排序是一个典型分治法思想的排序,它的思想是,如果两个序列是有序的,那么将它们合成,依然可以得到一个有序序列,而对于一个无需的序列,将它拆分为足够小的序列然后进行排序,最后将这些有序序列合并,那么就可以得到一个有序的序列

具体实现

//归并排序 
fun mergeSort(array: IntArray, left: Int, right: Int) {
    if (left == right) {
        return
    } else {
        val mid = (left + right) / 2
        mergeSort(array, left, mid)//左边
        mergeSort(array, mid + 1, right)//右边
        merge(array, left, mid + 1, right)//合并
    }
}

//合并
fun merge(array: IntArray, left: Int, mid: Int, right: Int) {
    val leftSize = mid - left
    val rightSize = right - mid + 1
    //生成数组
    val leftArray = IntArray(leftSize)
    val rightArray = IntArray(rightSize)
    //填充左边的数组
    for (i in left until mid) {
        leftArray[i - left] = array[i]
    }
    //填充右边的数组
    for (i in mid..right) {
        rightArray[i - mid] = array[i]
    }

    //合并数组
    var i = 0
    var j = 0
    var k = left
    while (i < leftSize && j < rightSize) {
        if (leftArray[i] < rightArray[j]) {
            array[k] = leftArray[i]
            k++
            i++
        } else {
            array[k] = rightArray[j]
            k++
            j++
        }
    }
    while (i < leftSize) {//左边剩余数据
        array[k] = leftArray[i]
        k++
        i++
    }
    while (j < rightSize) {//右边剩余数据
        array[k] = rightArray[j]
        k++
        j++
    }

}

当然,还有很多各式各样的排序算法,这里就不一样列举了,大家有兴趣可以自行探索

参考资料:
https://www.cnblogs.com/onepixel/articles/7674659.html

你可能感兴趣的:(算法(4)-常见的排序算法)