LeetCode(912):排序数组 Sort an Array(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅
本文介绍了LeetCode第912题——排序数组的解决方案,重点是使用Java进行升序排列。文章包含题目描述和解题思路,是程序员笔试备考的重要参考资料。
摘要由CSDN通过智能技术生成

2019.8.8 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

排序算法最坏复杂度平均复杂度空间复杂度稳定性备注
归并排序nlognnlognn稳定其他排序(内存)配合多路归并(外存)可对进行文件进行外排序,链表排序最佳方法
快速排序n^2nlogn1不稳定海量数据,分段后长度过小改为插入,快排枢纽选取不好会造成n^2与递归过深,递归层次过深选择堆排。
堆排序nlognnlogn1不稳定海量数据,nlogn的常数项较大弱于快排,每次堆顶最大元素交换至尾部时容易交换到最小元素,堆调整时又调整至最尾容易形成无意义操作
插入排序n^2n^21稳定最好情况o(n)
希尔排序n^2n^1.3-21不稳定平均复杂度取决于增量序列,跳跃插入机制破坏了它的稳定性
折半插入排序n^2n^21稳定减少了比较次数,但是元素的移动次数不变
冒泡排序n^2n^21稳定最好情况o(n),改进方案:1.若本次无交换,说明元素有序,直接跳出;2.记录本次最后一个交换的位置,该位置之后的元素已经有序,不需要交换。
选择排序n^2n^21不稳定
基数排序/桶排序d(n+r)d(n+r)r稳定d个关键码/位数,关键码的取值范围/桶数r
计数排序n+kn+kk稳定k为数据的取值范围,将所有元素的频次放入对应的桶中,再进行排序。计数排序是桶排序的一种特殊情况。

传送门:排序数组

Given an array of integers nums, sort the array in ascending order.

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

示例 1:
输入:[5,2,3,1]
输出:[1,2,3,5]

示例 2:
输入:[5,1,1,2,0,0]
输出:[0,0,1,1,2,5]


import java.util.Arrays;

/**
 *
 * Given an array of integers nums, sort the array in ascending order.
 * 给定一个整数数组 nums,将该数组升序排列。
 *
 */

public class SortInArray {

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

    private static final int QUICKSORTSHOLD = 50;
    private static final int MERGESORTSHOLD = 300;

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2){
            return nums;
        }

        if (nums.length < QUICKSORTSHOLD){
            insertSort(nums);
        }else if (nums.length < MERGESORTSHOLD) {
            quickSort(nums);
        }else{
            mergeSort(nums);
        }
        return nums;
    }

    ///归并排序/

    //归并排序:最坏复杂度o(nlogn),平均复杂度o(nlogn),空间复杂度o(n),稳定
    public int[] mergeSort(int[] nums) {
        Merge(nums, 0, nums.length - 1);
        return nums;
    }

    private void Merge(int[] nums, int begin, int end){
        if(begin >= end){
            return;
        }
        int mid = (begin + end) >> 1;
        Merge(nums, begin, mid);
        Merge(nums, mid + 1, end);
        
        int[] newNums = new int[end - begin + 1];
        int newIdx = 0;
        int leftIdx = begin;
        int rightIdx = mid + 1;
        while(leftIdx <= mid && rightIdx <= end){
            newNums[newIdx++] = nums[leftIdx] <= nums[rightIdx] ? nums[leftIdx++] : nums[rightIdx++];
        }
        if(leftIdx <= mid){
            System.arraycopy(nums, leftIdx, newNums, newIdx, mid - leftIdx + 1);
        }else{
            System.arraycopy(nums, rightIdx, newNums, newIdx, end - rightIdx + 1);
        }
        System.arraycopy(newNums, 0, nums, begin, end - begin + 1);
    }
    
    //非递归写法:将起始位置入栈
    

    ///快速排序/

    //快速排序:最坏复杂度o(n^2),平均复杂度o(nlogn),空间复杂度o(logn),不稳定
    //STL优化方法:当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。
    public int[] quickSort(int[] nums) {
        quickSortSolution(nums, 0, nums.length - 1);
        return nums;
    }

    private void quickSortSolution(int[] nums, int begin, int end){
        if(begin < end){
            int pivot = partition(nums, begin, end);
            quickSortSolution(nums, begin, pivot - 1);
            quickSortSolution(nums, pivot + 1, end);
        }
    }

    private int partition(int[] nums, int begin, int end){
        int pivot = begin + (int)(Math.random() * (end - begin));
        swap(nums, begin, pivot);
        pivot = begin;
        while(begin <= end){
            while(begin <= end && nums[begin] <= nums[pivot]){
                ++begin;
            }
            while(begin <= end && nums[end] > nums[pivot]){
                --end;
            }
            if(begin <= end){
                swap(nums, begin, end);
            }
        }
        //此时end的右边数字均比nums[pivot]大,交换后pivot对应数字位于正确位置end
        swap(nums, pivot, end);
        return end;
    }
    
    //非递归写法:将起始位置入栈

    ///堆排序///

    //堆排序:最坏复杂度o(nlogn),平均复杂度o(nlogn),空间复杂度o(1),不稳定
    public int[] heapSort(int[] nums) {
        //构建初始堆,从第一个非叶子结点至根节点进行堆调整,构建初始堆,此时最大值位于堆顶,且每一个结点均比子结点大。
        int firstNotLeaves = (nums.length >> 1) - 1;
        for(int i = firstNotLeaves; i >= 0; i--){
            heapAdjust(nums, nums.length, i);
        }
        //每次循环将堆顶元素(最大值)交换至最尾,再对剩余元素进行一次堆调整
        for(int tail = nums.length - 1; tail >= 1; tail--){
            swap(nums, 0, tail);
            heapAdjust(nums, tail, 0);
        }
        return nums;
    }

    //每次堆调整将rootIdx所在左右子结点的较大值交换至rootIdx,并且自上而下进行至不能再交换。
    private void heapAdjust(int[] nums, int length, int rootIdx){
        int maxIdx = rootIdx;
        //通过左子结点是否超出length判断根节点是否在length内有子树
        int leftIdx = (maxIdx << 1) + 1;
        int rightIdx = leftIdx + 1;
        while(leftIdx < length){
            maxIdx = nums[leftIdx] > nums[maxIdx] ? leftIdx : maxIdx;
            maxIdx = rightIdx < length && nums[rightIdx] > nums[maxIdx] ? rightIdx : maxIdx;
            if(maxIdx == rootIdx){
                break;
            }
            swap(nums, rootIdx, maxIdx);
            rootIdx = maxIdx;
            leftIdx = (maxIdx << 1) + 1;
            rightIdx = leftIdx + 1;
        }
    }

    ///插入排序/

    //普通插入排序:最坏复杂度o(n^2),平均复杂度o(n^2),空间复杂度o(1),稳定(最好情况o(n))
    public int[] insertSort(int[] nums) {
        for(int i = 0; i < nums.length; i++){
            int temp = nums[i];
            int j = i - 1;
            //将nums[i]插入i之前第一个不大于它的值j的位置之后
            while(j >= 0 && nums[j] > temp){
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = temp;
        }
        return nums;
    }

    ///希尔排序/

    //希尔排序:最坏复杂度o(n^2),平均复杂度o(n^1.3-2)(取决于增量序列),空间复杂度o(1),不稳定(跳跃插入机制破坏)
    public int[] shellSort(int[] nums) {
        for(int dk = nums.length / 2; dk >= 1; dk /= 2){
            for(int i = dk; i < nums.length; i++){
                if(nums[i] < nums[i - dk]){
                    int temp = nums[i];
                    int j = i - dk;
                    while(j >= 0 && nums[j] > temp){
                        nums[j + dk] = nums[j];
                        j -= dk;
                    }
                    nums[j + dk] = temp;
                }
            }
        }
        return nums;
    }

    /折半插入排序///

    //折半插入排序:最坏复杂度o(n^2),平均复杂度o(n^2),空间复杂度o(1),稳定(减少了比较次数,但是元素的移动次数不变)
    public int[] halfInsertSort(int[] nums) {
        for(int i = 0; i < nums.length; i++){
            int temp = nums[i];
            int begin = 0;
            int end = i - 1;
            while(begin <= end){
                int mid = (begin + end) >> 1;
                if(nums[mid] > temp){
                    end = mid - 1;
                }else{
                    begin = mid + 1;
                }
            }
            for(int j = i - 1; j >= end + 1; j--){
                nums[j + 1] = nums[j];
            }
            nums[end + 1] = temp;
        }
        return nums;
    }

    ///冒泡排序/

    //冒泡排序:最坏复杂度o(n^2),平均复杂度o(n^2),空间复杂度o(1),稳定(最好情况o(n))
    //改进方案:1.若本次无交换,说明元素有序,直接跳出;2.记录本次最后一个交换的位置lastExchangeIndex,该位置之后的元素已经有序,不需要交换。
    public int[] bubbleSort(int[] nums) {
        int lastExchangeIndex = nums.length - 1;
        int sortOrder = nums.length - 1;
        for(int i = 0; i < nums.length - 1; i++){
            boolean flag = false;
            for(int j = 0; j < sortOrder; j++){
                if(nums[j] > nums[j + 1]){
                    swap(nums, j + 1, j);
                    lastExchangeIndex = j;
                    flag = true;
                }
            }
            sortOrder = lastExchangeIndex;
            if(!flag){
                return nums;
            }
        }
        return nums;
    }

    ///选择排序/

    //选择排序:最坏复杂度o(n^2),平均复杂度o(n^2),空间复杂度o(1),不稳定
    public int[] selectionSort(int[] nums) {
        for(int i = 0; i < nums.length - 1; i++){
            int min = i;
            for(int j = i + 1; j < nums.length; j++){
                min = nums[j] < nums[min] ? j : min;
            }
            if(min != i){
                swap(nums, i, min);
            }
        }
        return nums;
    }

    ///基数排序/

    //基数排序/桶排序:最坏复杂度o(d(n+r)),平均复杂度o(d(n+r)),空间复杂度o(r),稳定(d个关键码/位数,关键码的取值范围/桶数r)
    //以下算法无法对负数排序
    public int[] radixSort(int[] nums) {
        //增加对负数适应性
        int max = Arrays.stream(nums).max().getAsInt();
        int min = Arrays.stream(nums).min().getAsInt();
        if(min < 0){
            for(int i = 0; i < nums.length; i++){
                nums[i] += (-min + 1);
            }
        }
        //从个位开始,对数组a按"指数"进行排序
        //当对数组按个位进行排序时,exp=1;按十位进行排序时,exp=10;...
        for (int exp = 1; (max - min + 1) / exp > 0; exp *= 10) {
            countSort(nums, exp);
        }

        if(min < 0){
            for(int i = 0; i < nums.length; i++){
                nums[i] -= (-min + 1);
            }
        }
        return nums;
    }

    private static void countSort(int[] nums, int exp) {
        // 存储"被排序数据"的临时数组
        int[] output = new int[nums.length];
        int[] buckets = new int[10];

        // 将数据出现的次数存储在buckets[]中
        for (int i = 0; i < nums.length; i++){
            buckets[(nums[i] / exp) % 10]++;
        }

        // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
        for (int i = 1; i < 10; i++){
            buckets[i] += buckets[i - 1];
        }

        // 将数据存储到临时数组output[]中
        for (int i = nums.length - 1; i >= 0; i--) {
            output[buckets[(nums[i] / exp) % 10] - 1] = nums[i];
            buckets[(nums[i] / exp) % 10]--;
        }

        // 将排序好的数据赋值给nums
        for (int i = 0; i < nums.length; i++){
            nums[i] = output[i];
        }
    }
    
    ///计数排序/

    //计数排序:最坏复杂度o(n+k),平均复杂度o(n+k),空间复杂度o(k),稳定(k为数据的取值范围)
    //将所有元素的频次放入对应的桶中,再进行排序。计数排序是桶排序的一种特殊情况。
    public int[] countingSort(int[] nums) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int num : nums) {
            max = num > max ? num : max;
            min = num < min ? num : min;
        }

        int[] buckets = new int[max - min + 1];
        for (int i : nums) {
            buckets[i - min] += 1;
        }

        int[] result = new int[nums.length];
        int resultIdx = 0;

        for (int i = 0; i < buckets.length; i++) {
            while (buckets[i]-- != 0) {
                result[resultIdx++] = i + min;
            }
        }
        return result;
    }
}


#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值