排序算法(上)

1.插入排序

func insertSort(arr []int) {
    
    
   // 将数组分为已经排序好的区间[0, bound), 和未排序好的区间[bound,length]
   for i := 0; i < len(arr); i++ {
    
    
   	end := i-1
   	cur := arr[i]
   	// 如果遇到了比cur大的元素就需要往后搬
   	for end >= 0 && arr[end] > cur  {
    
    
   		arr[end+1] = arr[end]
   		end--
   	}
   	arr[end+1] = cur
   }
}

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

2. 希尔排序

希尔排序就是将插入排序多分了几个组, 对每个组都使用插入排序
常常使用的gap = len(arr)/2, 之后再依次除以2

func insertSortWithGap(arr []int, gap int) {
    
    
  for i := 0; i < len(arr); i++ {
    
    
  	end := i-gap
  	cur := arr[i]
  	for end >= 0 && arr[end] > cur {
    
    
  		arr[end+gap] = arr[end]
  		end -= gap
  	}
  	arr[end+gap] = cur
  }
}

func shellSort(arr []int) {
    
    
  // 如果会插入排序那么希尔排序也就会, 希尔排序就是将插入排序分堆利用gap来分组块
  // 常用的gap就是长度依次除2
  gap := len(arr)/2
  for gap > 0 {
    
    
  	insertSortWithGap(arr, gap)
  	gap  /= 2
  }
}

3. 选择排序

将当前下标先看做最小值, 然后依次从[i, len(arr)-1]的位置找到一个最小值的下标

func selectSort(arr []int) {
    
    
   if arr == nil || len(arr) < 2 {
    
    
   	return
   }
   // 每次定义一个position, 用来找到后面的数组中的最小值
   for i := 0; i < len(arr)-1; i++ {
    
    
   	// 以本次的i为下标先当做最小的元素
   	position := i
   	for j := position+1; j < len(arr); j++ {
    
    
   		if arr[j] < arr[position] {
    
    
   			position = j
   		}
   	}
   	arr[i], arr[position] = arr[position], arr[i]
   }
}

O(n^2)
O(1) 常数级变量
还有一种可以改进的选择排序思想, 就是在找最小值的下标时, 同时找到最大值的下标与最后的元素交换, 这样就可以减少循环的次数

4 .堆排序

堆排和快排是两个最重要的排序也是面试中考的最多的, 一定要动手实现几次,
想要练习堆排, 建议可以去做力扣215题

func heapSort(arr []int) {
    
    
   size := len(arr)
   createHeap(arr, size)
   for i := len(arr)-1 ; i >= 0; i-- {
    
    
   	arr[0], arr[i] = arr[i], arr[0]
   	size--
   	shiftDown(arr, 0, size)
   }
}

func createHeap(arr []int, size int) {
    
    
   // 堆排序的核心思想就是, 先建立一个大跟堆, 再依次将数组第一个元素和最后一个元素交换, 直至堆的大小0
   // 从第一个非叶子结点开始建堆
   root := (len(arr)-1-1) >> 1 // size == len(arr) size-1是最后一个元素的下标, 那么他的父亲结点就是 parent = (size-1-1)/2
   for ; root >= 0; root-- {
    
    
   	// 依次按照每一个节点向下调整
   	shiftDown(arr, root, size)
   }
}

func shiftDown(arr []int, parent int, size int) {
    
    
   // 首先找到左孩子
   child := parent*2 + 1
   for child < size {
    
    
   	// 一直向下调整
   	// 判断左子树和右子数哪个大
   	if child+1 < size && arr[child] < arr[child+1] {
    
    
   		child += 1
   	}
   	// 判断父亲的值和孩子的值哪个大, 大了的话 就需要交换
   	if arr[parent] < arr[child] {
    
    
   		arr[parent], arr[child] = arr[child], arr[parent]
   	}else {
    
    
   		// 不需要交换直接break
   		break
   	}
   	parent = child
   	child = 2*parent +1
   }
}

时间复杂度O(n*logn)
空间O(1)(忽略递归使用的栈空间)

Guess you like

Origin blog.csdn.net/m0_53804791/article/details/121281152