前言
说到算法,就不得不说一下排序了,相信很多人的算法是从排序开始的,哪怕是算法导论这本书,也是从排序开始讲的算法,所以在说完了之前的各种算法是思想之后,是时候真刀真枪的干活了。这一篇就说说算法中经常遇到的各种排序算法(代码使用kotlin实现)
- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 快速排序
- 归并排序
冒泡排序和选择排序
为什么把这两个放一起说呢,说到排序算法,冒泡排序和选择排序可谓是基础中的基础了,拿这两个热身是再好不过了
冒泡排序
定义
冒泡排序(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,那么它就是插入排序了,所以我说,这两者甚至可以用一篇代码实现
快速排序和归并排序
接下来再说说快速排序和归并排序,之前在分治法的文中提到了这两种排序算法,它们都是分治法思想的算法,所以这里也是放一起说明
快速排序
定义
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
具体实现
例如如图:
- 首先设置两个变量分别从头和尾进行索引
- 将第一个元素3存储起来temp
- 从尾向前索引,找到第一个比3小的元素,放入3的位置,尾部索引减1,然后再从头向尾索引,找到第一个比3大的元素9,放入0的位置,头部索引加1
- 重复2,3两步,知道两个索引变量在某次减1或者加1的时候相等时为一次快速排序,然后以这个相等的变量为分割,分别将它左边和右边的数组再次进行同样的快速排序操作,直到排序结束
- 例如这个数组,第二次从尾部索引找比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