一、 快速排序基准位置的选取方法
1.固定位置法
(就是选取的基准是固定的、一般是数组的第一个元素)
这种方法的缺点是当给定的序列本身有序的时候快速排序就退化成了冒泡排序,效率很低;
2.随机选取基准法
import java.util.Arrays;
import java.util.Random;
/**
* @ClassName TestDemo3
* @Description 快速排序,随机选取基准法
* @Author lzq
* @Date 2018/10/27 22:24
* @Version 1.0
**/
public class TestDemo3 {
public static void main(String[] args) {
int[] array = {20,1,9,56,25,32,87,45,12,69,54,19,98,51,25};
quickSort(array);
System.out.println(Arrays.toString(array));
}
/**
* 快速排序
* @param array
*/
public static void quickSort(int[] array) {
if(array == null || array.length == 0) {
return;
}
sort(array,0,array.length-1);
}
/**
* 递归的排序数据
* @param array
* @param start
* @param end
*/
private static void sort(int[] array,int start,int end) {
Random random = new Random();
int x = random.nextInt(end-start+1)+start;//产生在start到end之间的随机数
swap(array,start,x);
int once = once(array,start,end);
if(once > start+1) { //排序左边
sort(array,start,once-1);
}
if(once < end-1) { //排序右边
sort(array,once+1,end);
}
}
/**
* 一趟快速排序
* @param array
* @param start
* @param end
* @return
*/
private static int once(int[] array,int start,int end) {
int tmp = array[start];
while(start < end) {
while(start < end && array[end] >= tmp) {
--end;
}
array[start] = array[end]; //把比基准tmp大的数字移到后面
while(start < end && array[start] <= tmp) {
++start;
}
array[end] = array[start]; //把比基准tmp小的数字移到前面
}
array[start] = tmp; //把基准放在它该放的位置
return start; //返回基准的位置,后面分别在这个基准位置两边重复上面的操作
}
/**
* 将产生的随机下标与start下标交换,而这个新的array[start]会为成为后面比较
* 的基准,这样就达到了,随机选取基准的目的
* @param array
* @param start
* @param end
*/
public static void swap(int[] array,int start,int end) {
int tmp = array[start];
array[start] = array[end];
array[end] = tmp;
}
}
3、三分取基准
这个方法也就是把数组的第一个最后一个和最中间的元素拿出来比较一下,把三者中最大的放最后面,最小的放最中间面,剩下那个元素放前面(原因是无论是哪种取基的方法,最终用的还是固定位置法,也就是还是要拿首部的数据开始进行比较调整!)。然后在用递归的方式比较排序:
import java.util.Arrays;
/**
* @ClassName TestDemo4
* @Description 快速排序——三分取基法
* @Author lzq
* @Date 2018/10/27 22:33
* @Version 1.0
**/
public class TestDemo4 {
public static void main(String[] args) {
int[] array = {20,23,98,54,12,15,0,19,3,46,78,59,84};
System.out.println("原数组"+Arrays.toString(array));
quickSort(array);
System.out.println("排序后数组:"+Arrays.toString(array));
}
/**
* 快速排序
* @param array
*/
public static void quickSort(int[] array) {
if(array == null || array.length == 0) {
return;
}
sort(array,0,array.length-1);
}
/**
* 三分取基
* 调整位置
* 使最大的数据在最后面,最小数据的在中间,剩下的那个在最前面
* @param array
* @param low
* @param high
*/
public static void new_sort(int[] array,int low,int high) {
int mid = low+(high-low)/2;
if(array[mid] > array[low]) {
swap(array,mid,low);
}
if(array[low] > array[high]) {
swap(array,low,high);
}
if(array[mid] > array[high]) {
swap(array,mid,high);
}
System.out.println("三分取基后:"+Arrays.toString(array));
}
/**
* 交换数组元素位置
* @param array
* @param start
* @param end
*/
public static void swap(int [] array ,int start,int end) {
int temp = array[start];
array[start] = array[end];
array[end] = temp;
}
/**
* 递归的排序数据
* @param array
* @param start
* @param end
*/
private static void sort(int[] array,int start,int end) {
new_sort(array,start,end);
int once = once(array,start,end);
if(once > start+1) { //排序左边
sort(array,start,once-1);
}
if(once < end-1) { //排序右边
sort(array,once+1,end);
}
}
/**
* 一趟快速排序
* @param array
* @param start
* @param end
* @return
*/
private static int once(int[] array,int start,int end) {
int tmp = array[start];
while(start < end) {
while(start < end && array[end] >= tmp) {
--end;
}
array[start] = array[end]; //把比基准tmp大的数字移到后面
while(start < end && array[start] <= tmp) {
++start;
}
array[end] = array[start]; //把比基准tmp小的数字移到前面
}
array[start] = tmp; //把基准放在它该放的位置
return start; //返回基准的位置,后面分别在这个基准位置两边重复上面的操作
}
}
运行结果:
原数组[20, 23, 98, 54, 12, 15, 0, 19, 3, 46, 78, 59, 84]
三分取基后:[20, 23, 98, 54, 12, 15, 0, 19, 3, 46, 78, 59, 84]
三分取基后:[3, 19, 0, 15, 12, 20, 54, 98, 23, 46, 78, 59, 84]
三分取基后:[0, 3, 12, 15, 19, 20, 54, 98, 23, 46, 78, 59, 84]
三分取基后:[0, 3, 12, 15, 19, 20, 54, 98, 23, 46, 78, 59, 84]
三分取基后:[0, 3, 12, 15, 19, 20, 54, 98, 23, 46, 78, 59, 84]
三分取基后:[0, 3, 12, 15, 19, 20, 23, 46, 54, 98, 78, 59, 84]
三分取基后:[0, 3, 12, 15, 19, 20, 23, 46, 54, 84, 78, 59, 98]
三分取基后:[0, 3, 12, 15, 19, 20, 23, 46, 54, 59, 78, 84, 98]
排序后数组:[0, 3, 12, 15, 19, 20, 23, 46, 54, 59, 78, 84, 98]
二、优化:
1、当待排序数组当中数据比较少的时候,用直插
import java.util.Arrays;
/**
* @ClassName TestDemo1
* @Description 快速排序的优化
* @Author lzq
* @Date 2018/10/27 21:24
* @Version 1.0
**/
public class TestDemo1 {
public static void main(String[] args) {
int[] array = {20,1,9,56,25,32,87,45,12,69,54,19};
System.out.println(Arrays.toString(array));
quickSort(array);
System.out.println(Arrays.toString(array));
}
/**
* 快速排序
* @param array
*/
public static void quickSort(int[] array) {
if(array == null || array.length == 0) {
return;
}
sort(array,0,array.length-1);
}
/**
* 递归的排序数据
* @param array
* @param start
* @param end
*/
private static void sort(int[] array,int start,int end) {
if(end-start <= 100) {
insert_Sort(array,start,end); //数据不多,用直接插入排序
return;
}else {
int once = once(array, start, end);
if (once > start+1) { //排序左边
sort(array, start, once - 1);
}
if (once < end-1) { //排序右边
sort(array, once + 1, end);
}
}
}
/**
* 给给定区间数据进行插入排序
* @param array
* @param low
* @param high
*/
public static void insert_Sort(int[] array,int low,int high) {
int j = 0,temp = 0;
for(int i = low+1;i <= high;i++) {
temp = array[i];
for(j = i-1;j >= low;j--) {
if(array[j] > temp) {
array[j+1] = array[j];
}else {
break;
}
}
array[j+1] = temp;
}
}
/**
* 一趟快速排序
* @param array
* @param start
* @param end
* @return
*/
private static int once(int[] array,int start,int end) {
int tmp = array[start];
while(start < end) {
while(start < end && array[end] >= tmp) {
--end;
}
array[start] = array[end]; //把比基准tmp大的数字移到后面
while(start < end && array[start] <= tmp) {
++start;
}
array[end] = array[start]; //把比基准tmp小的数字移到前面
}
array[start] = tmp; //把基准放在它该放的位置
return start; //返回基准的位置,后面分别在这个基准位置两边重复上面的操作
}
}
2、聚集相同元素法(基准一样的元素),减少递归的次数
当i和j遍历完后,也就把相同元素聚集完了,
这时,下一次基准的左右两边再次递归排序时,就不是这次基准位置的两边了,而是left的左边和right的右边。
import java.util.Arrays;
/**
* @ClassName TestDemo5
* @Description 聚集相同元素法
* @Author lzq
* @Date 2018/10/27 22:47
* @Version 1.0
**/
public class TestDemo5 {
public static void main(String[] args) {
int[] array = {3,9,10,2,10,10,10,12,11,10,10,13};
quickSort(array);
System.out.println(Arrays.toString(array));
}
/**
* 快速排序
* @param array
*/
private static void quickSort(int[] array) {
if(array == null || array.length == 0) {
return;
}
sort(array,0,array.length-1);
}
/**
* 交换数组中两个元素的位置
* @param array
* @param low
* @param high
*/
public static void swap(int[] array,int low,int high) {
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}
/**
* 一趟快排
* 确定一个基准,把小于基准的数据移到前面去,大于基准的移到后面,确定基准的位置
* @param array
* @param low
* @param high
* @return
*/
public static int partion(int[] array,int low,int high) {
int temp = array[low];
while(low < high) {
while(low < high && array[high] > temp) {
--high;
}
array[low] = array[high];
while(low < high && array[low] <= temp) {
++low;
}
array[high] = array[low];
}
array[low] = temp;
return low;
}
/**
* 递归的排序数据
* @param array
* @param start
* @param end
*/
private static void sort(int[] array,int start,int end) {
int par = partion(array,start,end);
int[] element = focusParNum(array,par,start,end);
if(element[0] > start+1) { //排序左边
sort(array,start,element[0]);
}
if(element[1] < end-1) { //排序右边
sort(array,element[1],end);
}
}
/**
* 聚集相同元素
* @param array
* @param par
* @param start
* @param end
* @return
*/
private static int[] focusParNum(int[] array, int par,int start,int end) {
int left = par-1;
int right = par+1;
/**
* 聚集左边
*/
for(int i = par-1;i >= start;i--) {
if(array[i] == array[par]) {
if(i != left) {
swap(array, left, i);
left--;
}else {
left--;
}
}
}
/**
* 聚集右边
*/
for(int i = par+1;i <= end;i++) {
if(array[i] == array[par]) {
if(i != right) {
swap(array, i, right);
right--;
}else {
right--;
}
}
}
return new int[] {left,right};
}
}
运行结果:
[2, 3, 9, 10, 10, 10, 10, 10, 10, 11, 12, 13]