非比较排序

一.计数排序
1.先是将数组映射到一个presum数组上,每个元素presum[i]的值为i的出现次数。
2.在通过一次遍历将presum数组变为前缀和数组,此时presum[i]的值的含义变为:小于等于i的数有几个
3.根据presum即可得知原数组每个元素在排序会后所对应的位置,用一个temp数组保存结果,在复制回原数组。
4.对于值较大的一组数据或有负数的数据,可以将值映射到一段距离上解决。
5.时间复杂度为O(n),空间复杂度为O(max-min+1+n),这是优化后的空间复杂度,max和min为数组最大最小值。
局限:
1.数据跨度较大则既浪费时间(退化为O(NlogN))和空间。
2.不能对浮点数和排序。
计数排序原理解释链接

class Solution {
public:
//计数排序
    vector<int> sortArray(vector<int>& nums) {
        int len = nums.size();
        if (len < 1) {
            return nums;
        }
        int maxVal = nums[0];
        int minVal = nums[0];
        for (int x : nums) {
            if (maxVal < x) maxVal = x;
            if (minVal > x) minVal = x;
        }
        vector<int> presum(maxVal - minVal + 1, 0);
        for (int x : nums) {
            presum[x - minVal]++;
        }
        for (int i = 1; i < presum.size(); i++) {
            presum[i] += presum[i - 1];
        }

        vector<int> temp(len);
        for (int i = len - 1; i >= 0; i--) {
            int index = presum[nums[i] - minVal] - 1;
            temp[index] = nums[i];
            presum[nums[i] - minVal]--;
        }

        for (int i = 0; i < len; i++) {
            nums[i] = temp[i];
        }
        return nums;
    }
};

二.基数排序
计数排序的变形,解决数据跨度较大的问题,通过每次将每一位进行计数排序,因此空间复杂度为O(n),时间复杂度为O(d*n),d为数组中最大数的位数,典型的时间换空间

class Solution {
    private static final int OFFSET = 50000;
    public int[] sortArray(int[] nums) {
        //基数排序,对数组每个元素的每一位进行计数排序
        int len = nums.length;

        //预处理,让所有的数都大于等于0,这样才可以使用基数排序
        for (int i = 0; i < len; i++) {
            nums[i] += OFFSET;
        }

        //第一步先找出最大数
        int max = nums[0];
        for (int i = 0; i < nums.length; i++) {
            if (max < nums[i]) max = nums[i];
        }
        //第二步,找出最大数的位数
        int maxlen = (max + "").length();

        //计数排序所需使用的计数数组和临时数组
        int[] presum = new int[10];
        int[] temp = new int[len];

        //第三步:对每一位进行计数排序
        for (int i = 0, divisor = 1; i < maxlen; i++, divisor *= 10) {
            countSort(nums, temp, divisor, presum);
        }

        //将偏移量置回
        for (int i = 0; i < nums.length; i++) {
            nums[i] -= OFFSET;
        } 
        return nums;
    }

    private void countSort(int[] nums, int[] temp, int divisor, int[] presum) {
        //初始化前缀和数组
        for (int i = 0; i < nums.length; i++) {
            int remain = ((nums[i] / divisor) ) % 10;
            presum[remain]++;
        }
        for (int i = 1; i < 10; i++) {
            presum[i] += presum[i - 1];
        }

        //从后向前为每个元素找到自己的位置
        //从后向前保证了关键字相同的元素后面的一定排在后面,即保证了稳定性
        for (int i = nums.length - 1; i >= 0; i--) {
            int remain = (nums[i] / divisor) % 10;
            int index = presum[remain] - 1;
            temp[index] = nums[i];
            presum[remain]--;
        }

        //将结果复制回原数组,注意:这里不能单纯用nums=temp,想一想为什么
        for (int i = 0; i < nums.length; i++) {
            nums[i] = temp[i];
        }

        //将presum置零,以便下次使用
        for (int i = 0; i < 10; i++) {
            presum[i] = 0;
        }
    }
}

3.桶排序
1.思想是设置几个域连续的桶,将数组所有的元素装入各自所对应的桶。桶内元素各自排序,因为桶间是有序的,所以每个桶排好序后顺序从桶中取出即可完成排序。也是一种空间换时间的排序算法
2.复杂度:
时间复杂度为m * (n/m)log(n/m)=n (log n-log m).m为桶个数,n为数组长度
空间复杂度为O(n+m)

class Solution {
    // 桶排序
    // 1 <= nums.length <= 10000
    // -50000 <= nums[i] <= 50000
    private static final int OFFSET = 50000;

    public int[] sortArray(int[] nums) {
        int len = nums.length;

         // 第 1 步:将数据转换为 [0, 10_0000] 区间里的数
        for (int i = 0; i < len; i++) {
            nums[i] += OFFSET;
        }

        // 第 2 步:观察数据,设置桶的个数
        List[] bucket = new ArrayList[101]; //此时每个桶的容量为1000,设置桶100是考虑边界100000

        //初始化每一个桶
        for (int i = 0; i < 101; i++) {
            bucket[i] = new ArrayList<Integer>();
        }

        //第3步:分桶
        for (int num : nums) {
            int bucketIdx = num / 1000;
            bucket[bucketIdx].add(num);
        }

        //第4步:桶内单独排序
        for (int i = 0; i < 101; i++) {
            bucket[i].sort(null);
        }

        //第5步:从桶内依次取出
        int index = 0;
        for (int i = 0; i < 101; i++) {
            for (int j = 0; j < bucket[i].size(); j++) {
                nums[index++] = (int)bucket[i].get(j) - OFFSET;
            }
        }

        return nums;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值