912. 排序数组

发布于:2022-12-23 ⋅ 阅读:(169) ⋅ 点赞:(0)

题目描述:

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

Level AC rate
Medium 55.6%


题目解析(冒泡排序):

从第一个数开始,逐个与后面的数进行比较,若该数大于后面的数则和后面的数进行位置交换,这样就可以确保在第一次循环后,位于数组最后的是最大的数,同理,然后在len-1的子数组上重复操作,将第二大的数放到倒数第二的位置上,一直这样下去,直到只剩两个数进行交换判断,即可完成整个数组的排序,代码如下:


// 冒泡排序 O(N^ 2) O(1)
class Solution {
    public int[] sortArray(int[] nums) {
        int len = nums.length;
        for(int i = 0 ; i<len-1 ; i++){
            for(int j = 0; j<len-i-1 ; j++){
                if(nums[j]>=nums[j+1]){
                    nums[j] ^= nums[j+1];
                    nums[j+1] ^= nums[j];
                    nums[j] ^= nums[j+1];
                }
            }
        }
        return nums;
    }
}

时间复杂度为O(N^2),空间复杂度为O(1),导致程序运行时间超时。

题目解析(选择排序):

从未排序的数中选出最小的数,将其放置到已经排好序的数列后,重复操作即可实现数列的升序排列,代码如下:

// 选择排序  O(N^ 2) O(1)
class Solution {
    public int[] sortArray(int[] nums) {
        int len = nums.length;
        for(int i = 0 ; i<len ; i++){
            int pos = i;
            for(int j = i ; j<len ; j++){
                if(nums[pos]>nums[j])pos = j;
            }
            if(pos!=i){
                nums[i] ^= nums[pos];
                nums[pos] ^= nums[i];
                nums[i] ^= nums[pos];
            }
        }
        return nums;
    }
}

时间复杂度为O(N^2),空间复杂度为O(1),导致程序运行时间超时。

题目解析(插入排序):

插入排序类似于打麻将,我们只需要遍历数组,该目前的数按大小插入到该数前面的子数组中即可,怎么按大小插入呢,while循环往回看,直到找到第一个数小于目前的数,那么将该数放这个数后即可,若找完了仍然没有找到,则将该数放着整个数组的开头位置,代码如下:

// 插入排序 打麻将 O(N^ 2) O(1)
class Solution {
    public int[] sortArray(int[] nums) {
        int len = nums.length;
        for(int i = 1 ; i<len ; i++){
            int done = i-1;
            int cur = -1;
            for(int j = 0; j<=done; j++){
                if(nums[j]>nums[i]){
                    cur = j;
                    break;
                }
            }
            if(cur!=-1){
                int temp = nums[i];
                for(int j = done; j>=cur; j--){
                    nums[j+1] = nums[j];
                }
                nums[cur] = temp;
            }
        }
        return nums;
    }
}

时间复杂度为O(N^2),空间复杂度为O(1),导致程序运行时间超时。

题目解析(快速排序):

快速排序涉及到了对于基准数的选择,通常情况下,我们一般选择数字开头的数字作为基准数,那么选择了基准数过后应该怎么进行排序呢,我用文字的形式大概说明一下。比如数组 [12,49,23,11,47,89,63]。选取基准数为47,首先我们需要将47与数组的末位数字进行交换得到[12,49,23,11,63,89,47],将指针pos指向-1的位置,将index指向0的位置,开始的时候判断index指向的数是否小于等于我们选择的基准数,如果是,那么pos++, 判断i是否大于pos,若大于则交换pos和i指向的两个数,这里明显不成立,则i++进入下一个循环;然后pos=0 i=1,再进行判断,i++;i=2此时指向的数字23小于基准数47,则将pos++, pos=1, 交换两个数,得到数组[12,23,49,11,63,89,47],计算到最后得到数组[12,23,11,47,49,63,89],然后以同样的方式处理基准数左边的子数组和右边的子数组,直到,子数组的长度只有1为止,即可实现数组排序,代码如下:

// 快速排序 O(NLogN) O(logN)
class Solution {
    public int[] sortArray(int[] nums) {
        return sort(nums,0,nums.length-1);
    }


    int[] sort(int[] nums, int sta, int end){
        if(nums.length<1||sta<0||end>=nums.length||sta>end)return null;
        int pos = part(nums,sta,end);
        if(pos>sta)sort(nums,sta,pos-1);
        if(pos<end)sort(nums,pos+1,end);
        return nums;
    }


    int part(int[] nums, int sta, int end){
        if(sta==end)return sta;
        int index = sta;
        int temp = nums[sta];
        nums[index] ^= nums[end];
        nums[end] ^= nums[index];
        nums[index] ^= nums[end];
        int pos = sta-1;
        for(int i = sta; i<=end ; i++){
            if(nums[i]<=temp){
                pos++;
                if(i>pos){
                    nums[pos] ^= nums[i];
                    nums[i] ^= nums[pos];
                    nums[pos] ^= nums[i];
                }
            }
        }
        return pos;
    }
}

