十大排序算法 Go语言实现

620 阅读4分钟

1.冒泡排序(Bubble Sort)

冒泡排序是一种简单的交换排序算法。它依次比较相邻的两个元素,如果它们的顺序错误,就交换它们,直到没有需要交换的元素。这个过程像气泡冒到表面一样,所以称为冒泡排序。

时间复杂度: O(n^2) ,空间复杂度: O(1)。当数据已经有序时,冒泡排序的时间复杂度可以达到O(n)。

优劣分析:易于理解和实现,但是时间复杂度较高,为O(n^2),不适用于大规模数据排序。

func sortArray(nums []int) []int {
   n := len(nums)
   for i := 0; i < n; i++ {
      isSwap := false
      for j := 0; j < n-i-1; j++ {
         if nums[j+1] < nums[j] {
            // 交换
            nums[j+1], nums[j] = nums[j], nums[j+1]
            isSwap = true
         }
      }
      // 如果没有交换过,说明已经排序完成,退出循环
      if !isSwap {
         break
      }
   }
   return nums
}

2.选择排序(Selection Sort)

选择排序是一种简单的选择排序算法。它一次选择数组中最小的元素,并将其放到数组的开头。然后从剩余未排序的元素中继续选择最小的元素,将其放在已排序的数组的末尾。

时间复杂度: O(n^2) ,空间复杂度: O(1)

选择排序时间复杂度也为O(n^2),但是与冒泡排序相比,选择排序的交换次数较少。

func sortArray(nums []int) []int {
   n := len(nums)
   for i := 0; i < n-1; i++ {
      min := i
      for j := i + 1; j < n; j++ {
         if nums[j] < nums[min] {
            min = j
         }
      }
      nums[i], nums[min] = nums[min], nums[i]
   }
   return nums
}

3. 插入排序(Insertion Sort)

插入排序是一种简单地插入排序算法。它通过逐个将未排序的元素插入到已排序的元素中,以达到排序的目的。

时间复杂度: O(n^2) 空间复杂度: O(1)

优劣分析:插入排序的时间复杂度也为O(n^2),但是对于近乎有序的序列,插入排序的效率较高。

func sortArray(nums []int) []int {
    n:=len(nums)
    for i := 1; i < n; i++ {
        j := i
        for j > 0 && nums[j] < nums[j-1] {
            nums[j], nums[j-1] = nums[j-1], nums[j]
            j--
        }
    }
    return nums
}

4. 希尔排序(Shell Sort)

希尔排序是一种改进的插入排序算法。它通过交换不相邻的元素以使用插入排序的优点,同时也避免了插入排序的缺点,即向已排序数组插入元素需要将所有大于待插入元素的元素后移。

希尔排序的基本思想是:将数组按照一定间隔进行分组,对每组使用插入排序算法排序,不断缩小间隔直至为1,最后对整个数组进行一次插入排序。

时间复杂度: O(n log n) - O(n^2) 空间复杂度: O(1)。希尔排序是不稳定的排序算法。

优劣分析:时间复杂度约为O(n^1.3),效率较高。但是希尔排序的选取增量gap的方式比较困难。

func sortArray(nums []int) []int {
    n:=len(nums)
    gap := n/2
    for gap > 0 {
        for i := gap; i < n; i++ {
            j := i
            for j-gap >= 0 && nums[j] < nums[j-gap] {
                nums[j], nums[j-gap] = nums[j-gap], nums[j]
                j -= gap
            }
        }
        gap /= 2
    }
    return nums
}

5. 归并排序(Merge Sort)

归并排序是分治算法的典型应用,它将待排序序列分成若干个子序列,每个子序列都是有序的,然后再把有序子序列合并成一个有序的序列。归并排序是稳定的排序算法。

优劣分析:归并排序的时间复杂度为O(nlogn),但是需要额外的存储空间。同时,在处理链表时,归并排序比较常用,因为链表没有顺序存储的限制。

func sortArray(nums []int) []int {
    if len(nums) < 2 {
        return nums
    }
    mid := len(nums) / 2
    return merge(sortArray(nums[:mid]), sortArray(nums[mid:]))
}

func merge(left, right []int) []int {
    result := make([]int, 0)
    for len(left) > 0 && len(right) > 0 {
        if left[0] <= right[0] {
            result = append(result, left[0])
            left = left[1:]
        } else {
            result = append(result, right[0])
            right = right[1:]
        }
    }
    if len(left) > 0 {
        result = append(result, left...)
    }
    if len(right) > 0 {
        result = append(result, right...)
    }
    return result
}

6. 快速排序(Quick Sort)

快速排序同样是分治算法的典型应用,它的基本思想:选择一个基准元素,将序列分成两个子序列,比基准元素小的放到左子序列,比基准元素大的放到右子序列,然后对左右子序列进行递归排序。

时间复杂度为O(n log n),空间复杂度为O(log n)。快速排序是不稳定的排序算法。

func sortArray(list []int) []int {
    if len(list) <= 1 {
        return list
    }

    pivot := list[0]
    left, right := 1, len(list)-1

    for left <= right {
        if list[left] > pivot && list[right] < pivot {
            list[left], list[right] = list[right], list[left]
        }
        if list[left] <= pivot {
            left++
        }
        if list[right] >= pivot {
            right--
        }
    }

    list[0], list[right] = list[right], list[0]
    sortArray(list[:right])
    sortArray(list[right+1:])

    return list
}

