排序算法小结

排序算法小结

一、堆排序

步骤:

  1. 初始化堆(从最后一个非叶子节点为根节点的子树开始,nums.length / 2 - 1);
  2. 调整大顶堆,将最大的数放到末尾,之后进行新一轮调整。
class Solution {
    public int heapSort(int[] nums, int k) {
        int len = nums.length;
        buildMaxHeap(nums, len);
        for (int len - 1; i > 0; i--) {
            // 将最大值交换到数组最后
            swap(arr, 0, i);
            // 调整剩余数组,使其满足大顶堆
            maxHeapify(arr, 0, i);
        }
    }
    public void buildMaxHeap(int[] nums, int heapSize){
        int i = heapSize / 2 - 1;//从最后一个非叶子节点开始建立堆
        for(int j = i; j >= 0; j--){
            maxHeapify(nums, j, heapSize);
        }
    }
    public void maxHeapify(int[] nums, int i, int heapSize){//i是当前堆的根节点
        int leftChild = 2 * i + 1;
        int rightChild = leftChild + 1;
        int largest = i;
        if(leftChild < heapSize && nums[leftChild] > nums[largest]){
            largest = leftChild;
        }
        if(rightChild < heapSize && nums[rightChild] > nums[largest]){
            largest = rightChild;
        }
        if(largest != i){
            swap(nums, largest, i);
            maxHeapify(nums, largest, heapSize);
        }
    }

    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

二、快速排序

每轮一个pivot,左边的数比其小,右边的比其大。

class Solution {
    private static final Random RANDOM = new Random();
    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    //代码简洁版的快排
    public void quickSort(int[] nums, int begin, int end){
        if(begin >= end){
            return;
        }
        int random = begin + RANDOM.nextInt(end - begin + 1);//pivot预处理,防止递归树偏斜
        swap(nums, random, begin);

        int left = begin;//也是pivot
        int right = end;
        while(left < right){
            while(left < right && nums[right] >= nums[begin]){
                right--;
            }
            while(left < right && nums[left] <= nums[begin]){
                left++;
            }
            swap(nums, left, right);
        }
        swap(nums, begin, left);
        quickSort(nums, begin, left - 1);
        quickSort(nums, left + 1, end);
    }
    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

三、归并排序

自顶向下不断拆分数组,直到不可分,再自底向上不断排序子数组并合并。

class Solution {
    public int sort(int[] nums) {
        int[] res = new int[nums.length];
        mergeSort(nums, 0, nums.length - 1, res);
    }
    public void mergeSort(int[] nums, int head, int tail, int[] res){
        if(head == tail) return;
        int mid = head + (tail - head) / 2;
        mergeSort(nums, head, mid, res);
        mergeSort(nums, mid + 1, tail, res);
        merge(nums, head, tail, res);
    }
    public void merge(int[] nums, int head, int tail, int[] res){
        int mid = head + (tail - head) / 2;
        int index1 = head;
        int index2 = mid + 1;
        int index = head;
        while(index1 <= mid && index2 <= tail){
            if(nums[index1] <= nums[index2]){
                res[index++] = nums[index1++];
            }else{
                res[index++] = nums[index2++];
            }
        }
        while(index1 <= mid){
            res[index++] = nums[index1++];
        }
        while(index2 <= tail){
            res[index++] = nums[index2++];
        }
        while(head <= tail){
            nums[head] = res[head++];
        }
    }
}

四、计数排序

步骤:

  1. 求得数组最大值与最小值差值,得到计数数组counting的大小。
  2. counting数组记录每个数的个数。
  3. counting数组记录每个数应在的位置。
  4. 通过counting数组记录的位置,将原数组的元素一一赋值到临时数组中。
  5. 复制回原数组
class Solution {
    public int[] sortArray(int[] nums) {
        countingSort(nums);
        return nums;
    }
    public void countingSort(int[] nums){
        int max = nums[0];
        int min = nums[0];
        for(int i = 0; i < nums.length; i++){
            if(nums[i] > max){
                max = nums[i];
            }else if(nums[i] < min){
                min = nums[i];
            }
        }

        int range = max - min + 1;
        int[] counting = new int[range];
        int[] res = new int[nums.length];
        for(int element : nums){
            counting[element - min]++; 
        }

        counting[0]--;
        for(int i = 1; i < range; i++){
            counting[i] = counting[i] + counting[i - 1];//该值最后一个元素的位置
        }

        for(int i = nums.length - 1; i >= 0; i--){
            res[counting[nums[i] - min]--] = nums[i];
        }

        for(int i = 0; i < nums.length; i++){
            nums[i] = res[i];
        }
    }
}

五、基数排序

  1. 非负整数,counting数组长度为10;有负数,长度为19。
  2. 先求最大绝对值数字的长度,并得到其位数。
  3. 从最低位开始循环,每一轮步骤类似计数排序2,3,4步,并且维护一个变量dev,每轮自乘10,保证每轮计算的是下一个高位的数字。
class Solution {
    public int sort(int[] nums) {
        int len = nums.length;
        radixSort(nums);
    }
    public void radixSort(int[] nums){
        int len = nums.length;
        int max = 0;
        for(int element : nums){
            if(element > max){
                max = element;
            }
        }
        int maxLength = 0;
        while(max != 0){
            max /= 10;
            maxLength++;
        }
        int dev = 1;
        int[] res = new int[len];
        for(int i = 0; i < maxLength; i++){
            int[] counting = new int[10];
            for(int element : nums){
                int radix = element / dev % 10;
                counting[radix]++;
            }

            counting[0]--;
            for(int j = 1; j < 10; j++){
                counting[j] += counting[j - 1];
            }

            for(int j = len - 1; j >= 0; j--){
                int radix = nums[j] / dev % 10;
                res[counting[radix]--] = nums[j];
            }
            
            System.arraycopy(res, 0, nums, 0, len);
            
            dev *= 10;
        }
    }
}

六、Arrays.sort()逻辑

对于基本类型的数组排序时,len < 47,双插入排序(每次从左边选两个数,先插入大的,然后小的从大的插入的位置往前寻找插入位置); 47 < len < 286,双轴快排(从中间位置往前后各走两次1/7数组长度,选取5个备选轴,一般情况选择2,4轴,如果中间区域过大,超过了数组长度的4/7,则剔除中间区域的左右边界进行下一轮排序,也就是[pivot1, pivot2]变为了(pivot1, pivot2),如果备选轴有元素相等,则改为单轴快排,选取第3个备选轴);len > 286,优先使用类似TimSort的排序方法(归并排序的改进,检测单调递增和递减的子数组,每一块都记为一个run数组,遇到递减的子数组就反转),但是若连续相同的元素达到了33个,或者子数组的个数达到了67个,就改为双轴快排。

对于非基本类型的数组排序采用TimSort或者归并排序,JDK1.7之前是默认归并,1.7及以后默认TimSort,但可以手动更改默认,继续使用归并。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值