排序算法(六):快速排序

目录

一、排序方法:

二、快速排序

三、双路快速排序(代码中解决以上三个问题)

四、三路快速排序(更高速的改进)

五、剑指offer26 最小K个数


一、排序方法:

快速排序,双路快速排序,三路快速排序

二、快速排序

1.实现原理:

  1. 选择一个数作为标定点(如图中的4)
  2. 同下一个元素进行比较(通过i进行遍历,<v放在l和j 之间,>v放在j之后)
  3. 完成遍历后,j左右的元素分别是小于和大于它的元素集合
  4. 最后将j和l处的元素进行交换,完成排序

2.实现代码:

package IMUHERO;
import java.util.*;
public class QuickSort {
    public QuickSort(){}

    public static void sort(Comparable []arr){
        int n=arr.length;
        sort(arr,0,n-1);
    }

    private static void sort(Comparable[]arr,int left,int right){
        if (left>=right)return;

        int middle=parttion(arr,left,right);//parttion的功能是找到当前元素应该放的位置下标
        sort(arr,left,middle);
        sort(arr,middle+1,right);
    }

    private static int parttion(Comparable[]arr,int left,int right){
        //方法中:i是用来知识当前遍历到的元素,最终遍历整个数组
        //v 表示我们选择的元素下标,比如选择第一个元素,那么下标为0
        //j 表示小于v的元素集合的最右边元素
        int v=left;
        Comparable e=arr[v];//存储选择的选择的元素内容
        int j=left;
        //整个排序是:v~j~i~right,其中
        // v+1~j存贮小于e的元素,
        // j+1~i存储大于e的元素,
        // i~right是还没有经过比较的元素
        for (int i=left+1;i<=right;i++){
            if (arr[i].compareTo(e)<0){
                j++;
                swap(arr,i,j);
            }
        }
        swap(arr,v,j);
        return j;
    }

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

}

3.存在的问题:

①.可能出现分配极度不均匀的情况

解决办法,随机选择数组中的一个元素

// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap( arr, l , (int)(Math.random()*(r-l+1))+l ); 

非常多重复元素

造成分割不平衡

退化成O(n^2)

方法:重复元素放在两边:双路快速排序

③性能提升
元素数量较少时可以使用插入排序,提高性能

三、双路快速排序(代码中解决以上三个问题)

1.基本原理

  1. 设置左右右边界j,使用i来进行遍历
  2. <v的元素放在l-i之间,>v的元素放在j-r之间

3.使用while()语句判断e是否<v或者e是否>v,是的话i++或者j++;

不是的话代表这i处的元素>=v,或者j处的元素<=v,在这种情况下交换二者的元素

4.这样可以保证在出现重复元素时,i和j也会有一次随机交换的过程,重复元素分配在两边!

完成交换之后i++,j—进行下一轮循环,直到i>j跳出大循环!

2.实现代码:

package IMUHERO;

import java.util.*;

public class QuickSort2 {

   public  QuickSort2(){ }  //构造方法,什么都不做

    //这个方法是公用的,给用户用
    public static void sort(Comparable []arr){
       int length=arr.length;
       sort(arr,0,length-1);
    }

    //这个方法是私有的,程序员在此设计内部函数
    private static void sort(Comparable []arr,int begin,int end){
//        if(begin>=end)return;           //递归结束条件,bengin和end是同一个元素,也就无需排序
        if(end-begin<=15)
        {InsertionSort.sort(arr,begin,end);
        return;}
       int div=parttion(arr,begin,end);     //找到V应该放在的位置
       sort(arr,begin,div-1);             //对前半部分进行递归排序
       sort(arr,div+1,end);      //对后半部分进行递归排序
    }

    //这个方法用于找到V应该放的位置(从小到大)
    private static int  parttion(Comparable[]arr,int begin,int end){
        //@函数使用:Math.random() 方法生成[0, 1)范围内的double类型随机数;
        // @如无特殊需求,则使用(int)(Math.random()*n)的方式生成随机数即可
        swap(arr,begin,(int)(Math.random()*(end-begin+1))+begin ) ;       //将随机选择的数存入当前数组段的头位置中
        Comparable V=arr[begin];     //V作为标记来比较大小

        int i=begin+1;  //i用于从begin到div向后遍历数组
        int j=end;    //j用于从end到向前遍历数组

        while(true){
            while (i<=end&&arr[i].compareTo(V)<0){      //特别注意,此处应该包含=的情况
                i++;
            }

            while (j>begin&&arr[j].compareTo(V)>0){    //特别注意,此处应该包含=的情况
                j--;
            }

            if (i>j)break;

            swap(arr,i,j);
            i++;
            j--;
        }
        swap(arr,begin,j);              //特别注意,此处应该交换j的值,因为上面swap后i++,j--,此时i指向目标位置的下一个位置,j指向目标位置!
        return j;

    }