时间复杂度为O(NLogN),空间复杂度为O(LogN),导致程序运行时间超时,一般情况下,在子数组的长度较小时,比如小于15时,可换做插入排序来提高程序的运行效率。

题目解析(希尔排序):

类似于插入排序,只不过将其按gap分为了很多个组,通常来说,我们取gap初值为数组长度的一半,在每次循环后,将其赋值为当前值的1/2即可,直到gap=0。从1开始增长,然后以gap为间距判断与之前的子数组的大小关系,起始原理和插入排序一样,只不过引入了gap数值,代码如下:

// 希尔排序 O(NLogN)~O(N^ 2) O(1)
class Solution {
    public int[] sortArray(int[] nums) {
        int len = nums.length;
        int gap = len/2;
        while(gap>0){
            for(int i = gap ; i<len ; i++){
                int done = i-gap;
                int cur = nums[i];
                while(done>=0&&nums[done]>cur){
                    nums[done+gap] = nums[done];
                    done-=gap;
                }
                nums[done+gap] = cur;
            }
            gap/=2;
        }
        return nums;
    }
}

时间复杂度为O(NLogN)~O(N^2),空间复杂度为O(1)。

执行用时:26 ms, 在所有 Java 提交中击败了38.35%的用户

内存消耗:51.1 MB, 在所有 Java 提交中击败了40.69%的用户

题目解析(归并排序):

将数组从中间分为两个数组,假设两个数组已经按从小到大的序列排好了,只需要使用一个双指针,将两个数组合并就可。那么怎么将这两个子数组排序呢,只需要将子数组继续再细分即可,直到数组内只有一个值,使用递归进行计算即可,代码如下:


// 归并排序 O(NLogN) O(N)
class Solution {
    public int[] sortArray(int[] nums) {
        if(nums.length<2)return nums;
        int mid = nums.length/2;
        int[] left = Arrays.copyOfRange(nums,0,mid);
        int[] right = Arrays.copyOfRange(nums,mid,nums.length);
        return merge(sortArray(left),sortArray(right));
    }


    int[] merge(int[] left, int[] right){
        int l = 0, r = 0,pos = 0;
        int[] ans = new int[left.length+right.length];
        while(pos<ans.length){
            if(l==left.length){
                ans[pos++] = right[r++];
                continue;
            }
            if(r==right.length){
                ans[pos++] = left[l++];
                continue;
            }
            if(left[l]<=right[r]){
                ans[pos++] = left[l++];
            }
            else{
                ans[pos++] = right[r++];
            }
        }
        return ans;
    }
}

时间复杂度为O(NLogN),空间复杂度为O(N)。

执行用时:29 ms, 在所有 Java 提交中击败了37.14%的用户

内存消耗:51.4 MB, 在所有 Java 提交中击败了32.90%的用户

题目解析(堆排序):

将一个数组排列为二叉堆来进行排序,首先我们需要使用一个函数来将一个数组生成二叉堆,根据完全二叉树的概念,数组长度除2减1对应的数值为最后一个不是叶子结点的结点,然后根据完全二叉树的概念,这个数之前的数同样符合,依次对其进行调整,怎么调整呢,只需要判断该值的左结点的数是不是大于该数,则将index指向左,然后判断右子结点,如果右子结点的数大于根结点和左子结点那么index指向右,然后将根结点同index交换即可,然后再将index指向结点作为根结点进行调整,怎么排序呢,只要每次将根结点取出(最大值),然后将最后一个结点(最小值)放置于根结点,再对根结点进行调整即可,直到树的长度为0,代码如下:

// 堆排序 O(NLogN) O(1)
class Solution {
    int len;
    public int[] sortArray(int[] nums) {
        len = nums.length;
        if(len<=1)return nums;
        buildheap(nums);
        while(len>1){
            nums[0] ^= nums[len-1];
            nums[len-1] ^= nums[0];
            nums[0] ^= nums[len-1];
            len--;
            adjust(nums,0);
        }
        return nums;
    }


    void buildheap(int[] nums){
        int cur = len/2-1;
        for(int i = cur; i>=0 ; i--){
            adjust(nums,i);
        }
    }


    void adjust(int[] nums, int pos){
        int l = pos*2+1, r = (pos+1)*2;
        int index = pos;
        if(l<len&&nums[l]>nums[pos])index = l;
        if(r<len&&nums[r]>nums[pos]&&nums[r]>nums[l])index = r;
        if(index!=pos){
            nums[pos] ^= nums[index];
            nums[index] ^= nums[pos];
            nums[pos] ^= nums[index];
            adjust(nums,index);
        }
    }
}

时间复杂度为O(NLogN),空间复杂度为O(1)。

执行用时:29 ms, 在所有 Java 提交中击败了37.14%的用户

内存消耗:51.4 MB, 在所有 Java 提交中击败了32.90%的用户

题目解析(计数排序):

