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  }