不管是日常的逻辑开发还是面试时的笔试中,都会经常碰到对某些数据做排序,已有的排序算法有多种,像:冒泡排序、选择排序、插入排序、快速排序、堆排序、希尔排序、归并排序、基数排序等,不过说到底最常见的还是前面5种,不过在这里我就介绍前面4种啦,因为我在日常用的和面试的基本都是前面4种,堆排序问的也没有前面的多。上面的排名部分先后,不能直接断言这些排序哪个比哪个好,哪个比哪个效率高,因为根据数据量的不同、数据的有序程度不同都会影响排序的效率,这个要根据情况来决定使用哪种排序算法。
冒泡排序
冒泡排序是最简单的排序算法了,每次都拿第一个元素和后面的未放到最终位置的元素列中的元素依次做比较,并不断地和相邻的元素交换位置,直到这个元素和未放到最终位置的元素列的最后一个元素相比较为止,这就完成了一趟的排序,本趟排序就将一个元素放到最终位置上了,后面不断重复这个过程,直到所有元素排完序为止。
排序过程:
后续其他元素排序依次类推。总结起来就是一句话:每趟都从第一个元素开始依次和相邻元素做比较,得出2者中的较大值后将和下一个相邻的未排序元素作比较,直到没有下一个元素或者下一个元素是已排序元素停止。每趟的最终结果是得到一个未排序序列中的最大值放到最右侧最终位置(按从小到大排序,从大到小排序也是这个规律)
算法设计:
//冒泡排序
//arr:长度为8的整型数组
func BubbleSort(arr [8]int)[8]int{
//count:数组长度
count := len(arr)
for i := 0;i < count;i++ {
for j := 1;j < count - i;j++ {
if arr[j - 1] > arr[j] {
//元素交换位置
arr[j - 1],arr[j] = arr[j],arr[j - 1]
}
}
}
return arr
}
选择排序
冒泡排序每次都是将值往右边推直到放到最终位置,而选择排序不同,选择排序每趟比较完都会将值放到左侧最终位置。可以说冒泡排序的最终位置放置位置和选择排序的最终位置放置是相反的,冒泡排序总是从最右侧开始填满有序序列,而选择排序总是从最左侧开始填满有序序列。(同样以从小到大排序为例)选择排序每趟从未排序序列中取得首个元素并依次和右侧相邻元素比较,比较出2者的最小值后记录最小值元素索引,下一次比较就用当前记录的最小元素的索引取得的最小元素和下一个元素比较,得出最小元素的索引并依次类推,直到比较到整个序列最后一个元素为止。这时候我们就得到了当前未排序序列中最小元素对应的索引,通过该索引可以取到本趟最小值元素,并将该最小值元素和已排序序列中的最后一个元素的下一个元素交换位置,从而实现本趟排序得出的最小元素放到最终位置。下一趟元素开始就从该元素的下一个元素开始依次往后比较,因为该元素已加入有序序列,无需调整。
排序过程:
后续其他元素排序依次类推。总结:每次都从未排序序列中选出一个最小值,并将该最小值与未排序序列的首个元素交换位置,因为未排序序列的左侧为已排序序列,已排序序列中的元素都是放到最终位置的,因此无需比较(按从小到大排序)
算法设计:
//选择排序
func SelectionSort(arr [8]int)[8]int{
count := len(arr)
for i := 0;i < count;i++ {
min := i
for j := i + 1;j < count;j++ {
if arr[min] > arr[j] {
min = j
}
}
arr[i],arr[min] = arr[min],arr[i]
}
return arr
}
插入排序
插入排序与前面的冒泡排序和选择排序不同,冒泡排序和选择排序每一趟都会将一个元素放到最终位置,而插入排序每趟只是将一个元素加入到有序序列中,但是这些元素虽然有序,却不一定是处在最终位置。插入排序每趟都会取未排序序列的首个元素与左侧的已排序序列中的元素依次比较,若该元素小于已排序序列中的某个元素,就进行交换,知道遇到比当前整个元素小的元素为止。这样就把元素插入到已排序序列中构成一个新的有序序列了。虽然是有序序列,但并不表示这些数据就放到了其最终的位置,因为你永远不知道未排序序列中的元素是否一定会大于已排序序列中的最后一个元素(即最大元素)
排序过程:
后续其他元素排序依次类推。总结:插入排序在整个乱序序列上分2部分,左部分是有序序列,右部分是无序序列,现在的任务就是将无序序列中的每一个元素依次和有序序列中的元素比较,直到放到合适的位置使有序序列构成一个新的扩容的新有序序列,我们的新元素就相当于插入原来的有序序列中。我们默认第一个元素就位于一个有序序列之中,不需要拿第一个元素和第0个元素比较(没有所谓的第0个元素,因为做for循环从0开始表示拿第1一个元素和第0个元素比较,是没有任何意义的,因此这里从1开始)
算法设计:
//插入排序
func InsertionSort(arr [8]int)[8]int{
count := len(arr)
for i := 1;i < count;i++ {
for j := i;j > 0;j-- {
if arr[j - 1] > arr[j] {
arr[j - 1],arr[j] = arr[j],arr[j - 1]
}
}
}
return arr
}
快速排序
快速排序是一种典型的大事化小的排序算法,它将一个原本要进行排序的无序序列划分成若干个小的序列进行比较和交换,每一趟都能将一个元素放到最终的位置,我们每次都取序列的首个元素(这个元素叫做枢轴)进行比较并在本趟排序结束之后放到最终位置。再以这个元素作为分界将整个序列分成2部分的子序列,再分别在子序列中完成枢轴元素的最终位置摆放。这样不断将整个序列进行递归分割和排序,最终可以实现对整个无序序列的排序。快速排序的思想就是要求任何元素左边的比它小,右变的比它他大。
排序过程:
后续其他元素排序依次类推。总结:快速排序只有一个宗旨,那就是我希望枢轴元素的左边元素都比枢轴元素要小,右边元素都要比枢轴元素要大,按照这个思路去进行比较和交换元素位置,直到本次所有元素比较完毕就能成功将本次的枢轴元素放到最终位置。接着以该枢轴元素作为分界(因为它已经放到最终位置了,就不要再动它了)左部分和右部分按照相同的做法去比较交换即可。Tip:比较之后如果需要进行元素交换,并不是直接将枢轴元素和需要交换的元素直接进行交换就可以的,这样的话无法确保在交换后枢轴元素左侧的元素都比它小。具体可以参考上面快速排序第一张图中第4行数据中的6和2的比较交换。
算法设计:
func QuickSort(arr []int,left,right int){
if left >= right {
return
}
pos := left
for i := pos + 1;i <= right;i++ {
if arr[pos] > arr[i] {
arr[pos + 1],arr[i] = arr[i],arr[pos + 1]
arr[pos],arr[pos + 1] = arr[pos + 1],arr[pos]
pos++
}
}
QuickSort(arr,left,pos - 1)
QuickSort(arr,pos + 1,right)
}