Go 语言实现常见排序算法

开发 前端
利用递归与分治技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列。其中“归”代表的是递归的意思,即递归地将数组折半地分离为单个数组。

1.计数排序

package sort

func countingSort(arr []int, bias int) (retArr []int) {
  countingArr := make([]int, bias+1, bias+1)
  retArr = make([]int, len(arr), cap(arr))
  for _, v := range arr {
    countingArr[v]++
  }
  for i := 1; i < len(countingArr); i++ {
    countingArr[i] += countingArr[i-1]
  }
  for i := len(arr) - 1; i >= 0; i-- {
    retArr[countingArr[arr[i]]-1] = arr[i]
    countingArr[arr[i]]--
  }
  return
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

2.插入排序

插入排序,英文名(insertion sort)是一种简单且有效的比较排序算法。

思想:在每次迭代过程中算法随机地从输入序列中移除一个元素,并将改元素插入待排序序列的正确位置。重复该过程,直到所有输入元素都被选择一次,排序结束。

类比:抓牌

package sort

func insertionSort(unsorted []int) {
    length = len(unsorted)
    for i := 0; i < length; i++ {
        var insertElement = unsorted[i]
        var insertPosition = i
        for j := insertPosition - 1; j >= 0; j-- {
            if insertElement < unsorted[j] {
                unsorted[j+1] = unsorted[j]
                insertPosition--
            }
        }
        unsorted[insertPosition] = insertElement
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

详见文章:跟着动画学 Go 数据结构之插入排序

3.冒泡排序

冒泡排序是一种最简单的交换排序算法。

什么是交换?交换就是两两进行比较,如果不满足次序就可以交换位置。比如,我们想要从小到大排序,通过两个位置上的值两两比较,如果逆序就交换,使关键字大的记录像泡泡一样冒出来放在末尾。重复执行若干次冒泡排序,最终得到有序序列。

类比: 值较小的元素如同”气泡“一样逐渐漂浮到序列的顶端。

package sort

func bubbleSort(nums []int) {

    length = len(nums)
    
    lastSwap := length - 1
    lastSwapTemp := length - 1
    
    for j := 0; j < length; j++ {
        lastSwap = lastSwapTemp
        for i := 0; i < lastSwap; i++ {
            if nums[i] > nums[i+1] {
                nums[i], nums[i+1] = nums[i+1], nums[i]
                lastSwapTemp = i
            }
        }

        if lastSwap == lastSwapTemp {
            break
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

4.选择排序

选择排序(selection sort)是一种原地(in-place)排序算法,适用于数据量较少的情况。由于选择操作是基于键值的且交换操作只在需要时才执行,所以选择排序长用于数值较大和键值较小的文件。

package sort

func selectionSort(nums []int) {

  length = len(nums)
  var minIdx int // 被选择的最小的值的位置
  for i := 0; i < length-1; i++ {
    minIdx = i
    // 每次循环的第一个元素为最小值保存
    var minValue = nums[minIdx]
    for j := i; j < length-1; j++ {
      if minValue > nums[j+1] {
        minValue = nums[j+1]
        minIdx = j + 1
      }
    }
    // 如果最小值的位置改变,将当前的最小值位置保存
    if i != minIdx {
      var temp = nums[i]
      nums[i] = nums[minIdx]
      nums[minIdx] = temp
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

详见文章:跟着动画学Go数据结构之选择排序

5.堆排序

堆排序是一种树形选择排序算法。堆排序的过程:

构建初始堆

在输出堆的顶层元素后,从上到下进行调整,将顶层元素与其左右子树的根节点进行比较,并将最小的元素交换到堆的顶部;然后不断调整直到叶子节点得到新的堆。

package sort

import (
  "github.com/shady831213/algorithms/heap"
)

func heapSort(arr []int) {
  h := heap.NewHeapIntArray(arr)
  for i := h.Len() - 1; i > 0; i-- {
    h.Pop()
  }
}

/*not generic heap*/
type intArrayForHeapSort []int

func (h *intArrayForHeapSort) parent(i int) int {
  return i >> 1
}
func (h *intArrayForHeapSort) left(i int) int {
  return (i << 1) + 1
}
func (h *intArrayForHeapSort) right(i int) int {
  return (i << 1) + 2
}

func (h *intArrayForHeapSort) maxHeaplify(i int) {
  largest, largestIdx := (*h)[i], i
  if (*h).left(i) < len((*h)) && (*h)[(*h).left(i)] > largest {
    largest, largestIdx = (*h)[(*h).left(i)], (*h).left(i)
  }
  if h.right(i) < len((*h)) && (*h)[h.right(i)] > largest {
    _, largestIdx = (*h)[h.right(i)], h.right(i)
  }
  if i != largestIdx {
    (*h)[largestIdx], (*h)[i] = (*h)[i], (*h)[largestIdx]
    h.maxHeaplify(largestIdx)
  }
}

func (h *intArrayForHeapSort) buildHeap() {
  for i := (len((*h)) >> 1) - 1; i >= 0; i-- {
    h.maxHeaplify(i)
  }
}

func heapSort2(arr []int) {
  h := intArrayForHeapSort(arr)
  h.buildHeap()
  for i := len(h) - 1; i > 0; i-- {
    h[0], h[i] = h[i], h[0]
    h = h[:i]
    h.maxHeaplify(0)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

详见文章:跟着动画学Go数据结构之堆排序

6.希尔排序

package sort

func swap(array []int, a int, b int) {
    array[a] = array[a] + array[b]
    array[b] = array[a] - array[b]
    array[a] = array[a] - array[b]
}

func shellSort(array []int) {

    length = len(array)
    for gap := length / 2; gap > 0; gap = gap / 2 {
        for i := gap; i < length; i++ {
            var j = i
            for {
                if j-gap < 0 || array[j] >= array[j-gap] {
                    break
                }
                swap(array, j, j-gap)
                j = j - gap
            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

详见文章:跟着动画学Go数据结构之希尔排序

7.归并排序

利用递归与分治技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列。其中“归”代表的是递归的意思,即递归地将数组折半地分离为单个数组。

给定一组序列含n个元素,首先将每两个相邻的长度为1的子序列进行归并,得到n/2(向上取整)个长度为2或1的有序子序列,再将其两两归并,反复执行此过程,直到得到一个有序序列为止。

package sort

/*
merge sort O(nlgn):
T(n) = 2T(n/2) + O(n)
master theorem:
a = 2, b = 2, f(n) = n
logb(a) = lg2 = 1 f(n) = f(n^logb(a)) = f(n^1)
so, O(n) = O(n^logb(a)lgn) = O(nlgn)
*/
import (
  "sync"
)

func merge(arr []int) {
  i := len(arr) / 2
  //copy left and right array
  leftArr, rightArr := make([]int, i, i), make([]int, len(arr)-i, len(arr)-i)
  copy(leftArr, arr[:i])
  copy(rightArr, arr[i:])
  leftIter, rightIter := ints(leftArr).Iter(), ints(rightArr).Iter()
  leftValue, leftHasNext := leftIter()
  rightValue, rightHasNext := rightIter()
  //merge
  for k := range arr {
    if !leftHasNext { //left empty, use right value, in CLRS, use infinity
      arr[k] = rightValue
      rightValue, rightHasNext = rightIter()
    } else if !rightHasNext { //right empty, use left value, in CLRS, use infinity
      arr[k] = leftValue
      leftValue, leftHasNext = leftIter()
    } else {
      if leftValue > rightValue {
        arr[k] = rightValue
        rightValue, rightHasNext = rightIter()
      } else {
        arr[k] = leftValue
        leftValue, leftHasNext = leftIter()
      }
    }
  }
}

func mergeSort(arr []int) {
  i := len(arr) / 2
  if i > 0 {
    mergeSort(arr[:i])
    mergeSort(arr[i:])
    merge(arr)
  }
}

func mergeSortParallel(arr []int) {
  i := len(arr) / 2
  if i > 0 {
    var wd sync.WaitGroup
    wd.Add(2)
    go func() {
      mergeSortParallel(arr[:i])
      wd.Done()
    }()
    go func() {
      mergeSortParallel(arr[i:])
      wd.Done()
    }()
    wd.Wait()
    merge(arr)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

8.快速排序

高效的排序算法,它采用 分而治之 的思想,把大的拆分为小的,小的再拆分为更小的。

其原理是:对于一组给定的记录,通过一趟排序后,将原序列分为两部分,其中前部分的所有记录均比后部分的所有记录小,然后再依次对前后两部分的记录进行快速排序,递归该过程,直到序列中的所有记录均有序为止。

package sort

import "math/rand"

func partition(arr []int) (primeIdx int) {
  primeIdx = 0
  for i := 0; i < len(arr)-1; i++ {
    if arr[i] < arr[len(arr)-1] {
      arr[i], arr[primeIdx] = arr[primeIdx], arr[i]
      primeIdx++
    }
  }
  arr[primeIdx], arr[len(arr)-1] = arr[len(arr)-1], arr[primeIdx]
  return
}

func quickSort(arr []int) {
  if len(arr) > 1 {
    primeIdx := partition(arr)
    quickSort(arr[:primeIdx])
    quickSort(arr[primeIdx+1:])
  }
}

func randomQuickSort(arr []int) {
  if len(arr) > 1 {
    primeIdx := rand.Intn(len(arr))
    arr[primeIdx], arr[len(arr)-1] = arr[len(arr)-1], arr[primeIdx]
    primeIdx = partition(arr)
    randomQuickSort(arr[:primeIdx])
    randomQuickSort(arr[primeIdx+1:])
  }
}

func quickSortTail(arr []int) {
  for len(arr) > 1 {
    primeIdx := partition(arr)
    if primeIdx < len(arr)/2 {
      quickSortTail(arr[:primeIdx])
      arr = arr[primeIdx+1:]
    } else {
      quickSortTail(arr[primeIdx+1:])
      arr = arr[:primeIdx]
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
责任编辑:武晓燕 来源: 宇宙之一粟
相关推荐

2023-05-08 07:55:05

快速排序Go 语言

2022-05-19 14:14:26

go语言限流算法

2022-04-18 10:01:07

Go 语言汉诺塔游戏

2020-08-12 08:56:30

代码凯撒密码函数

2024-08-29 13:23:04

WindowsGo语言

2013-02-21 17:02:00

C语言

2012-03-13 10:40:58

Google Go

2021-06-09 09:06:52

Go语言算法

2022-04-27 15:18:30

Go 语言API排序算法

2022-05-17 12:23:25

排序算法面试

2021-08-04 08:56:34

语言Go排序

2012-08-06 08:50:05

Go语言

2021-07-12 15:50:55

Go 语言netstat命令

2024-06-06 09:47:56

2022-05-07 08:55:11

Go语言排序算法

2023-07-31 08:01:13

二叉搜索测试

2021-07-26 09:47:38

Go语言C++

2023-03-27 00:20:48

2021-03-01 18:35:18

Go语言虚拟机

2021-03-01 21:59:25

编程语言GoCX
点赞
收藏

51CTO技术栈公众号