leetcode912排序数组

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 104
  • -5 * 104 <= nums[i] <= 5 * 104

归并排序(重点)

  • 基本思路:借助额外空间,合并两个有序数组,得到更长的有序数组。例如:「力扣」第 88 题:合并两个有序数组。
  • 算法思想:分而治之(分治思想)。「分而治之」思想的形象理解是「曹冲称象」、MapReduce,在一定情况下可以并行化。

个人建议:「归并排序」是理解「递归思想」的非常好的学习材料,大家可以通过理解:递归完成以后,合并两个有序数组的这一步骤,想清楚程序的执行流程。即「递归函数执行完成以后,我们还可以做点事情」。因此,「归并排序」我个人觉得非常重要,一定要掌握。

class Solution {
    //归并排序
    public int[] sortArray(int[] nums) {
       return mergeSort(nums, 0, nums.length-1);
       
    }
    public int[] mergeSort(int[] nums, int left, int right){
        //递归退出条件
        //如果左指针大于右指针,就退出循环
        //经过左右拆分,数组元素形成单个元素的树
        if(left >=right){
            return nums;
        }
        //数组中的中位数
        int mid = (right+left)/2;
        //数组左拆分
        mergeSort(nums, left, mid);
        //数组右拆分
        mergeSort(nums, mid+1, right);
        //数组合并,将单个元素进行合并
        return merge(nums, left, mid, right);
    }
    public int[] merge(int[] nums, int left, int mid, int right){
        //定义一个临时数组,存储排序好的元素
        int[] temp = new int[right-left+1];
        //左排序的元素数组的左指针
        int i = left;
        //右排序的元素数组的左指针
        int j = mid+1;
        //定义一个指向临时数组的左指针
        int t = 0;
        //循环判断条件
        //左数组到mid,右数组到right
        //左右数组都有元素的时候,进行比较
        while(i<=mid&&j<=right){
            //取左右数组中较小的元素,填入临时数组中
            //并将较小元素所在数组的左指针和临时数组的左指针都一起右移
            if(nums[i]<=nums[j]){
                temp[t++] = nums[i++];
            }else{
                temp[t++] = nums[j++];
            }
        }
        //当左右数组其中一个数组没有元素的时候
        //如果左数组中还有剩余元素,就将剩余元素全部加入到临时数组中
        while(i<=mid){
            temp[t++]=nums[i++];
        }
        //如果有数组中还有元素,就将剩余元素全部加入到临时数组中
        while(j<=right){
            temp[t++] = nums[j++];
        }
        //将临时数组的元素复制到原数组中去
        for(int k = 0; k<temp.length;k++){
            //特别注意这便nums数组起始位置要从 k+left开始 
            //原因在加右数组的时候,起始位置要加left
            //这里不理解,直接把它记住。
            nums[left+k]=temp[k];
        }
        //返回原数组
        return nums;
    }
}
  • 优化 1:在「小区间」里转向使用「插入排序」,Java 源码里面也有类似这种操作,「小区间」的长度是个超参数,需要测试决定,我这里参考了 JDK 源码;
  • 优化 2: 在「两个数组」本身就是有序的情况下,无需合并;
  • 优化 3:全程使用一份临时数组进行「合并两个有序数组」的操作,避免创建临时数组和销毁的消耗,避免计算下标偏移量。
    注意:实现归并排序的时候,要特别注意,不要把这个算法实现成非稳定排序,区别就在 <= 和 < ,已在代码中注明。
    「归并排序」比「快速排序」好的一点是,它借助了额外空间,可以实现「稳定排序」,Java 里对于「对象数组」的排序任务,就是使用归并排序(的升级版 TimSort,在这里就不多做介绍)。

    快速排序

堆排序


Cherry
1 声望0 粉丝

一名自然语言处理方向的人工智能研究生!