11.動態規劃:戳氣球
阿新 • • 發佈:2021-07-16
排序演算法總結
定義一個通用的交換
-
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++) {
- 選擇排序
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
- 插入排序
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 | √ | ||
基數排序 | √ |