首先,定义一个函数,交换数组中给定下标的两个元素
fun swap(list: ArrayList<Int>, index1: Int, index2: Int) {
val maxIndex = - 1
val minIndex = 0
if (index1 < minIndex || index1 > maxIndex) throw IndexOutOfBoundsException()
if (index2 < minIndex || index2 > maxIndex) throw IndexOutOfBoundsException()
val tmp = list[index1]
list[index1] = list[index2]
list[index2] = tmp
}
1. 冒泡排序
fun bubbleSort(list: ArrayList<Int>) {
if ( == 0) return
val maxIndex = - 1
for (n in 0 until maxIndex) {
for (i in 0 until maxIndex - n) {
if (list[i] > list[i + 1]) {
swap(list, i, i + 1)
}
}
}
}
以上实现的冒泡排序,即使在最好的情况下(输入已经有序),时间复杂度仍为O(n^2)。将其改进,在最好的情况下时间复杂度为O(n):
fun bubbleSort(list: ArrayList<Int>) {
if ( == 0) return
val maxIndex = - 1
var haveSwap = false // 标识算法执行过程中是否发生过交换操作
for (n in 0 until maxIndex) {
for (i in 0 until maxIndex - n) {
if (list[i] > list[i + 1]) {
swap(list, i, i + 1)
haveSwap = true
}
}
if (!haveSwap) return // 快速结束
}
}
2.快速排序
这是我最熟练的排序算法了。
fun quickSort(list: ArrayList<Int>) {
quickSort(list, 0, - 1)
}
fun quickSort(list: ArrayList<Int>, minIndex: Int, maxIndex: Int) {
if (minIndex >= maxIndex) return
var i = minIndex - 1
var tmp = list[maxIndex] // 工程实现中应该随机取一个元素
for (j in minIndex until maxIndex) {
if (list[j] <= tmp) {
i++
swap(list, i, j)
}
}
swap(list, i + 1, maxIndex)
quickSort(list, minIndex, i)
quickSort(list, i + 2, maxIndex)
}
3.选择排序
基本思想就是,每次从前半部分选择最大的元素,放到后半部分,反复迭代或递归。因此,首先定义一个函数,返回最大元素的下标。
fun max(list: ArrayList<Int>, lastIndex: Int): Int { // 注意是闭区间 [0,lastIndex]
if (lastIndex == 0) return 0
var maxIndex = 0
var maxValue = list[0]
for (i in 1..lastIndex) {
if (list[i] > maxValue) {
maxValue = list[i]
maxIndex = i
}
}
return maxIndex
}
在此基础上,实现选择排序
fun selectionSort(list: ArrayList<Int>) {
val maxIndex = - 1
for (i in maxIndex downTo 0) {
var idx = max(list, i)
swap(list, idx, i)
}
}
由于没有维护一个堆结构,从数组中选取最大元素的时间复杂度为O(N),选择排序的时间复杂度为O(N^2)。
4. 插入排序
有点类似于选择排序。前半部分有序,后半部分无序,每次从后半部分取出一个元素,插入到前半部分合适的位置。
这里需要依赖于二分查找,在找不到元素时,返回元素应该插入的位置。
fun insertionSort(list: ArrayList<Int>) {
val maxIndex = - 1
for (i in 1..maxIndex) { // [1,maxIndex]
val target = list[i]
var idx = binarySearch(list, target, 0, i - 1)
for (j in i downTo idx + 1) { // 循环将元素后移
list[j] = list[j - 1]
}
list[idx] = target
}
}
5.归并排序
归并操作
fun merge(list: ArrayList<Int>, lo: Int, mi: Int, hi: Int) { // [lo,mi) [mi,hi)
// 初始化两个数组,主要是为了在最后加上哨兵
var leftLength = mi - lo + 1
var rightLength = hi - mi + 1
var leftList = ArrayList<Int>(leftLength)
var rightList = ArrayList<Int>(rightLength)
for (i in lo until mi) {
(list[i])
}
(Int.MAX_VALUE) // 哨兵
for (i in mi until hi) {
(list[i])
}
(Int.MAX_VALUE) // 哨兵
// 归并
var leftIndex = 0
var rightIndex = 0
var idx = lo
val n = hi - lo // 循环次数
for (i in 1..n) {
when {
leftList[leftIndex] < rightList[rightIndex] -> {
list[idx] = leftList[leftIndex]
leftIndex++
}
leftList[leftIndex] >= rightList[rightIndex] -> {
list[idx] = rightList[rightIndex]
rightIndex++
}
}
idx++
}
}
归并排序
fun mergeSort(list: ArrayList<Int>, minIndex: Int, maxIndex: Int) {
if (minIndex > maxIndex) return
if (maxIndex == minIndex) return
val mi = (minIndex + maxIndex) / 2
mergeSort(list, minIndex, mi)
mergeSort(list, mi + 1, maxIndex)
merge(list, minIndex, mi + 1 , maxIndex + 1) // 这里需要+1主要是因为归并的实现是右开区间的
}
实现的时候遇到一个坑,由于输入是[minIndex, maxIndex]这种闭区间,在划分成子问题的时候,必须是
mergeSort(list, minIndex, mi)
mergeSort(list, mi + 1, maxIndex)
而不能是
mergeSort(list, minIndex, mi - 1)
mergeSort(list, mi, maxIndex)
否则递归无法结束。