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<r-p; m++){
            a[m] = temp[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<r; j++){
            if(a[j]<pivot){
                int temp=a[j];  
                a[j]=a[i];
                a[i]=temp;
                i++;
            }
        }
        //交换a[i]和a[r]的值
        int temp1 = a[r];
        a[r] = a[i];
        a[i] = temp1;
        return i;
    };
}

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

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<high){
               //从后往前找到值比轴枢值小的值
              while(low<high && a[high]>=pivot){
                  high--;
              }
              a[low] = a[high];    
              while(low<high && a[low]<=pivot){
                  low++;
              }
              a[high] = a[low]; 
        }
        a[low] = pivot;    //找到了轴枢值应该在的位置
        if(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<p,那我们就在 nums[0...p-1]区间查找。
  • 时间复杂度: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<index){
                right = index-1;
            }else{
                left = index+1;
            }
        }
    };
    public int partition(int[]nums, int p, int q){
        int pivot = nums[q];
        int i = p;
        for(int j=p; j<q; j++){
            if(nums[j]<pivot){
                swap(nums,i,j);
                i++;
            }
        }
        swap(nums, i, q);
        return i;
    };
    public void swap(int[]nums,int p, int q){
        int temp = nums[p];
        nums[p] = nums[q];
        nums[q] = temp;
    };
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值