排序算法总结
排序算法总结
定义一个通用的交换
-
private void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
- 冒泡排序
private void bubbleSort(int[] nums) { for (int i = nums.length - 1; i >= 1; i--) { // 冒泡得到n-1个最大值 for (int j = 1; j <= i; j++) { if (nums[j-1]>nums[j]) swap(nums, j, j-1); // 交换得到较大值 } } }
- 选择排序
private void selectionSort(int[] nums) { for (int i = nums.length - 1; i > 0; i--) { int maxIndex = 0; // 最大元素的位置 for (int j = 0; j <= i; j++) { if (nums[maxIndex]<nums[j]) { maxIndex = j; } } swap(nums, maxIndex, i); // 把这个最大的元素移到最后 } }
- 插入排序
private void insertionSort(int[] nums) { for (int i = 1; i < nums.length; i++) { // 从第二个元素开始遍历 int j = i; while (j>0&&nums[j]<nums[j-1]) { // 将当前元素移动到合适的位置 swap(nums, j, j-1); j--; } } }
- 希尔排序
private void shellSor2(int[] nums) { int gap = nums.length >> 1; while (gap > 0) { for (int i = 0; i < gap; i++) { // 对每个子序列进行排序 for (int j = i+gap; j < nums.length; j+=gap) { // 插入排序的部分 int temp = j; while (temp > i && nums[temp] < nums[temp-gap]) { swap(nums, temp, temp-gap); temp -= gap; } } } gap >>= 1; } }
- 归并排序
private void mergeSort(int[] nums, int left, int right) { // 需要左右边界确定排序范围 if (left >= right) return; int mid = (left+right) / 2; mergeSort(nums, left, mid); // 先对左右子数组进行排序 mergeSort(nums, mid+1, right); int[] temp = new int[right-left+1]; // 临时数组存放合并结果 int i=left,j=mid+1; int cur = 0; while (i<=mid&&j<=right) { // 开始合并数组 if (nums[i]<=nums[j]) temp[cur] = nums[i++]; else temp[cur] = nums[j++]; cur++; } while (i<=mid) temp[cur++] = nums[i++]; while (j<=right) temp[cur++] = nums[j++]; for (int k = 0; k < temp.length; k++) { // 合并数组完成,拷贝到原来的数组中 nums[left+k] = temp[k]; } }
- 快速排序
- 第一种实现
private void quickSort(int[] nums, int left, int right) { if (left >= right) return; int lo = left+1; // 小于分界点元素的最右侧的指针 int hi = right; // 大于分界点元素的最左侧的指针 while (lo<=hi) { if (nums[lo]>nums[left]) { // 交换元素确保左侧指针指向元素小于分界点元素 swap(nums, lo, hi); hi--; } else { lo++; } } lo--; // 回到小于分界点元素数组的最右侧 swap(nums, left, lo); // 将分界点元素移到左侧数组最右侧 quickSort2(nums, left, lo-1); quickSort2(nums, lo+1, right); }
- 第二种实现
private void quickSort(int[] nums, int left, int right) { if (left>=right) return; int cur = left + 1; // 从左侧第二个元素开始 int lo = left; // 分界点为第一个元素 while (cur <= right) { if (nums[cur] <= nums[left]) { // 交换位置保证lo的左侧都是小于num[left] swap(nums, lo+1, cur); lo ++; } cur++; } swap(nums, left, lo); // 把分界点元素移动到新的分界位置 quickSort(nums, left, lo-1); quickSort(nums, lo+1, right); }
- 第一种实现
- 堆排序
private void heapSort(int[] nums) { heapify(nums); // 新建一个最大堆 for (int i = nums.length - 1; i >= 1; i--) { swap(nums, 0, i); // 弹出最大堆的堆顶放在最后 rebuildHeap(nums, 0,i-1); // 重建最大堆 } } private void heapify(int[] nums) { for (int i = 1; i < nums.length; i++) { int par = (i-1)>>1; // 找到父节点 int child = i; // 定义子节点 while (child>0&&nums[par]<nums[child]) { // 从子节点到根节点构建最大堆 swap(nums, par, child); child = par; par = (par-1) >> 1; } } } private void rebuildHeap(int[] nums, int par, int last) { int left = 2*par+1; // 左子节点 int right = 2*par+2; // 右子节点 int maxIndex = left; if (right<=last && nums[right]>nums[left]) { // 找到最大子节点 maxIndex = right; } if (left<=last && nums[par] < nums[maxIndex]) {// 和最大子节点比较 swap(nums, par, maxIndex); // 互换到最大子节点 rebuildHeap(nums, maxIndex, last); // 重建最大子节点代表的子树 } }
- 二叉搜索树排序
private int[] bstSort(int[] nums) { TreeNode root = new TreeNode(nums[0]); // 构建根节点 for (int i = 1; i < nums.length; i++) { // 将所有的元素插入到二叉搜索树中 buildTree(root, nums[i]); } inorderTraversal(root, nums, new int[1]);// 中序遍历获取二叉树中的所有节点 return nums; } private void inorderTraversal(TreeNode node, int[] nums, int[] pos) { if (node == null) return; inorderTraversal(node.left, nums, pos); nums[pos[0]++] = node.val; inorderTraversal(node.right, nums, pos); } private void buildTree(TreeNode node, int num) { if (node == null) return; if (num >= node.val) { // 插入到右子树中 if (node.right == null) { node.right = new TreeNode(num); } else { buildTree(node.right, num); } } else { // 插入到左子树中 if (node.left == null) { node.left = new TreeNode(num); } else { buildTree(node.left, num); } } } static class TreeNode { // 树节点的数据结构 int val; TreeNode left; TreeNode right; public TreeNode(int val) { this.val = val; } }
- 计数排序
private void countSort(int[] nums) { int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (int num : nums) { // 找到最大最小值 min = Math.min(min, num); max = Math.max(max, num); } int[] count = new int[max-min+1]; // 建立新数组 for (int num : nums) { // 统计每个元素出现频率 count[num-min]++; } int cur = 0; for (int i = 0; i < count.length; i++) { // 根据出现频率把计数数组中的元素放回到旧数组中 while (count[i]>0) { nums[cur++] = i+min; count[i]--; } } }
- 桶排序
private void bucketSort(int[] nums) { int INTERVAL = 100; // 定义桶的大小 int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (int num : nums) { // 找到数组元素的范围 min = Math.min(min, num); max = Math.max(max, num); } int count = (max - min + 1); // 计算出桶的数量 int bucketSize = (count % INTERVAL == 0) ?( count / INTERVAL) : (count / INTERVAL+1); List<Integer>[] buckets = new List[bucketSize]; for (int num : nums) { // 把所有元素放入对应的桶里面 int quotient = (num-min) / INTERVAL; if (buckets[quotient] == null) buckets[quotient] = new ArrayList<>(); buckets[quotient].add(num); } int cur = 0; for (List<Integer> bucket : buckets) { if (bucket != null) { bucket.sort(null); // 对每个桶进行排序 for (Integer integer : bucket) { // 还原桶里面的元素到原数组 nums[cur++] = integer; } } } }
- 基数排序
private void radixSort(int[] nums) { int max = -1; int min = 1; for (int num : nums) { // 计算最大最小值 max = Math.max(max, num); min = Math.min(min, num); } max = Math.max(max, -min); // 求得绝对值最大的值 int digits = 0; while (max > 0) { // 计算绝对值最大的值的位数 max /= 10; digits++; } List<Integer>[] buckets = new List[19]; // 建一个包含所有位数的数组 for (int i = 0; i < buckets.length; i++) { buckets[i] = new ArrayList<>(); } int pos; int cur; for (int i = 0, mod = 1; i < digits; i++, mod*=10) { // 对十进制每一位进行基数排序 for (int num : nums) { // 扫描数组将值放入对应的桶 pos = (num / mod) % 10; buckets[pos+9].add(num); } cur = 0; for (List<Integer> bucket : buckets) { // 将桶内元素放回到数组里面 if (bucket!=null) { for (Integer integer : bucket) { nums[cur++] = integer; } bucket.clear(); // 将桶清空 } } } }
- 总结
排序算法 | 最好情况 | 平均情况 | 最差情况 | 空间复杂度 | 稳定性 |
冒泡排序 | n2 | n2 | 1 | √ | |
选择排序 | n2 | n2 | 1 | ||
插入排序 | n | n2 | 1 | √ | |
希尔排序 | nlogn | n4/3 | 1 | ||
二叉树排序 | nlogn | nlogn | n | √ | |
归并排序 | nlogn | nlogn | n | √ | |
快速排序 | nlogn | nlogn | logn | ||
堆排序 | nlogn | nlogn | 1 | ||
计数排序 | n+r | n+r | √ | ||
桶排序 | n+r | n+r | √ | ||
基数排序 | √ |