7. 堆排序 (heapSort)

堆排序是一种选择排序,它利用堆这种数据结构特性来进行排序。堆分为大根堆和小根堆,在堆中,如果父节点的值大于或等于子节点的值,则称为大根堆,反之称为小根堆。

时间复杂度为O(n log n),空间复杂度为O(1)。堆排序是不稳定的排序算法。

func sortArray(list []int) []int{
    // 构建大根堆
    for i := len(list)/2 - 1; i >= 0; i-- {
        heapify(list, i, len(list))
    }

    // 从大根堆中取出元素,放到有序序列末尾
    for i := len(list) - 1; i > 0; i-- {
        list[0], list[i] = list[i], list[0]
        heapify(list, 0, i)
    }
    return list
}

func heapify(list []int, i, length int) {
    largest := i
    left, right := 2*i+1, 2*i+2

    if left < length && list[left] > list[largest] {
        largest = left
    }

    if right < length && list[right] > list[largest] {
        largest = right
    }

    if largest != i {
        list[i], list[largest] = list[largest], list[i]
        heapify(list, largest, length)
    }
}

8. 计数排序 (countingSort)

计数排序是一种非比较排序算法,是一种线性时间复杂度的排序算法。计数排序的核心思想是将输入数据转化为键存储在额外开辟的数组空间中,其排序速度快于任何比较排序算法。

其中,第一个循环用于找出待排序数组中的最大值和最小值;第二个循环用于计算原始数组中每个元素出现的次数,并在计数数组相应位置加1;第三个循环用于累加计数数组中的元素,得到元素在有序数组中对应的下标;第四个循环用于迭代原始数组,根据元素在计数数组中的位置,将元素依次放入有序数组中。

时间复杂度为O(n+k),其中k为整数范围,空间复杂度为O(n+k)。计数排序是稳定的排序算法。

func sortArray(arr []int) []int {
    minVal, maxVal := arr[0], arr[0]
    for _, val := range arr {
        if val < minVal {
            minVal = val
        }
        if val > maxVal {
            maxVal = val
        }
    }

    counts := make([]int, maxVal-minVal+1)
    for _, val := range arr {
        counts[val-minVal]++
    }

    for i := 1; i < len(counts); i++ {
        counts[i] += counts[i-1]
    }

    result := make([]int, len(arr))
    for i := len(arr) - 1; i >= 0; i-- {
        idx := arr[i] - minVal
        result[counts[idx]-1] = arr[i]
        counts[idx]--
    }
    return result
}

9. 桶排序 (bucketSort)

桶排序是一种非比较排序算法,它的基本思想:将数组分到有限数量的桶子里,然后对每个桶子再进行单独排序。

func sortArray(list []int) []int {
    bucketSize:=len(list)/2
    if len(list) <= 1 {
        return list
    }

    // 计算最大值和最小值
    max, min := list[0], list[0]
    for _, value := range list {
        if value > max {
            max = value
        }
        if value < min {
            min = value
        }
    }

    // 计算桶的数量
    bucketCount := (max-min)/bucketSize + 1
    buckets := make([][]int, bucketCount)

    // 将元素放入桶中
    for _, value := range list {
        index := (value - min) / bucketSize
        buckets[index] = append(buckets[index], value)
    }

    // 对每个桶中的元素进行排序,并按顺序放入结果中
    result := make([]int, 0, len(list))
    for _, bucket := range buckets {
        if len(bucket) > 0 {
            insertSort(bucket)
            result = append(result, bucket...)
        }
    }

    return result
}

func insertSort(list []int) {
    for i := 1; i < len(list); i++ {
        temp := list[i]
        j := i - 1
        for ; j >= 0 && list[j] > temp; j-- {
            list[j+1] = list[j]
        }
        list[j+1] = temp
    }
}

时间复杂度为O(n+k),其中k为桶的数量,空间复杂度为O(n+k)。桶排序是稳定的排序算法。

10. 基数排序 (radixSort)

基数排序是一种非比较排序算法,它的基本思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。

时间复杂度为O(kn),其中k为整数位数,空间复杂度为O(n+k)。基数排序是稳定的排序算法。

func sortArray(arr []int) []int {
   maxVal, minVal := arr[0], arr[0]
   for _, val := range arr {
      if val > maxVal {
         maxVal = val
      }
      if val < minVal {
         minVal = val
      }
   }
   if maxVal < 0 { // 如果数组中全是负数,则直接返回
      return arr
   }

   // 对于存在负数的情况,需要将负数映射到正数
   offset := 0
   if minVal < 0 {
      offset = -minVal
   }
   for i := 1; (maxVal+offset)/i > 0; i *= 10 {
      countingSortByDigit(arr, i, offset)
   }
   return arr
}

func countingSortByDigit(arr []int, digit int, offset int) {
   counts := make([]int, 10)
   for _, val := range arr {
      index := ((val + offset) / digit) % 10
      counts[index]++
   }

   for i := 1; i < len(counts); i++ {
      counts[i] += counts[i-1]
   }

   result := make([]int, len(arr))
   for i := len(arr) - 1; i >= 0; i-- {
      index := ((arr[i] + offset) / digit) % 10
      result[counts[index]-1] = arr[i]
      counts[index]--
   }

   copy(arr, result)
}