04排序操作之归并、快排

前面所讲的冒泡、插入和选择排序,因为平均时间复杂度为o(n^2),所以仅仅适合小数据量的排序工作。在数据量比较大时,平均时间复杂度为o(nlogn)的归并和快排就派上用场了,下面分别介绍归并排序和快速排序。

1. 归并排序

  • 最好、最坏、平均时间复杂度都为o(nlogn)
  • 不属于原地排序算法,其空间复杂度为o(n),因为merge函数中需要使用额外的空间
  • 其排序的关键在于merge函数,merge函数中可以实现稳定排序,所以整个归并排序过程是稳定的
class solution{
    public void mergeSort(int[]a, n){
        mergeSortC(a, 0, n-1)
    };
    public void mergeSortC(int[]a, int p, int r){
        if(p>=r) return;
        int q = (p+r)/2;
        //递归归并排序
        mergeSortC(a, p, q);
        mergeSortC(a, q+1, r);
        merge(a,p,q,r);
    };
    public void merge(int[]a, int p, int q,int r){
        int[] temp = new int[r-p];
        int i=p, j=q+1,k=0;
        while(i<=q && j<=r){
            if(a[i]<=a[j]){         
                temp[k++] = a[i++];
            }else{
                temp[k++] = a[j++];
            }
        }
        //判断那个子数组有剩余数据
        int start = i, end = q;
        if(j<=r){
            start = j;
            end = r;
        }
        //将剩余的数据拷贝到临时数组
        while(start<=end){
            temp[k++] = a[start++];
        }
        for(int m=0; m

2. 快速排序

  • 最好时间复杂度,平均时间复杂度都为o(nlogn),最坏时间复杂度为o(n^2)
  • 快速排序与归并排序相比,巧妙的在partition函数中进行原地分区,所以快速排序是原地排序算法
  • 快速排序partition中的原地分区使得快速排序不稳定

法一:不加哨兵

class Solution{
    public void quickSort(int[]a, int n){
        quickSortC(a, 0, n-1); 
    };
    //实现快排的主函数
    public void quickSortC(int[]a, int p, int r){
        if(p>=r) return;  //递归终止条件
        q = partition(a, p, r);
        quickSortC(a, p, q-1);
        quickSortC(a, q+1, r);
    };
    //partition分割函数
    public int partition(int[]a, int p, int r){
        int pivot = a[r];
        //将整个数组分为两个部分,左半部分小于pivot,右半部分大于pivot
        int i = p;
        for(int j=p; j

法二:添加哨兵,可以减少一些交换步骤,提高程序效率

class Solution{
    public void quickSort(int[]a, int n){
        quickSortC(a, 0, n-1);
    };

    public  void quickSortC(int[]a, int p, int r){

        int low = p;
        int high = r;
        int pivot = a[low];   //选择最左端点的值为轴枢值

        while(low=pivot){
                  high--;
              }
              a[low] = a[high];    
              while(low p+1){ 
            quickSortC(a, p, low-1);
        }
        if(high < r-1){
            quickSortC(a, high+1, r);
        }

    };
}

实战一:求无序数组的第k大元素(leetcode215)

  • 思路:我们将问题转换为求无序数组的第k大的元素的索引,其索引值为len-k,其中len为数组的长度,我们选择数组区间 nums[0...n-1]的最后一个元素 nums[n-1]作为 pivot,对数组 nums[0...n-1]原地分区,这样数组就分成了三部分,nums[0...p-1]、nums[p]、nums[p+1...n-1]。如果 p=len-k,那么nums[p]就是要求解的元素;如果 len-k>p, 说明第 k 大元素出现在 nums[p+1...n-1]区间,我们再按照上面的思路递归地在 nums[p+1...n-1]这个区间内查找。同理,如果 len-k
  • 时间复杂度:o(n)。时间复杂度为一个等比数列,n, n/2,n/4,n/8,...,1,最后求和为值2n-1。
  • 空间复杂度为o(1)
class Solution {
    //基于快速排序partition方法的实现
    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;   //得出数组的长度
        int left = 0;
        int right = len -1;

        //将求第k大的元素转换为第k大元素的索引是len-k
        int target = len-k;
        while(true){
            int index = partition(nums, left, right);
            if(target == index){
                return nums[index];
            }
            else if(target

 

你可能感兴趣的:(算法,排序算法,快速排序,数据结构)