15、排序:选择类排序和归并排序
代码更新自上篇,如下:
1 package ren.laughing.datastructure.algorithm; 2 3 import ren.laughing.datastructure.baseImpl.BinTreeNode; 4 5 /** 6 * 排序 7 * 8 * @author Laughing_Lz 9 * @time 2016年4月22日 10 */ 11 public class Sorter { 12 /** 13 * 直接插入排序(从小到大) 时间复杂度为O(n^2) 14 * 15 * @param arr 16 * 要排序的数组 17 * @param low 18 * 要排序的最低下标 19 * @param high 20 * 要排序的最高下标 21 * @return 排序后的数组 22 */ 23 public static void insertSort(int[] arr, int low, int high) { 24 for (int i = low + 1; i <= high; i++) { 25 int temp = arr[i];// 待插入元素 26 int j = i;// 记录待插入位置 27 for (; j > low && temp < arr[j - 1]; j--) {// 28 arr[j] = arr[j - 1];// 后移 29 } 30 arr[j] = temp;// 插入 31 } 32 printResult("直接插入排序:", arr); 33 } 34 35 /** 36 * 折半插入排序 时间复杂度仍为O(n^2) 37 * 38 * @param arr 39 * @param low 40 * @param high 41 * @return 42 */ 43 public static void binInsertSort(int[] arr, int low, int high) { 44 for (int i = low + 1; i <= high; i++) { 45 int temp = arr[i]; 46 int lo = low; 47 int hi = i - 1; 48 while (lo <= hi) {// 注意'=' ★ 49 int mid = (lo + hi) / 2; 50 if (temp < arr[mid]) { 51 hi = mid - 1;// 由此看出最终temp的位置在hi+1 52 } else { 53 lo = mid + 1;// hi+1等同于lo (此处应该没错吧?) 54 } 55 } 56 for (int j = i - 1; j > hi; j--) {// 后移 57 arr[j + 1] = arr[j]; 58 } 59 arr[hi + 1] = temp;// 插入 60 } 61 printResult("折半插入排序:", arr); 62 } 63 64 /** 65 * 希尔排序(缩小增量排序) 当n在某个范围内,时间复杂度可达到O(n^1.3) 66 * 67 * @param arr 68 * @param low 69 * @param high 70 * @param delta 71 * 步长序列 72 */ 73 public static void shellSort(int[] arr, int low, int high, int[] delta) { 74 for (int i = 0; i < delta.length; i++) {// 第一步:遍历步长序列 75 for (int m = low; m < low + delta[i]; m++) {// 第二步:循环起始点,保证每个被拆分的子序列都被直接排序 76 for (int j = m + delta[i]; j <= high; j += delta[i]) {// 对每个子序列直接排序 77 int temp = arr[j]; 78 int k = j; 79 for (; k > m && temp < arr[(k - delta[i])]; k -= delta[i]) { 80 arr[k] = arr[(k - delta[i])];// 后移 81 } 82 arr[k] = temp;// 插入 83 } 84 } 85 } 86 printResult("希尔排序:", arr); 87 } 88 89 /** 90 * 冒泡排序 时间复杂度为O(n^2) 91 * 92 * @param arr 93 * @param low 94 * @param high 95 */ 96 public static void bubbleSort(int[] arr, int low, int high) { 97 int len = high - low + 1; 98 for (int i = 1; i < len; i++) { 99 for (int j = low; j <= high - i; j++) {// 这里j<= high-i ★ 100 if (arr[j] > arr[j + 1]) { 101 int temp = arr[j];// 交换 102 arr[j] = arr[j + 1]; 103 arr[j + 1] = temp; 104 } 105 } 106 } 107 printResult("冒泡排序:", arr); 108 } 109 110 /** 111 * 快速排序 需要递归 112 * 113 * @param arr 114 * @param low 115 * @param high 116 */ 117 public static void quickSort(int[] arr, int low, int high) { 118 if (low < high) { 119 int pa = partition(arr, low, high);// 分治 120 quickSort(arr, low, pa - 1); 121 quickSort(arr, pa + 1, high); 122 } 123 } 124 125 /** 126 * 将序列划分为两个子序列并返回枢轴元素的位置 127 * 128 * @param arr 129 * @param low 130 * 划分区间 131 * @param high 132 * @return 133 */ 134 private static int partition(int[] arr, int low, int high) { 135 int pivot = arr[low];// 首先定义枢轴为low所指元素 136 while (low < high) {// 交替扫描 137 while (arr[high] > pivot && low < high) { 138 high--; 139 } 140 arr[low] = arr[high];// 将比 pivot 小的元素移向低端 141 while (arr[low] < pivot && low < high) { 142 low++; 143 } 144 arr[high] = arr[low];// 将比 pivot 大的元素移向高端 145 } 146 arr[low] = pivot;// 设置枢轴 147 return low; 148 } 149 150 /** 151 * 简单选择排序 时间复杂度为O(n^2) 152 * 153 * @param arr 154 * @param low 155 * @param high 156 */ 157 public static void selectSort(int[] arr, int low, int high) { 158 for (int i = low; i < high - 1; i++) { 159 int min = i;// 记录位置,不是元素是位置!★ 160 for (int j = i; j <= high; j++) { 161 if (arr[j] < arr[min]) { 162 min = j;// 更新最小元素位置 163 } 164 } 165 if (min != i) {// 交换 166 int temp = arr[min]; 167 arr[min] = arr[i]; 168 arr[i] = temp; 169 } 170 } 171 printResult("简单选择排序:", arr); 172 } 173 174 /** 175 * 堆排序,时间复杂度为O(nlogn) 堆排序中,arr[]数组的首位arr[0]弃用。所以这里arr[1]为根结点 176 * 177 * @param arr 178 */ 179 public static void heapSelectSort(int[] arr) { 180 int n = arr.length - 1; 181 for (int i = n / 2; i >= 1; i--) {// 遍历叶子结点的上层第一个结点及之前所有结点,i>=1是因为数组中首位元素弃用 182 heapAdjust(arr, i, n);// 初始化建堆,i=n/2位置 183 } 184 for (int i = n; i > 1; i--) {// 185 int temp = arr[1];// 堆顶元素(根节点)为最大元素,将它和堆底元素交换 186 arr[1] = arr[i]; 187 arr[i] = temp; 188 heapAdjust(arr, 1, i - 1);// 从根结点更新堆 189 } 190 printResult("堆排序:", arr); 191 } 192 193 /** 194 * 创建堆 195 * 196 * @param r 197 * 数组元素根据二叉树层次遍历顺序存储 198 * @param low 199 * 遍历是否比左右孩子大的起始结点 200 * @param high 201 * 数组长度 202 */ 203 private static void heapAdjust(int[] r, int low, int high) { 204 int temp = r[low];// r[low]为要与左右孩子比较的父结点,以保证其比左右孩子都大 205 for (int j = 2 * low; j <= high; j = j * 2) {// 需要循环保证该起始结点low以下的树都符合堆定义★ 206 if (j < high && r[j] < r[j + 1]) {// r[j]是r[low]的左孩子,r[j+1]是右孩子 207 j++; 208 } 209 if (temp > r[j]) {// 父结点与较大的孩子比较大小,若父结点小,交换 210 break; 211 } 212 r[low] = r[j];// 父结点存放的是三者中最大的元素 213 low = j; 214 } 215 r[low] = temp;// 将原父结点的元素存入孩子结点 216 } 217 218 /** 219 * 归并排序 220 * 221 * @param arr 222 * @param low 223 * @param high 224 */ 225 public static void mergeSort(int[] arr, int low, int high) { 226 if (low < high) {// 分治 227 mergeSort(arr, low, (low + high) / 2); 228 mergeSort(arr, (low + high) / 2 + 1, high); 229 merge(arr, low, (low + high) / 2, high); 230 } 231 } 232 233 /** 234 * 将两个有序区间[low,mid]和[mid+1,high]合并成一个有序区间 235 * 236 * @param arr 237 * @param low 238 * @param mid 239 * @param high 240 */ 241 private static void merge(int[] arr, int low, int mid, int high) { 242 int[] temp = new int[high - low + 1]; 243 int a = low; 244 int b = mid + 1; 245 int t = 0; 246 while (a <= mid && b <= high) {// 注意这个循环★ 247 if (arr[a] < arr[b]) { 248 temp[t++] = arr[a++]; 249 } else { 250 temp[t++] = arr[b++]; 251 } 252 } 253 while (a <= mid) { 254 temp[t++] = arr[a++]; 255 } 256 while (b <= high) { 257 temp[t++] = arr[b++]; 258 } 259 for (int i = 0; i < temp.length; i++) { 260 arr[low + i] = temp[i]; 261 } 262 } 263 264 /** 265 * 程序入口 266 * 267 * @param args 268 */ 269 public static void main(String[] args) { 270 int[] arr = new int[] { 2, 5, 7, 3, 4, 8, 1, 9, 6, 0 }; 271 int[] delta = new int[] { 5, 3, 1 }; 272 // insertSort(arr, 0, 9); 273 // binInsertSort(arr, 0, 9); 274 // shellSort(arr, 0, 9, delta); 275 // bubbleSort(arr, 0, 9); 276 // quickSort(arr, 0, 9); 277 // printResult("快速排序:", arr); 278 // selectSort(arr, 0, 9); 279 // heapSelectSort(arr); 280 mergeSort(arr, 0, 9); 281 printResult("归并排序:", arr); 282 } 283 284 /** 285 * 打印 286 * 287 * @param str 288 * @param arr 289 */ 290 public static void printResult(String str, int[] arr) { 291 System.out.print(str); 292 for (int i = 0; i < arr.length; i++) { 293 System.out.print(arr[i] + " "); 294 } 295 System.out.println(); 296 } 297 }
—————————————————————————————————————行走在人猿的并行线——Laughing_Lz