排序

1. 插入排序

  • 直接插入排序 (内存6.3MB,用时 944ms)

插排的思想是排序的排到底i个的时候,保证前面下标是0~(i-1)的序列是有序的。

func sortArray(nums []int) []int {
    for i,v := range nums {
        if i == 0{
            continue
        }
          // 左子序列判断+挪位子
        for j := i-1 ; j>=0 ; j--{
            if v < nums[j]{
                nums[j],nums[j+1] = nums[j+1], nums[j]
            }else{
                break
            }
        }
    }
    return nums
}

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

  • 折半(二分)插入排序 (内存6.3MB,用时452ms)

直接插入排序是边查边挪,折半插入排序是先用二分查找找到插入点,然后统一挪位置

func sortArray(nums []int) []int {
    var lens = len(nums)
    if lens <= 1 {
        return nums
    }
    for i,v := range nums{
        if i == 0 {
            continue
        }
        low := 0
        high := i-1
          // 二分查找
        for low <= high {
            mid := (low+high)/2
            if (nums[mid]> v) {
                high = mid - 1
            }else {
                low = mid +1
            }
        }
          // 往后挪
        for j := i-1 ; j>high ; j--{
            nums[j+1] = nums[j]
        }
        // 插入
        nums[high+1]=v
        // 重置高低下标
        low = 0
        high = i-1
    }
    return nums
}

比较次数降至 O(nlog2(n))

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

  • 希尔排序 (内存6.3MB,用时28ms)

以步长为单位,将下表为i的与i+d的元素比较,若i元素大于i+d元素,则交换,每一趟之后再将步长缩小为一般再次进行比较。

func sortArray(nums []int) []int {
    // 外层设置步长
    for d := len(nums)/2; d > 0; d /= 2 {
          // 中层设置趟数
        for i := range nums{
            for j:=i; j-d >=0; j-=d{
                if nums[j] < nums[j-d]{
                    nums[j],nums[j-d] = nums[j-d],nums[j]
                }else{
                    break
                }
            }
        }
    }
    return nums
}

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

2. 交换排序

  • 冒泡排序

从后往前或者两两比较交换次序,每一趟把最小的放在队首,或者将最大的队尾,每一趟之后将排序元素数减一进行多次交换 (小切片代码还行,大的直接超出时间限制)

func sortArray(nums []int) []int {
    var lens = len(nums)
    for i := range nums{
        for j:=i+1; j<lens; j++ {
            if nums[j] < nums[i] {
                nums[j],nums[i] = nums[i],nums[j]
            }
        }
    }
    return nums
}

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

  • 快速排序

先找个枢纽元素,默认为下标为0的元素,然后数组头尾设置标记点和枢纽元素比,找到2个元素,前面的比枢纽大,后面的比枢纽小,就交换。每一趟下来,枢纽元素的左侧都是比枢纽元素小的,右侧都是比枢纽元素大的,当头尾标记移动到相同位置时,此位置即为枢纽的位置。然后再对左右两个子序列进行递归。

递归方法:(内存6.4MB,用时24ms)

func sortArray(nums []int) []int {
    quickSort(nums)
    return nums
}

func quickSort(nums []int) {
    left, right := 0, len(nums) - 1
    for right > left {
        // 右边部分放大于
        if nums[right] > nums[0] {
            right--
            continue
        }
        // 左边部分放小于等于
        if nums[left] <= nums[0] {
            left++
            continue
        }
        nums[left], nums[right] = nums[right], nums[left]
    }
    nums[0], nums[right] = nums[right], nums[0]
    if len(nums[:right]) > 1 {
        sortArray(nums[:right])
    }
    if len(nums[right + 1:]) > 1 {
        sortArray(nums[right + 1:])
    }
}

时间复杂度 O(nlogn) 空间复杂度O(1)

3.选择排序

  • 简单选择排序 每趟一次交换 (内存6.4MB,用时1456 ms)

每一趟都和第 (i+1) 个到第 n 个比较,在和把最小的元素和i交换。

func sortArray(nums []int) []int {
    var (
        lens = len(nums)
        index int
        min int
    )
    for i,v := range nums{
        index = i
        min = v
        for j:=i+1; j<lens; j++ {
            if nums[j] < min {
                index = j 
                min = nums[j]
            }
        }
        if index != i {
            nums[i],nums[index] = nums[index],nums[i]
        }
    }
    return nums
}
  • 堆排序 (内存6.4MB,用时28ms)

func sortArray(nums []int) []int {
      // 先构建最大堆
    for i:= len(nums)/2; i >=0; i--{
        MaxHead(nums,i)
    }
    var end = len(nums)-1
    for end > 0 {
           // 每次把堆顶元素(切片下标1)和最后一个叶子节点互换
        nums[0],nums[end] = nums[end],nums[0]
        // 把最后一个叶子节点(上次排序的最大值)扣去,重新排序      
        MaxHead(nums[:end],0)
        end--
    }

    return nums
}

func MaxHead(nums []int,pos int){
  var (
      lens = len(nums) - 1 
      left = pos * 2 + 1
      right = left + 1
      step = left
  )
  if left > lens{
        return
    }
  // 右节点存在的处理
    if right <= lens {
        if  nums[right] > nums[left]{
            step = right
        }
    }
  // 子节点大就交换
    if nums[pos] < nums[step]{
        nums[pos],nums[step] = nums[step],nums[pos]
    // 递归子节点
        MaxHead(nums,step)
    }
  return
}

4.归并排序

  • 归并排序 (执行用时 32 ms,内存消耗 7.4 MB)

// 分治
func sortArray(nums []int) []int {
    var lens = len(nums)
    // 拆出来可能是单个的处理下
    if lens <= 1{
        return nums
    }
    // 比较+交换
    if lens == 2{
        if nums[0] > nums[1]{
            nums[1],nums[0] = nums[0],nums[1]
        }
        return nums
    }
    return Merge(sortArray(nums[:lens/2]),sortArray(nums[lens/2:]))
}
// 合并
func Merge(Pre,Post []int) []int{
    var lenPre,lenPost = len(Pre),len(Post)
    var list []int
    var i,j int 
    // 比较+合并
    for i < lenPre && j < lenPost{
        if Pre[i] <= Post[j]{
            list = append(list,Pre[i])
            i++
        }else{
            list = append(list,Post[j])
            j++
        }
    }
    // 多出来数的直接接到后面
    if i < lenPre{
        list = append(list,Pre[i:]...)
    }
    if j < lenPost{
        list = append(list,Post[j:]...)
    }
    return list
}

时间复杂度 O(nlogn)

空间复杂度 O(n)

Tips:

归并排序不是原地排序算法。

最后更新于