1. 程式人生 > 其它 >11.動態規劃:戳氣球

11.動態規劃:戳氣球

排序演算法總結

定義一個通用的交換

  •   
    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
基數排序