2.认识O(NlogN)的排序 | 左程云算法笔记

155 阅读1分钟

归并排序

class Solution {
//    归并排序
    public void mergeSort(int[] array) {
      if (array == null || array.length < 2) return ;
      mergeSort(array,0,array.length - 1);

    }

    public void mergeSort(int[] array,int left,int right){
//        其中一段只有一个元素没有必要再分再比较,直接返回
        if (right == left) return;
        int mid = left + ((right - left) >> 1);
//        分别递归排序左半段和右半段,再合并在一起
        mergeSort(array,left,mid);
        mergeSort(array,mid + 1,right);
        merge(array,left,mid,right);
    }

    public void merge(int[] array,int left,int mid,int right){
//        开辟一个辅助数组储存排序好的数字
        int[] help = new int[right - left + 1];
//        设置两个指针分别指向左右半段的起点,i指针指向help的起点方便往help里储存数字
        int p1 = left;
        int p2 = mid + 1;
        int i = 0;
        while (p1 <= mid && p2 <= right){
            help[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
        }
        while (p1 <= mid){
            help[i++] = array[p1++];
        }

        while (p2 <= right){
            help[i++] = array[p2++];
        }
//        最后再把help排序好的数字再挪回array中
        for (int j : help) {
            array[left++] = j;
        }
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = new int[]{1,3,4,2,5};
        solution.mergeSort(nums);
        System.out.println(nums);
    }

}

小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

例子:[1,3,4,2,5] 1左边比1小的数,没有;

3左边比3小的数,1;

4左边比4小的数,1、3;

2左边比2小的数,1;

5左边比5小的数,1、3、4、2;

所以小和为1+1+3+1+1+3+4+2=16

class Solution {
    public int mergeSort(int[] array) {
      if (array == null || array.length < 2) return 0;
      return mergeSort(array,0,array.length - 1);

    }

    public int mergeSort(int[] array,int left,int right){
//        其中一段只有一个元素没有必要再分再比较,直接返回
        if (right == left) return 0;
        int mid = left + ((right - left) >> 1);
//        分别递归排序左半段和右半段,再合并在一起
        int leftSum = mergeSort(array,left,mid);
        int rightSum = mergeSort(array,mid + 1,right);
        return leftSum + rightSum + merge(array,left,mid,right);
    }

    public int merge(int[] array,int left,int mid,int right){
//        开辟一个辅助数组储存排序好的数字
        int[] help = new int[right - left + 1];
//        设置两个指针分别指向左右半段的起点,i指针指向help的起点方便往help里储存数字
        int p1 = left;
        int p2 = mid + 1;
        int i = 0;
        int result = 0;
        while (p1 <= mid && p2 <= right){
            result += array[p1] < array[p2] ? (right - p2 + 1) * array[p1] : 0;
            help[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
        }
        while (p1 <= mid){
            help[i++] = array[p1++];
        }

        while (p2 <= right){
            help[i++] = array[p2++];
        }
//        最后再把help排序好的数字再挪回array中
        for (int j : help) {
            array[left++] = j;
        }
        return result;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = new int[]{1,3,4,2,5};
        System.out.println(solution.mergeSort(nums));
    }

}

逆序队问题

在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有逆序对。

同上

荷兰国旗问题

给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)

返回值含义: 一定会返回一个长度为2的数组,等于区域的左边界和右边界(也就是相等区域的边界范围 )

class Solution {
    
    public int[] partition(int[] arr, int left, int right, int num) {
//        less为小于num区的边界,more为大于num的边界,left当做遍历数组的指针
         int less = left - 1;
         int more = right + 1;
         while (left < more){
             if (arr[left] < num){
                 swap(arr,left++,++less);
             }else if (arr[left] > num){
                 swap(arr,--more,left);
             }else {
                 left++;
             }
         }

         return new int[]{less + 1,more - 1};
    }

    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = new int[]{3,5,0,3,4,5,2,6,9,6,1};
        solution.partition(nums,0,nums.length - 1,5);
        System.out.println(solution.partition(nums,0,nums.length - 1,5));
    }

}

快速排序

class Solution {
    public  void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    public void quickSort(int[] arr, int l, int r) {
        if (l < r) {
            int[] p = partition(arr, l, r);
            quickSort(arr, l, p[0]);
            quickSort(arr, p[1], r);
        }
    }


    public int[] partition(int[] arr, int left, int right) {
//        less为小于num区的边界,more为大于num的边界,left当做遍历数组的指针
        int less = left - 1;
        int more = right + 1;
        while (left < more){
            if (arr[left] < arr[right]){
                swap(arr,left++,++less);
            }else if (arr[left] > arr[right]){
                swap(arr,--more,left);
            }else {
                left++;
            }
        }

        return new int[]{less,more};
    }

    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = new int[]{3,5,0,3,4,5,2,6,9,6,1};
        solution.quickSort(nums);
        System.out.println(nums.toString());
    }
}