使用一个数组来记录每个数字的个数,比如出现了两个3,那么在序号3处存放数值2,遍历数组来进行排序即可,代码如下:

// 计数排序 O(N+k) O(k)
class Solution {
    public int[] sortArray(int[] nums) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i = 0; i<nums.length ; i++){
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        int[] data = new int[max-min+1];
        for(int key : map.keySet()){
            data[key-min]+=map.get(key);
        }
        int pos = 0;
        for(int i = 0 ; i<nums.length ;){
            while(data[pos]!=0){
                nums[i++] = pos+min;
                data[pos]--;
            }
            pos++;
        }
        return nums;
    }
}

时间复杂度为O(N+k),空间复杂度为O(k)。

执行用时:30 ms, 在所有 Java 提交中击败了36.90%的用户

内存消耗:49.7 MB, 在所有 Java 提交中击败了97.16%的用户

题目解析(桶排序):

将整个数组的范围分为某几个指定范围,将对应范围的数值存入到该范围的链表中去,将每个范围中的数值进行排序,然后整合输出即可,代码如下:

// 桶排序 O(N+c) O(n+k)
class Solution {
    public int[] sortArray(int[] nums) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for(int i = 0; i<nums.length ; i++){
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
        }
        int num = (max-min)/10+1;
        ArrayList<ArrayList<Integer>> list = new ArrayList<>(num);
        for(int i = 0; i<num ; i++){
            list.add(new ArrayList<Integer>());
        }
        for(int i = 0 ; i<nums.length ; i++){
            list.get((nums[i]-min)/10).add(nums[i]);
        }
        int pos = 0;
        for(int i = 0 ; i<num ; i++){
            list.get(i).sort(Comparator.naturalOrder());
            for(int j = 0 ; j<list.get(i).size() ; j++){
                nums[pos++] = list.get(i).get(j);
            }
        }
        return nums;
    }
}

时间复杂度为O(N+c),空间复杂度为O(N+k)。

执行用时:18 ms, 在所有 Java 提交中击败了50.13%的用户

内存消耗:51.6 MB, 在所有 Java 提交中击败了30.11%的用户

题目解析(基数排序):

分为十个堆,代表不同数字位数的对应情况,先将数组按照个位存进行,然后按堆进行输出后,按十位存进去再输出,直到将数字的最高位存进去进行输出即可,这里有一个比较麻烦的,针对于存在负数的情况,有点难处理,我用了二十个堆,将负数和正数都存进去,再输出,这时候正数是排好的,负数都存在于前半部分,这时候我将负数转为整数,使用十个堆再进行处理,转为负数,逆向输出即可,代码如下:

// 基数排序 O(N*k) O(N+k)
class Solution {
    public int[] sortArray(int[] nums) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for(int i = 0 ; i<nums.length ; i++){
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
        }
        min = -min;
        int maxdigit = 0;
        int mindigit = 0;
        while(max>0){
            maxdigit++;
            max/=10;
        }
        while(min>0){
            mindigit++;
            min/=10;
        }
        ArrayList<ArrayList<Integer>> list = new ArrayList<>(20);
        for(int i = 0 ; i<20 ; i++){
            list.add(new ArrayList<Integer>());
        }
        for(int j = 0 ; j<maxdigit ; j++){
            for(int k = 0 ; k<nums.length ; k++){
                int cur = (nums[k]/(int)(Math.pow(10,j)))%10+10;
                list.get(cur).add(nums[k]);
            }
            int pos = 0;
            for(int k = 0; k<20 ; k++){
                for(int q = 0; q<list.get(k).size(); q++){
                    nums[pos++] = list.get(k).get(q);
                }
                list.get(k).clear();
            }
        }
        int pos = -1;
        for(int i =0 ; i<nums.length ; i++){
            if(nums[i]>=0)break;
            pos++;
        }
        if(pos!=-1){
            list.clear();
            int[] med = new int[pos+1];
            for(int i = 0 ; i<=pos ; i++){
                med[i] = -nums[i];
            }
            for(int i = 0 ; i<10 ; i++){
                list.add(new ArrayList<Integer>());
            }
            for(int j = 0 ; j<mindigit ; j++){
                for(int k = 0 ; k<=pos ; k++){
                    int cur = (med[k]/(int)(Math.pow(10,j)))%10;
                    list.get(cur).add(med[k]);
                }
                int index = 0;
                for(int k = 0; k<10 ; k++){
                    for(int q = 0; q<list.get(k).size(); q++){
                        med[index++] = list.get(k).get(q);;
                    }
                    list.get(k).clear();
                }
            }
        for(int i = pos; i>=0 ; i--){
            nums[pos-i] = -med[i];
        }
        }
        return nums;
    }
}

时间复杂度为O(N*k),空间复杂度为O(N+k)。

执行用时:62 ms, 在所有 Java 提交中击败了34.54%的用户

内存消耗:52.2 MB, 在所有 Java 提交中击败了27.74%的用户

本文含有隐藏内容,请 开通VIP 后查看