github.com/chenjiandongx/go-queue@v0.0.0-20191023082232-e2a36f382f84/sort.go (about) 1 package collections 2 3 // 冒泡排序:稳定 4 // 平均 O(n^2) 最好 O(n) 最坏 O(n^2) 5 // https://upload.wikimedia.org/wikipedia/commons/3/37/Bubble_sort_animation.gif 6 func BubbleSort(items []int) { 7 if len(items) < 2 { 8 return 9 } 10 n := len(items) 11 index := n 12 for index > 0 { 13 n = index 14 index = 0 15 for i := 1; i < n; i++ { 16 if items[i-1] > items[i] { 17 items[i-1], items[i] = items[i], items[i-1] 18 // 表示 i 后面的数都已经排序好了 19 // 如 3 2 4 5 6 7 8 9,在 3 和 2 交换之后 无需和后面进行交换 20 // 就代表着 3 之后的数都是有序的,那后面的数就不用排了 21 // 如果数组是基本有序的情况 性能会好很多 22 index = i 23 } 24 } 25 } 26 } 27 28 // 直接插入排序:稳定 29 // 平均 O(n^2) 最好 O(n) 最差 O(n^2) 30 // https://upload.wikimedia.org/wikipedia/commons/2/25/Insertion_sort_animation.gif 31 func InsertionSort(items []int) { 32 length := len(items) 33 if length < 2 { 34 return 35 } 36 37 for i := 1; i < length; i++ { 38 // 如果右边的数小于左边的数 表示这个数的需要移动到左边 39 if items[i] < items[i-1] { 40 x := items[i] 41 var j int 42 // i-1 是原来的有序区 需要将 i 放入到原来的有序区中 形成一个更大的有序区 43 // 2 3 4 5 1 6 44 // j j+1 => j+1 = j 45 // 2 3 4 1 5 6 46 // j j+1 => j+1 = j 47 // ... => j == -1 48 // 1 2 3 4 5 6 => items[j+1] = x 49 for j = i - 1; j >= 0 && items[j] > x; j-- { 50 // 将比 x 大的数玩后面移动一位 51 items[j+1] = items[j] 52 } 53 // 将 x 放入到正确的位置中 因为前面有 -- 操作,所以这里是 j+1 54 items[j+1] = x 55 } 56 } 57 } 58 59 // 快速排序:不稳定 60 // 平均 O(nlogn) 最好 O(nlogn) 最坏 O(n^2) 61 // https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif 62 func QuickSort(items []int) { 63 if len(items) < 2 { 64 return 65 } 66 quickSort(items, 0, len(items)-1) 67 } 68 69 // 递归执行快速排序 70 func quickSort(items []int, l, r int) { 71 // 当左右相遇的时候 递归出口 72 if l < r { 73 i, j := l, r 74 // 轴点 用于划分左右两部分 75 pivot := items[i] 76 // 左右相遇 完成一次划分排序操作 77 for i < j { 78 // 从右往左找 找到第一个小于 轴点 的数 79 for i < j && items[j] >= pivot { 80 j-- 81 } 82 // 如果找到的话 `填坑` 83 if i < j { 84 items[i] = items[j] 85 i++ 86 } 87 // 从左往右找 找到第一个大于 轴点 的数 88 for i < j && items[i] < pivot { 89 i++ 90 } 91 // 如果找到的话 `填坑` 92 if i < j { 93 items[j] = items[i] 94 j-- 95 } 96 } 97 items[i] = pivot // 将轴点放到左右两边分界处 98 quickSort(items, l, i-1) // 递归排序左半部分 99 quickSort(items, i+1, r) // 递归排序由半部分 100 } 101 } 102 103 // 希尔排序 不稳定 104 // 平均 O(nlogn) 最好 O(n) 最坏 O(n^2) 105 // https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif 106 func ShellSort(items []int) { 107 length := len(items) 108 if length < 2 { 109 return 110 } 111 112 // 增量为 gap,每次更新为 gap /= 2 113 for gap := length / 2; gap > 0; gap /= 2 { 114 // 假设数组为 49, 38, 65, 55, 26, 13, 27, 49, 97, 4 长度为 10 115 // 第一轮的时候 gap=5 分为 116 // 13 49 - 27 38 - 49 65 - 97 55 - 4 26 117 // 总共需要 5 次直接插入排序 排序完后的顺序 118 // 13 49 - 27 38 - 49 65 - 55 97 - 4 26 119 120 // 第二轮的时候 gap=2 分为 121 // 13 27 49 55 4 - 49 38 65 97 26 122 // 总共需要 2 次直接插入排序 排序完后的顺序 123 // 4 13 27 49 55 - 26 38 49 65 97 124 125 // 第三轮的时候 gap=1 分为 126 // 4 13 27 49 55 26 38 49 65 97 127 // 总共需要 1 次直接插入排序 排序完后的顺序 128 // 4 13 26 27 38 49 49 55 65 97 129 for j := gap; j < length; j++ { 130 if items[j] < items[j-gap] { 131 x := items[j] 132 k := j - gap 133 // items[k] 为`原先在左边的数` 可理解为原来的有序区 134 // x 为即将放入到有序区的值,x < items[k],所以要把它往左边放 135 for k >= 0 && items[k] > x { 136 // 把 x 往左边放就意味着要把原先右边的值右移 137 items[k+gap] = items[k] 138 k -= gap 139 } 140 // 将 x 放入到正确的位置 141 items[k+gap] = x 142 } 143 } 144 } 145 } 146 147 // 堆排序:不稳定 148 // 平均 O(nlogn) 最好 O(nlogn) 最坏 O(nlogn) 149 // https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif 150 func HeapSort(items []int) { 151 length := len(items) 152 if length < 2 { 153 return 154 } 155 156 // 初始化大顶堆 从最后一个父节点开始往前调整 157 for i := length/2 - 1; i >= 0; i-- { 158 adjustHeap(items, i, length-1) 159 } 160 161 // 进行堆排序 将最后一个叶子节点与根节点交换 162 // 调整后的最后的的叶子节点即为最大值 适合求最大的 k 个数 163 // 如 i > 0 => i > 3 则可以只算出最大的 3 个数 164 for i := length - 1; i > 0; i-- { 165 items[0], items[i] = items[i], items[0] 166 adjustHeap(items, 0, i-1) 167 } 168 } 169 170 // 调整堆 使其保持堆结构 171 func adjustHeap(items []int, start, end int) { 172 dad := start // 父节点 173 son := dad*2 + 1 // 左子节点 174 for son <= end { 175 // son+1 为右子节点 如果右节点 > 左节点 son++ 取子节点中较大的一个 176 if son+1 <= end && items[son] < items[son+1] { 177 son++ 178 } 179 // 如果符合大顶堆结构 直接跳出 180 if items[dad] > items[son] { 181 return 182 } 183 // 讲父节点与子节点中较大的节点进行交换 184 items[dad], items[son] = items[son], items[dad] 185 // 在此修正子节点堆结构 186 dad = son 187 son = dad*2 + 1 188 } 189 } 190 191 // 归并排序:稳定 192 // 平均 O(nlogn) 最好 O(nlogn) 最差 O(nlogn) 193 // https://upload.wikimedia.org/wikipedia/commons/c/c5/Merge_sort_animation2.gif 194 func MergeSort(items []int) { 195 length := len(items) 196 if length < 2 { 197 return 198 } 199 res := make([]int, length) 200 mergeSort(items, 0, length-1, res) 201 } 202 203 // 递归执行归并排序及合并 204 func mergeSort(items []int, first, last int, res []int) { 205 // 递归出口 206 if first < last { 207 mid := (first + last) / 2 // 计算出切分左右部分的边界点 208 mergeSort(items, first, mid, res) // 递归排序排序左半部分 209 mergeSort(items, mid+1, last, res) // 递归排序排序右半部分 210 mergeArray(items, first, mid, last, res) // 合并左右数组 211 } 212 } 213 214 // 将两个有序数组按序合并为一个 215 func mergeArray(items []int, first, mid, last int, res []int) { 216 i, j := first, mid+1 217 leftLen, rightLen := mid, last 218 219 var k int 220 221 // 左半部分 first mid 222 // 右半部分 mid+1 last 223 for i <= leftLen && j <= rightLen { 224 // 如果左边的值大 则将左边的值放入到 res 中 225 if items[i] <= items[j] { 226 res[k] = items[i] 227 k++ 228 i++ 229 // 如果右边的值大 则将右边的值放入到 res 中 230 } else { 231 res[k] = items[j] 232 k++ 233 j++ 234 } 235 } 236 237 // 右边数组遍历结束了 将左边剩下的值放入 res 中 238 for i <= leftLen { 239 res[k] = items[i] 240 k++ 241 i++ 242 } 243 244 // 左边数组遍历结束了 将右边剩下的值放入 res 中 245 for j <= rightLen { 246 res[k] = items[j] 247 k++ 248 j++ 249 } 250 251 // 这里是重点 将合并后的数据放到`原 items`中 252 // 也就说放入后从 first+i 到 k(leftLen+rightLen)中是有序的 253 for i := 0; i < k; i++ { 254 items[first+i] = res[i] 255 } 256 }