Day 01| 88合并两个有序数组&912排序数组

60 阅读4分钟

合并两个有序数组 LeetCode 88

题目链接:[LeetCode 88 - 简单]

思路

首先,该题需要时间复杂度O(m+n),因此该题不能使用两个for循环来遍历。 其次该题没有返回值,最终的结果是放到num1中。

由提示已知num1.length==m+n,因此不用担心num1的长度问题。

一开始的设想是进行一个从0到m+n-1的循环,通过两个下标,比较num1和num2中的大小,将小的那个值放入到num1中。但是由此衍生出来一个问题,如果num2中存在比num1小的数,会导致num1中的数被覆盖。

由此可以采用两种方式进行解决:①创建一个新的数组,其大小与num1一致,最后将结果返回到num1②将转化为从后往前遍历

双指针:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(nums1.length==0||nums2.length==0)return;
        int p1=m-1,p2=n-1;
        for(int i=m+n-1;i>=0;i--){
            if(p1>=0&&p2>=0){
                if(nums1[p1]>nums2[p2]){
                    nums1[i]=nums1[p1--];
                }else{
                    nums1[i]=nums2[p2--];
                }
            }else{
                if(p1>=0){
                    nums1[i]=nums1[p1--];
                }else if(p2>=0){
                    nums1[i]=nums2[p2--];
                }
            }
        }
    }
}

方法①的代码:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = 0, p2 = 0;
        int[] sorted = new int[m + n];
        int cur;
        while (p1 < m || p2 < n) {
            if (p1 == m) {
                cur = nums2[p2++];
            } else if (p2 == n) {
                cur = nums1[p1++];
            } else if (nums1[p1] < nums2[p2]) {
                cur = nums1[p1++];
            } else {
                cur = nums2[p2++];
            }
            sorted[p1 + p2 - 1] = cur;
        }
        for (int i = 0; i != m + n; ++i) {
            nums1[i] = sorted[i];
        }
    }
}

还有一种很简单的做法,但是不能体现其代码的精髓。 做的时候想到过,但是没有用此方法。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        for (int i = 0; i != n; ++i) {
            nums1[m + i] = nums2[i];
        }
        Arrays.sort(nums1);
    }
}

排序数组 LeetCode 912

题目链接:[LeetCode 812 - 中等]

思路

首先使用了冒泡,在10/21时超时。 选择了选择排序,在13/21时超时。 因此选择快排or堆排序进行解题:

首先介绍堆排序

912 (1).png

维护堆的性质:

主要就是判断根节点的值是否比左右子树小,如果小于左右子树,进行交换,并且继续递归判断其下一个节点的左右子树大小问题。

/**
 *@param arr 存储堆的数组
 *@param n 数组的长度
 *@param i 待维护节点的下标
**/
    private void heapify(int[] arr,int n,int i){
        int largest = i;
        int lson = 2 * i + 1;
        int rson = 2 * i + 2;
        if(lson < n && arr[largest] < arr[lson])
            largest = lson;
        if(rson < n && arr[largest] < arr[rson])
            largest = rson;

        if(largest!=i){
            int temp = arr[largest];
            arr[largest] = arr[i];
            arr[i] = temp;
            heapify(arr,n,largest);
        }
    }

堆排序分为两步走:①建堆②排序

        //建堆
        for(int i = n / 2 - 1; i >= 0 ; i--){
            heapify(arr,n,i);
        }
        //排序
        for(int i = n - 1 ; i > 0 ; i--){
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            heapify(arr,i,0);
        }

堆排序:

class Solution {
    public int[] sortArray(int[] nums) {
        return sort(nums);
    }
    private int[] sort(int[] arr){
        int n = arr.length;
        //建堆
        for(int i = n / 2 - 1; i >=0 ; i--){
            heapify(arr,n,i);
        }
        //排序
        for(int i = n - 1 ; i > 0 ; i--){
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            heapify(arr,i,0);
        }
        return arr;
    }
    private void heapify(int[] arr,int n,int i){
        int largest = i;
        int lson = i * 2 + 1;
        int rson = i * 2 + 2;

        if(lson < n && arr[largest] < arr[lson])
            largest = lson;
        if(rson < n && arr[largest] < arr[rson])
            largest = rson;

        if(largest!=i){
            int temp = arr[i];
            arr[i] = arr[largest];
            arr[largest]=temp;
            heapify(arr,n,largest);
        }
    }
}

第二天使用了快排,当出现很多个相同的数的时候会发生超时的情况。

主要是由于在快速排序中,选取基准值的选择对算法的效率有很大影响。如果每次选取的基准值都是数组的最左边或最右边的元素,且数组近乎有序时,可能会导致快速排序的时间复杂度退化为 O(n^2)。为了避免这种情况,可以随机选择数组中的一个元素作为基准值。 以下就是采用了随机基准值之后的代码,有一部分还没有能够完全理解。

class Solution {
    public int[] sortArray(int[] nums) {
        randomizedQuicksort(nums, 0, nums.length - 1);
        return nums;
    }

    public void randomizedQuicksort(int[] nums, int l, int r) {
        if (l < r) {
            int pos = randomizedPartition(nums, l, r);
            randomizedQuicksort(nums, l, pos - 1);
            randomizedQuicksort(nums, pos + 1, r);
        }
    }

    public int randomizedPartition(int[] nums, int l, int r) {
        int i = new Random().nextInt(r - l + 1) + l; // 随机选一个作为我们的主元
        swap(nums, r, i);
        return partition(nums, l, r);
    }

    public int partition(int[] nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums, i, j);
            }
        }
        swap(nums, i + 1, r);
        return i + 1;
    }

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