• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【数据结构和算法】排序之归并排序,快速排序

分治思想跟我们前面讲的递归思想很像。是的,分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归是一种编程技巧

一、归并排序

1、归并排序的原理

2、归并排序的分析

  • 是否是稳定排序:在合并数组的环节,当左右数组的元素相同时,优先放左侧元素到合并数组中,则为稳定排序算法。
  • 空间复杂度:O(n),在merge阶段需要申请临时数组进行合并,故不是原地排序算法
  • 时间复杂度:T(n)=O(nlogn)

3、归并排序的案例

(1)递推公式

递推公式:
merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))

终止条件:
p >= r 不用再继续分解
View Code

(2)案例:

https://www.cnblogs.com/shangxiaofei/articles/10890150.html

 

二、快速排序

1、快速排序的原理

快排的思想是这样的:如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。

根据分治、递归的处理思想,我们可以用递归排序下标从 p 到 q-1 之间的数据和下标从 q+1 到 r 之间的数据,直到区间缩小为 1,就说明所有的数据都有序了。

2、快速排序的分析

空间复杂度:原地排序,O(1)

是否为稳定排序算法:不是稳定的排序算法,寻找分区的时候,会对数据进行交换导致相同值顺序变化。

时间复杂度

  • 平均时间复杂度:O(n*lgn) (取决于分区数据的选择,是否会将数组拆成对等的左,右两部分)
  • 时间复杂度会退化为O(n^2):我举一个比较极端的例子。如果数组中的数据原来已经是有序的了,比如 1,3,5,6,8。如果我们每次选择最后一个元素作为 pivot,那每次分区得到的两个区间都是不均等的。我们需要进行大约 n 次分区操作,才能完成快排的整个过程。每次分区我们平均要扫描大约 n/2 个元素,这种情况下,快排的时间复杂度就从 O(nlogn) 退化成了 O(n2)。

 

3、快速排序的案例

(1)递推公式

递推公式:
quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1… r)

终止条件:
p >= r
View Code

(2)代码案例

 /**
     * 快速排序
     *
     * @param array
     * @return
     */
    public static int[] quickSort(int[] array) {
        if (array == null || array.length <= 1) {
            return array;
        }
        //快速排序
        quickSort(array, 0, array.length - 1);
        return array;
    }

    private static void quickSort(int[] array, int leftIndex, int rightIndex) {
        if (leftIndex >= rightIndex) {
            return;
        }
        //对数组进行分区
        int partitionIndex = partition(array, leftIndex, rightIndex);
        //排序左边数组
        quickSort(array, leftIndex, partitionIndex - 1);
        //排序右边数组
        quickSort(array, partitionIndex + 1, rightIndex);
    }


    private static int partition(int[] array, int leftIndex, int rightIndex) {
        //用数组的最右边的元素作为分区元素
        int target = array[rightIndex];
        //声明一个分区的下标元素(作为左侧扫描下标)
        int i = leftIndex;
        int tmp;

        for (int j = leftIndex; j < rightIndex; j++) {
            if (array[j] < target) {
                tmp = array[j];
                array[j] = array[i];
                array[i]=tmp;
                //左边指针向右移动1位
                i++;
            }
        }

        tmp = array[rightIndex];
        array[rightIndex] = array[i];
        array[i]=tmp;

        return i;
    }
View Code

 

https://www.cnblogs.com/shangxiaofei/articles/10978864.html

 

 

快速排序和归并排序的区别

可以发现,归并排序的处理过程是由下到上的,先处理子问题,然后再合并。而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题。归并排序虽然是稳定的、时间复杂度为 O(nlogn) 的排序算法,但是它是非原地排序算法。我们前面讲过,归并之所以是非原地排序算法,主要原因是合并函数无法在原地执行。快速排序通过设计巧妙的原地分区函数,可以实现原地排序,解决了归并排序占用太多内存的问题。

 

 

算法题:如何用快排思想在O(n)内查找第K大元素?

