快速排序&归并排序

1. 快速排序

1)思路(先整体排序,再局部排序)

从无序数组中选择一个pivot(中心点),小于pivot的数放到左边,大于pivot的数放到右边,得到两个无序子数组。再分别对两个无序子数组选择pivot(中心点),小于pivot的数放在左边,大于pivot的数放在右边,以此类推,层层递归。递归终止条件是子数组中只剩下一个数,无需排序。

2)时间复杂度:最优为 O(nlogn),最坏为 O(n^2)

空间复杂度:O(1),不需要额外的空间,直接改动原数组

public class Solution {
    /*
     * @param A: an integer array
     * @return: 
     */
    public void sortIntegers2(int[] A) {
        quickSort(A, 0, A.length - 1);
    }
    
    public void quickSort(int[] A, int start, int end) {
        if (start >= end) {
            return;
        }

        int pivot = A[start + (end - start) / 2];
        int left = start;
        int right = end;
        
        // left <= right not left < right
        while (left <= right) {
            // A[left] < pivot not <=, 避免极端情况导致时间复杂度变大
            while (left <= right && A[left] < pivot) {
                left++;
            }
            while (left <= right && A[right] > pivot) {
                right--;
            }
            
            if (left <= right) {
                int temp = A[left];
                A[left] = A[right];
                A[right] = temp;
                
                left++;
                right--;
            }
        }
        // sort A[start... right] 
        quickSort(A, start, right);
        // sort A[left ... end]
        quickSort(A, left, end);
    }
}

注意:

1)如果将left<=right 改成 left<right,则在指针相遇时便跳出while循环。

考虑不断将数组分区至子数组包含两个整数时,往下再分区其子数组分别包含1个整数、2个整数,即 [2,3] ----> [2] , [2,3]

递归会无限调用,遇到stackOverFlowError

2) 如果将代码中A[left] < pivot 改成 A[left] <= pivot

当遇到极端情况,数组中有大量相同整数,即[1,1,1,1,1,1,2,4,7]

每当A[left] <= pivot,left指针便右移,则分割后的子数组会一大一小,导致时间复杂度变大。

2. 归并排序

1) 思路(先局部排序,再整体排序)

将给定的无序数组从中间分割成两个子数组,再分别将分割后的子数组从中间分隔,以此类推,直至数列中只有一个元素。从小到大将子数组排序后再合并。

2)时间复杂度:稳定为O(nlogn)

空间复杂度:O(n),因为需要额外的空间存放排好序的数,再对应赋值给原数组

public class Solution {
    /**
     * @param A an integer array
     * @return void
     */
    public void sortIntegers2(int[] A) {
        // use a shared temp array, the extra memory is O(n) at least
        int[] temp = new int[A.length];
        mergeSort(A, 0, A.length - 1, temp);
    }
    
    private void mergeSort(int[] A, int start, int end, int[] temp) {
        if (start >= end) {
            return;
        }
        
        int left = start, right = end;
        int mid = (start + end) / 2;

        mergeSort(A, start, mid, temp);
        mergeSort(A, mid + 1, end, temp);
        merge(A, start, mid, end, temp);
    }
    
    private void merge(int[] A, int start, int mid, int end, int[] temp) {
        int left = start;
        int right = mid + 1;
        int index = start;
        
        // merge two sorted subarrays in A to temp array
        while (left <= mid && right <= end) {
            if (A[left] < A[right]) {
                temp[index++] = A[left++];
            } else {
                temp[index++] = A[right++];
            }
        }
        while (left <= mid) {
            temp[index++] = A[left++];
        }
        while (right <= end) {
            temp[index++] = A[right++];
        }
        
        // copy temp back to A
        for (index = start; index <= end; index++) {
            A[index] = temp[index];
        }
    }
}

3. 快速选择算法

1)思路:

利用了快速排序算法的分区(partition) 思路,选取数组的中心点pivot,小于/大于pivot的数分别放到两边,只考虑包含target的子数组并对其进行排序。

2)时间复杂度 为O(n)

4. 例题

leetcode:剑指 Offer II 076. 数组中的第 k 大的数字75. 颜色分类, 912. 排序数组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值