1. 划分
划分是快速排序的根本机制。划分就是 将一个数组按照指定的值,划分为两部分。左侧小于指定值,右侧大于指定值。
思路:定义一个枢纽值,将指针定位到数组的两侧,相向移动。左侧指针遇到 应在右侧部分的项(即:数据项的值大于枢纽值)时停止,右侧指针遇到 应在左侧部分的项(即:数据项的值小于枢纽值)时停止,交换两个项的位置,重复这一过程,直至两个指针交错而过。
实现:
package com.test;
/**
* 划分
* @author xurongsheng
* @date 2017年4月12日 下午3:29:00
*
*/
public class Partition {
private long[] target;//待排序数据
public Partition(){
}
public Partition(int maxLength){
target = new long[maxLength];
}
public void setTargetValue(int index,long value){
target[index] = value;
}
public long getTargetValue(int index){
return target[index];
}
public void display(){
System.out.print("A=");
for (int i = 0; i < target.length; i++) {
System.out.print(target[i]+" ");
}
System.out.println(" ");
}
/**
* 划分
* 比较次数:N+1 或 N+2
* 交换次数:N/2
* 时间复杂度: O(N)
* @author xurongsheng
* @date 2017年4月12日 下午3:30:59
* @param left 数组的最左侧
* @param right 数组的最右侧
* @param pivot 枢纽值
*/
public int partition(int left,int right,long pivot){
int leftPtr = left - 1; //左侧指针
int rightPtr = right + 1; //右侧指针
while(true){
while(leftPtr < right && target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
<枢纽值的项 ; } while(rightptr>
left && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到 值>枢纽值的项
;
}
if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
break;
}else{
swap(leftPtr,rightPtr);
}
}
return leftPtr;
}
/**
* 交换两个位置上的值
* @author xurongsheng
* @date 2017年4月13日 下午2:50:29
* @param left
* @param right
*/
public void swap(int left,int right){
long temp;
temp = target[left];
target[left] = target[right];
target[right] = temp;
}
public static void main(String[] args) {
int maxSize = 10;
Partition p = new Partition(maxSize);
for (int i = 0; i < maxSize; i++) {
long n = (long) (Math.random()*199);
p.setTargetValue(i,n);
}
p.display();
long pivot = 99;
System.out.print("枢纽值:"+pivot);
int pivotIndex = p.partition(0, p.target.length-1, pivot);
System.out.println(",枢纽所在位置:"+pivotIndex);
p.display();
}
}
效率:
划分的时间复杂度为O(N) ,对于每一次的划分,都会有 N+1 或者 N+2次的比较。最多会有 N/2次的交换。
2. 快速排序
思路:将数组的最后一项作为枢纽值,进行划分,划分完后的 左右两部分,分别递归执行 划分。最终将实现排序
实现:
package com.test;
/**
* 快速排序
* @author xurongsheng
* @date 2017年4月13日 下午2:58:27
*
*/
public class QuickSort {
private long[] target;//待排序数据
public QuickSort(){
}
public QuickSort(int maxLength){
target = new long[maxLength];
}
public void setTargetValue(int index,long value){
target[index] = value;
}
public long getTargetValue(int index){
return target[index];
}
public void display(){
System.out.print("A=");
for (int i = 0; i < target.length; i++) {
System.out.print(target[i]+" ");
}
System.out.println(" ");
}
/**
* 快速排序
* 时间复杂度
* @author xurongsheng
* @date 2017年4月13日 下午3:13:13
*/
public void quickSort(){
recQuickSort(0,target.length-1);
}
public void recQuickSort(int left,int right){
if(right-left <= 0){
return;
}else{
long pivot = target[right]; //最后一项作为 枢纽值
int pivotIndex = partition(left,right,pivot);// 划分并返回枢纽值的最终位置
recQuickSort(left,pivotIndex-1);//将左侧 递归划分
recQuickSort(pivotIndex+1,right);//将右侧 递归划分
}
}
public int partition(int left,int right,long pivot){
int leftPtr = left - 1; //左侧指针
int rightPtr = right; //右侧指针[由于将最后一项作为了枢纽值,所以指针无需再右移]
while(true){
while(target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
<枢纽值的项 ; } while(rightptr>
0 && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到 值>枢纽值的项
;
}
if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
break;
}else{
swap(leftPtr,rightPtr);
}
}
swap(leftPtr,right); //将枢纽值与 划分后的右侧数组首项交换位置
return leftPtr;
}
/**
* 交换两个位置上的值
* @author xurongsheng
* @date 2017年4月13日 下午2:50:29
* @param left
* @param right
*/
public void swap(int left,int right){
long temp;
temp = target[left];
target[left] = target[right];
target[right] = temp;
}
public static void main(String[] args) {
int maxSize = 16;
QuickSort qs = new QuickSort(maxSize);
for (int i = 0; i < maxSize; i++) {
long n = (long) (Math.random()*99);
qs.setTargetValue(i,n);
}
qs.display();
qs.quickSort();
qs.display();
}
}
另一种实现:
package com.test;
/**
* 快速排序[切割版]
* @author xurongsheng
* @date 2017年4月13日 下午4:22:09
*
*/
public class quickSort2 {
private long[] target;//待排序数据
public quickSort2(){
}
public quickSort2(int maxLength){
target = new long[maxLength];
}
public void setTargetValue(int index,long value){
target[index] = value;
}
public long getTargetValue(int index){
return target[index];
}
public void display(){
System.out.print("A=");
for (int i = 0; i < target.length; i++) {
System.out.print(target[i]+" ");
}
System.out.println(" ");
}
/**
* 快速排序
* 时间复杂度
* @author xurongsheng
* @date 2017年4月13日 下午3:13:13
*/
public void quickSort(){
recQuickSort(0,target.length-1);
}
public void recQuickSort(int left,int right){
int size = right-left+1;
if(size < 10){ //数据项小于10个时,执行插入排序
insertionSort(left, right);
}else{
long pivot = medianOf3(left, right); //最左项、中间项、最右项,取中值作为 枢纽值
int pivotIndex = partition(left,right,pivot);// 划分并返回枢纽值的最终位置
recQuickSort(left,pivotIndex-1);//将左侧 递归划分
recQuickSort(pivotIndex+1,right);//将右侧 递归划分
}
}
public long medianOf3(int left,int right){
//将数组的最左侧的项、中间项、最右侧的项,排序
int center = (left + right) / 2;
if(target[left] > target[center]){
swap(left,center);
}
if(target[left] > target[right]){
swap(left,right);
}
if(target[center] > target[right]){
swap(center,right);
}
//中间项即为 枢纽值,将至置换到右侧
swap(center,right-1);
return target[right-1];
}
/**
* 插入排序
* @author xurongsheng
* @date 2017年4月13日 下午4:41:08
* @param left
* @param right
*/
public void insertionSort(int left,int right){
int in,out;
for(out = left+1; out<=right; out++){
long temp = target[out];
in = out;
while(in>left && target[in-1] >= temp){
target[in] = target[in-1];
--in;
}
target[in] = temp;
}
}
public int partition(int left,int right,long pivot){
int leftPtr = left - 1; //左侧指针
int rightPtr = right; //右侧指针[由于将最后一项作为了枢纽值,所以指针无需再右移]
while(true){
while(target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
<枢纽值的项 ; } while(rightptr>
0 && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到 值>枢纽值的项
;
}
if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
break;
}else{
swap(leftPtr,rightPtr);
}
}
swap(leftPtr,right); //将枢纽值与 划分后的右侧数组首项交换位置
return leftPtr;
}
/**
* 交换两个位置上的值
* @author xurongsheng
* @date 2017年4月13日 下午2:50:29
* @param left
* @param right
*/
public void swap(int left,int right){
long temp;
temp = target[left];
target[left] = target[right];
target[right] = temp;
}
public static void main(String[] args) {
int maxSize = 16;
quickSort2 qs = new quickSort2(maxSize);
for (int i = 0; i < maxSize; i++) {
long n = (long) (Math.random()*99);
qs.setTargetValue(i,n);
}
qs.display();
qs.quickSort();
qs.display();
}
}
说明:由于 枢纽的值对于排序的效率影响很大,为了避免出现最右侧的值偏小或者偏大的情况,可以去数组的第一个、最后一个、中间一个,从中选出中间值的项作为枢纽。方法medianOf3 就是选取中间值。这又带来另一个问题,三数据项取中的办法,首先要求数组的项数起码要大于3. 一个可行的办法是 小于3项的 手动排序. 3 就作为了 切割点,将排序方法分为了 手动排序和快速排序两种。 对于小于切割点的小数据,还可以用插入排序来处理。这样效率更高些,切割点也可以设置的高些,建议是9