快排核心思想就是分治和分区,我们可以利用分区的思想,来解答开篇的问题:O(n) 时间复杂度内求无序数组中的第 K 大元素。比如,[6,1,3,5,7,2,4,9,11,8]这样一组数据,第 3 大元素就是 8。

我们选择数组区间 A[0…n-1]的最后一个元素 A[n-1]作为 pivot,对数组 A[0…n-1]原地分区,这样数组就分成了三部分,A[0…p-1]、A[p]、A[p+1…n-1]。

  • 如果 p+1=K,那 A[p]就是要求解的元素;
  • 如果 K>p+1, 说明第 K 大元素出现在 A[p+1…n-1]区间,我们再按照上面的思路递归地在 A[p+1…n-1]这个区间内查找。
  • 如果 K<p+1,那我们就在 A[0…p-1]区间查找。

我们再来看,为什么上述解决思路的时间复杂度是 O(n)?

  • 第一次分区查找,我们需要对大小为 n 的数组执行分区操作,需要遍历 n 个元素。
  • 第二次分区查找,我们只需要对大小为 n/2 的数组执行分区操作,需要遍历 n/2 个元素。
  • 依次类推,分区遍历元素的个数分别为、n/2、n/4、n/8、n/16.……直到区间缩小为 1。
  • 如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于 2n-1。所以,上述解决思路的时间复杂度就为 O(n)。

案例:

public static void main(String[] args) {

        int[] array = {10,1,2,3,4,8,8,11,0,1,2};
        int data = findKnData(array,0,array.length-1,3 );
        System.out.println(data);

    }


    public static int findKnData(int[] array, int left, int right, int kn) {
        if (array == null || kn <= 0 || array.length < kn) {
            //如果数组为空,或数组的长度小于Kn,则认为入参有问题
            throw new IllegalArgumentException();
        }
        int partitionIn = partitionIn(array, left, right);
        if (partitionIn + 1 == kn) {
            //则表名该元素为数组中第kn大元素
            return array[partitionIn];
        } else if (partitionIn + 1 > kn) {
            return findKnData(array, left, partitionIn - 1, kn);
        } else {
            return findKnData(array, partitionIn + 1, right, kn);
        }
    }


    private static int partitionIn(int[] array, int left, int right) {
        if (left == right) {
            return left;
        }

        int i = left;
        int tmp;
        int compareData = array[right];
        for (int j = left; j <= right - 1; j++) {
            //如果j位置的元素比比较元素大,则进行位置交换,交换至i的左边
            if (array[j] > compareData) {
                tmp = array[j];
                array[j] = array[i];
                array[i] = tmp;
                i++;
            }
        }
        //将比较的元素和i的位置进行交换
        tmp=array[i];
        array[i]=array[right];
        array[right]=tmp;
        return i;
    }
View Code

 

 

算法题:基于选择排序找第N大的元素

   //将数组分为有序 和 无序两部分。 
    public static int findMaxData2(int[] array,int sortIndex){
        if(array==null || array.length<sortIndex){
            return -1;
        }
        if(array.length==1&&sortIndex==1){
            return array[0];
        }

        int maxData=0;
        int maxIndex=-1;
        int tmp=0;
        //外层控制有序数组的增长
        for(int i=0;i<array.length;i++){
            maxData=array[i];
            maxIndex=i;

            //内层控制,在无序数组中找到最大元素,并扩大有序数组
            for(int j=i;j<array.length;j++){

                if(array[j]>maxData){
                    maxData=array[j];
                    maxIndex=j;
                }
            }
            
           //将从无序数组部分的最大元素加入到有序数据的末尾 
           tmp =array[i];
           array[i]=maxData;
           array[maxIndex] =tmp;
           
           //如果当前有序数组的长度等于了第N大元素的位置,则表示找到了第N大元素
           if(i==sortIndex-1){
               return array[i];
           }
        }
        return -1;
    }
View Code

 

posted on 2020-08-09 17:29  无信不立  阅读(208)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3