快速排序法 java实现 三种方法实现

快速排序法

(1)定义

快速排序使用分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小。之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

(2)快速排序的三个步骤

选择基准:在待排序列中,按照某种方式挑出一个元素,作为 "基准"(pivot)

分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大

递归地对两个序列块进行快速排序,直到序列为空或者只有一个元素。

(3)选择基准的方式

对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。

最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列

先介绍第一种快速排序方法:固定基准元素法即固定选取数组块第一个元素为基准元素,进行比较。其中对于注视表明的“第一处”、“第二处”,“第三处”是交换元素的关键,需要详细理解。测试数据分析:如果输入序列是随机的,处理时间可以接受的。如果数组已经有序时,此时的分割就是一个非常不好的分割。因为每次划分只能使待排序序列减一,此时为最坏情况,快速排序沦为冒泡排序,时间复杂度为Θ(n^2)。而且,输入的数据是有序或部分有序的情况是相当常见的。因此,使用第一个元素作为基准元是非常糟糕的,为了避免这个情况,就引入了下面两个获取基准的方法。还有三数取中法和随即基准元法下面继续介绍。

(4)固定基准元

java源代码如下,已经编译成功,可直接复制调试,只要改一下包名和public 类名即可

package test;



class QuickSort{
private int[] data;
QuickSort(int[] data){
this.data=data;
}
public void startSort(int low,int high){
if(low>=high) return;
int partition=Partition(low,high);//以第一个元素为基准元素,大于基准元素的在右边
//小于基准元素的在左边
startSort(low,partition-1);//分治法的体现
startSort(partition+1,high);
}
/*
* 将数组分成两块,返回基准元素在数组中的位置
* @param arr 待排序数组
* @param low,high规定数组中的待排序数据块位置
* */
public int Partition(int low,int high){
int begin=low;
int end=high;
int key=data[low];//取该数组块的第一个元素作为基准元素
while(begin<end){
while(begin<end&&data[end]>=key){
end--;
}
data[begin]=data[end];//第一处,从后开始找到第一个小于key的元素,将这个元素放到begin位置(key已经复制了该位置原来的值,不用担心覆盖问题)
while(begin<end&&data[begin]<=key){
begin++;
}
data[end]=data[begin];//第二处,从前开始往后找到第一个大于key的元素,注意看好begin和end 的大小,将这个元素放到end位置
}
data[begin]=key;//基准元素居中,第三处,将复制的key即原先的data[begin]放到中间位置
return begin;
}
public void display(){
for(int i=0;i<data.length;i++)
System.out.print(data[i]+",");
}
}
public class Solution {
public static void main(String[] args){
int[] num={7,6,5,4,8,10,11};
QuickSort q=new QuickSort(num);
q.startSort(0, num.length-1);
q.display();
}

}

(5)随机基准元

与上述方法唯一的不同在于不是单纯的固定第一个元素作为基准元素,而是先在数组中随机选一个数作为第一个元素,将这个随机选出来的数作为基准元素。

该算法适用于:部分有序的待排序数组

源码如下,改变包名和public类名即可直接使用。

package test;


import java.util.Random;


class QuickSort1{
private int[] data;
QuickSort1(int[] data){
this.data=data;
}
public void startSort(int low,int high){
if(low>=high) return;
//randomSwap();//随机基准元素代替第一个元素
int partition=Partition(low,high);//以第一个元素为基准元素,大于基准元素的在右边
//小于基准元素的在左边
//display();
startSort(low,partition-1);
startSort(partition+1,high);
}
/*
* 将数组分成两块,返回基准元素在数组中的位置
* @param arr 待排序数组
* @param low,high规定数组中的待排序数据块位置
* */
public int Partition(int low,int high){
int begin=low;
int end=high;
int key=data[low];//取该数组块的第一个元素作为基准元素
while(begin<end){
while(begin<end&&data[end]>=key){
end--;
}
data[begin]=data[end];//第一步
while(begin<end&&data[begin]<=key){
begin++;
}
display();
System.out.println();
data[end]=data[begin];//第二步
}
data[begin]=key;//基准元素居中,第三步
return begin;
}
public void display(){
for(int i=0;i<data.length;i++)
System.out.print(data[i]+",");
}
/*
* 随即基准元,将确定好的基准元与第一个数交换,无返回值
* */
public void randomSwap(){
int low=0;
int high=data.length-1;
int randIndex=(int) (Math.random() * (high-low) + low);//随机产生的交换下标
int temp=data[low];//开始交换
data[low]=data[randIndex];
data[randIndex]=temp;
}
}
public class Solutiuon2 {
public static void main(String[] args){
int[] num={4,5,6,7,8,1,2,3,10,11};
QuickSort1 q=new QuickSort1(num);
q.startSort(0, num.length-1);
q.display();
}
}


(6) 三数取中法

引入的原因:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为基准元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为基准元。显然使用三数中值分割法消除了预排序输入的不好情形,并且减少快排大约14%的比较次数。

举例:待排序序列为:8 1 4 9 6 3 5 2 7 0

左边为:8,右边为0,中间为6

我们这里取三个数排序后,中间那个数作为枢轴,则枢轴为6

注意:在选取中轴值时,可以从由左中右三个中选取扩大到五个元素中或者更多元素中选取,一般的,会有(2t+1)平均分区法(median-of-(2t+1),三平均分区法英文为median-of-three。

具体思想:对待排序序列中low、mid、high三个位置上数据进行排序,取他们中间的那个数据作为基准,并用0下标元素存储基准。

源码如下,已经编译过了,直接用即可

package test;


import java.util.Random;


class QuickSort3{
private int[] data;
QuickSort3(int[] data){
this.data=data;
}
public void startSort(int low,int high){
if(low>=high) return;
medianOfThree(low,high);
//randomSwap();//随机基准元素代替第一个元素
int partition=Partition(low,high);//以第一个元素为基准元素,大于基准元素的在右边
//小于基准元素的在左边
//display();
startSort(low,partition-1);
startSort(partition+1,high);
}
/*
* 将数组分成两块,返回基准元素在数组中的位置
* @param arr 待排序数组
* @param low,high规定数组中的待排序数据块位置
* */
public int Partition(int low,int high){
int begin=low;
int end=high;
int key=data[low];//取该数组块的第一个元素作为基准元素
while(begin<end){
while(begin<end&&data[end]>=key){
end--;
}
data[begin]=data[end];//第一步
while(begin<end&&data[begin]<=key){
begin++;
}
display();
System.out.println();
data[end]=data[begin];//第二步
}
data[begin]=key;//基准元素居中,第三步
return begin;
}
public void display(){
for(int i=0;i<data.length;i++)
System.out.print(data[i]+",");
}
/*
*交换两个元素
* */
public void Swap(int n1,int n2){
int temp=data[n1];
data[n1]=data[n2];
data[n2]=temp;
}
/*
* 三数取中
* */
public void medianOfThree(int begin,int end){
int mid=(end-begin)/2+begin;
if(data[mid]>data[end]){
Swap(mid,end);
}
if(data[begin]>data[end]){
Swap(begin,end);
}
if(data[mid]>data[begin]){
Swap(mid,begin);
}
}
}
public class solution3 {
public static void main(String[] args){
int[] num={4,5,6,7,8,1,2,3,10,11};
QuickSort3 q=new QuickSort3(num);
q.startSort(0, num.length-1);
q.display();
}
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值