    //这个方法用于交换数组中的元素
    private static void swap(Comparable[]arr,int i,int j){
       Comparable temp=arr[i];
       arr[i]=arr[j];
       arr[j]=temp;
    }


}

四、三路快速排序(更高速的改进)

1.基本原理

  1. 三路快排主要用于处理多个重复元素的情况
  2. 声明lt表示左边边界和gt右边边界
  3. Lt-i的区间存放==v的元素,l-lt之间存放<v的元素,gt-r的元素存放>v的元素

4.使用i进行遍历(i总是指向下一个要判断的位置),当e==v时,i++

 

4.当e<v时,swap(lt+1,i);

 i++;

5.同理,当e>v时,swap(i,gt)

Gt--;

注意此时i不能++,因为更换过来的gt还没有经过判断

注意此时i不能++,因为更换过来的gt还没有经过判断

 

6.最后一步:交换v的元素swap(l,lt)

7.递归判断,划分成[l+1…lt]、[gt…r],两个位置

因此前面的计算主要是需要计算出lt和gt的位置

 

 Sort(arr,l,lt)

 Sort(arr,gt,r)

2.实现代码:

package IMUHERO;
import java.util.*;
public class QuickSort3Way {
    public QuickSort3Way(){}

    public static void sort(Comparable[]arr){
        int length=arr.length;
        sort(arr,0,length-1);
    }

    private static void sort(Comparable[]arr,int begin,int end){
        if(end-begin<=15) {
            InsertionSort.sort(arr, begin, end);     //优化方法,当数值小于15的时候使用插入排序
            return;
        }
        int ran=(int)Math.random()*(end-begin)+begin;       //优化方法是,选择随机值作为标定值
        swap(arr,begin,ran);
        Comparable V=arr[begin];    //头元素用于比较
        int lt=begin;       //arr[begin+1...lt]<v
        int gt=end+1;       //arr[gt...end]>v
        int i=begin+1;      //arr[lt+1...i)=v

        while(i<gt){
            if (arr[i].compareTo(V)<0){
                swap(arr,lt+1,i);
                i++;
                lt++;

            }

            if (arr[i].compareTo(V)>0){
                swap(arr,i,gt-1);
                gt--;
            }
            else{
                i++;
            }

        }
        swap(arr,begin,lt);
        //递归调用,排序左右两边的元素
        sort(arr,begin,lt);
        sort(arr,gt,end);
    }

    //这个方法用于交换数组中的元素
    private static void swap(Comparable[]arr,int i,int j){
        Comparable temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}

五、剑指offer26 最小K个数

1.题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
K大于数组长度时,返回null;
/**
*快速排序
*/ 

2.分析

(1)使用partition找到当前选择值对应的下标div

(2)div小于k代表要找的元素div右边,div大于k代表要找的元素div左边

(3)递归判断要找的元素,如果div正好等于k-1(注意题目说的是第k个元素,不是下标为k),就将结果输出到list结果中。

import java.util.ArrayList;
import java.util.List;

public class Solution {
    ArrayList<Integer> list=new ArrayList<>();
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        int len=input.length;
        if(k<=0)return list;
        
        quickSort(input,0,len-1,k);
        return list;
    }

    private void quickSort(int [] input,int start,int end,int k){
        if(start>end)return;
        int div=partition(input,start,end);
        //注意:本题隐含条件是,k>len则返回null
        if(div==k-1){
            //此处直接给list赋值,并返回
            for(int i=0;i<k;i++){
                list.add(input[i]);
            }
            return;
        }
        if(div<k-1){
            quickSort(input,div+1,end,k);
        }
        else{
            quickSort(input,start,div-1,k);
        }
        return;
    }
    //[1,2,3]
    private int partition(int [] input,int start,int end){
        int V = input[start];
        int lt=start+1;
        int rt=end;
        while(lt<=rt){
            while(lt<input.length&&input[lt]<V){
                lt++;
            }
            while(rt>=start&&input[rt]>V){
                rt--;
            }
            //快速排序,此处必须lt<=rt!!!注意
            if (lt<=rt) {
                swap(input, lt, rt);
                lt++;
                rt--;
            }
        }
        swap(input,start,lt-1);
        return lt-1;
    }

    private void swap(int[]arr,int a,int b){
        int tamp=arr[a];
        arr[a]=arr[b];
        arr[b]=tamp;
    }

//    public static void main(String[] args) {
//        Sword29_GetLeastNumbers_Solution s29=new Sword29_GetLeastNumbers_Solution();
//        int input[]={5,3,8,5,3,7,9,1,2,3};
//        List list=s29.GetLeastNumbers_Solution(input,4);
//        System.out.println(list);
//    }
}

注:关于排序的所有代码都放在我的Github上,有需要的同学可以自行Fork,如果觉得还不错,可以打个☆Star哦~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMUHERO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值