快速排序
算法思想:
1:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法)
2:然后分别从数组的两端扫描数组,设两个指示标志(lo指向起始位置,hi指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换lo和hi位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换lo和hi位置的值,如此往复循环,直到lo>=hi,然后把基准点的值放到hi这个位置。一次排序就完成了。
快速排序的基本思想就是从一个数组中任意挑选一个元素(通常来说会选择最左边的元素)作为中轴元素,将剩下的元素以中轴元素作为比较的标准,将小于等于中轴元素的放到中轴元素的左边,将大于中轴元素的放到中轴元素的右边。
2:以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。
排序过程:
举例2:
经过一次排序后,可能有如下情况:
/**
* 此时第一次循环比较结束,关键值的位置已经确定了。而且此时start和end指针重合
* 指向的都是我们初始设置的key,此时的顺序分为以下2种情况:
* 1:经过第一次排序 已经排好{1,5,9,12,15,16,20,23,30,45}
* 2:经过第一次排序 前一半和后一半已经排好 但前一半是乱序的和后一半是乱序的
* {1,9,5,12,16,15,20,23,30,45}
* 3:经过第一次排序 前一半和后一半已经排好 前一半是正序递增的和但后一半是乱序的
* {1,5,9,12,16,15,20,23,30,45}
* 4:经过第一次排序 前一半和后一半已经排好 前一半是乱序的和但后一半是正序递增的
* {1,9,5,12,15,16,20,23,30,45}
* 那么针对以上情况,我们进行如下方式处理:
* 下面采用递归的方式分别对前半部分(head--start)和后半部分排序(end---tail)的方式
* 对 从head--key的前一位 和 key的后一位---tail 这2段进行递归性的调用
*/
错误的写法: 如果数组里有重复元素 是不起作用的,
/**
* 快速排序的基本思想就是从一个数组中任意挑选一个元素(通常来说会选择最左边的元素)作为中轴元素,
* 将剩下的元素以中轴元素作为比较的标准,
* 将小于等于中轴元素的放到中轴元素的左边,
* 将大于中轴元素的放到中轴元素的右边。
*/
public class Fast {
public static void main(String[] args) {
int[] a = {12,20,5,16,15,1,30,45,23,9,9,15,16};
quickSort(a,0,a.length-1);
for(int i = 0; i<a.length; i++){
System.out.println(a[i]);
}
}
public static void quickSort(int[] a,int head,int tail){
int start = head;
int end = tail;
//拿第一个元素 当做快排的比较值
int key = a[head];
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
while (start < end){
//先从数组后面的元素 逆序的形式 和比较值key 做比较
/**
* 当最后的那个元素 < key元素时 数组元素不移动
* 如果最后的那个元素 >= key元素时 移动到Key元素的左边
* 继续比较key和倒数第二个元素 以此类推
* (start < end):防止 比如执行到此处 数组是{1,2,2} key=1 a[2]>=1 a[1]>=1 a[0]>=1 此时再执行end--的话 数组会越界
*/
while (a[end] > key){
//移动end下标 让其变成倒数第二个元素 倒数第三个元素 以此类推
end--;
}
/**
* 当最后的那个元素 >= key 数组元素移动到key的左边
* a[start]元素和a[end]元素调换位置 以此类推
*/
if(a[end] < key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//再从数组前面的元素 正序的形式 和比较值key 做比较
/**
* 当最前面的那个元素 <= key时 数组元素不移动
* 继续比较第二个元素和key 以此类推
* (start < end):防止 比如执行到此处 数组是{1,2,2} key=2 a[0]<2 a[1]<=2 a[2]<=2 此时再执行start++的话 数组会越界
*/
while(a[start] < key ){
//移动start下标 让其变成正数第二个元素 正数第三个元素 以此类推
start++;
}
/**
* 如果最前面的那个元素 > key 数组元素移动 将大于key的元素 移动到key的右边 不需要判断等于
* 因为上面 已经将=key的 移动到key的左边了 所以此处不需要再判断=key的元素
* a[start]元素和a[end]元素调换位置 以此类推
*/
if(a[start] > key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
}
/**
* 此时第一次循环比较结束,关键值的位置已经确定了。而且此时start和end指针重合
* 指向的都是我们初始设置的key,此时的顺序分为以下2种情况:
* 1:经过第一次排序 已经排好{1,5,9,12,15,16,20,23,30,45}
* 2:经过第一次排序 前一半和后一半已经排好 但前一半是乱序的和后一半是乱序的
* {1,9,5,12,16,15,20,23,30,45}
* 3:经过第一次排序 前一半和后一半已经排好 前一半是正序递增的和但后一半是乱序的
* {1,5,9,12,16,15,20,23,30,45}
* 4:经过第一次排序 前一半和后一半已经排好 前一半是乱序的和但后一半是正序递增的
* {1,9,5,12,15,16,20,23,30,45}
* 那么针对以上情况,我们进行如下方式处理:
* 下面采用递归的方式分别对前半部分(head--start)和后半部分排序(end---tail)的方式
* 对 从头--key的前一位 和 key的后一位---tail 这2段进行递归性的调用
*/
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
if(head < start-1){
quickSort(a,head,start-1);
}
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
if(end+1 < tail){
quickSort(a,end+1,tail);
}
}
}
打印结果:死循环
正确的写法:
/**
* 快速排序的基本思想就是从一个数组中任意挑选一个元素(通常来说会选择最左边的元素)作为中轴元素,
* 将剩下的元素以中轴元素作为比较的标准,
* 将小于等于中轴元素的放到中轴元素的左边,
* 将大于中轴元素的放到中轴元素的右边。
*/
public class Fast {
public static void main(String[] args) {
int[] a = {12,20,5,16,15,1,30,45,23,9,9,15,16};
quickSort(a,0,a.length-1);
for(int i = 0; i<a.length; i++){
System.out.println(a[i]);
}
}
public static void quickSort(int[] a,int head,int tail){
int start = head;
int end = tail;
//拿第一个元素 当做快排的比较值
int key = a[head];
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
while (start < end){
//先从数组后面的元素 逆序的形式 和比较值key 做比较
/**
* 当最后的那个元素 < key元素时 数组元素不移动
* 如果最后的那个元素 >= key元素时 移动到Key元素的左边
* 继续比较key和倒数第二个元素 以此类推
* (start < end):防止 比如执行到此处 数组是{1,2,2} key=1 a[2]>=1 a[1]>=1 a[0]>=1 此时再执行end--的话 数组会越界
*/
while (a[end] > key && (start < end)){
//移动end下标 让其变成倒数第二个元素 倒数第三个元素 以此类推
end--;
}
/**
* 当最后的那个元素 >= key 数组元素移动到key的左边
* a[start]元素和a[end]元素调换位置 以此类推
*/
if(a[end] <= key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//再从数组前面的元素 正序的形式 和比较值key 做比较
/**
* 当最前面的那个元素 <= key时 数组元素不移动
* 继续比较第二个元素和key 以此类推
* (start < end):防止 比如执行到此处 数组是{1,2,2} key=2 a[0]<2 a[1]<=2 a[2]<=2 此时再执行start++的话 数组会越界
*/
while(a[start] <= key && (start < end)){
//移动start下标 让其变成正数第二个元素 正数第三个元素 以此类推
start++;
}
/**
* 如果最前面的那个元素 > key 数组元素移动 将大于key的元素 移动到key的右边 不需要判断等于
* 因为上面 已经将=key的 移动到key的左边了 所以此处不需要再判断=key的元素
* a[start]元素和a[end]元素调换位置 以此类推
*/
if(a[start] > key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
}
/**
* 此时第一次循环比较结束,关键值的位置已经确定了。而且此时start和end指针重合
* 指向的都是我们初始设置的key,此时的顺序分为以下2种情况:
* 1:经过第一次排序 已经排好{1,5,9,12,15,16,20,23,30,45}
* 2:经过第一次排序 前一半和后一半已经排好 但前一半是乱序的和后一半是乱序的
* {1,9,5,12,16,15,20,23,30,45}
* 3:经过第一次排序 前一半和后一半已经排好 前一半是正序递增的和但后一半是乱序的
* {1,5,9,12,16,15,20,23,30,45}
* 4:经过第一次排序 前一半和后一半已经排好 前一半是乱序的和但后一半是正序递增的
* {1,9,5,12,15,16,20,23,30,45}
* 那么针对以上情况,我们进行如下方式处理:
* 下面采用递归的方式分别对前半部分(head--start)和后半部分排序(end---tail)的方式
* 对 从头--key的前一位 和 key的后一位---tail 这2段进行递归性的调用
*/
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
if(head < start-1){
quickSort(a,head,start-1);
}
//直到start>=end,然后把基准点的值放到hi这个位置。一次排序就完成了
if(end+1 < tail){
quickSort(a,end+1,tail);
}
}
}
输出结果: