三部排序
三部排序
题干
题目描述
一般的排序有许多经典算法,如快速排序、希尔排序等。
但实际应用时,经常会或多或少有一些特殊的要求。我们没必要套用那些经典算法,可以根据实际情况建立更好的解法。
比如,对一个整型数组中的数字进行分类排序:
使得负数都靠左端,正数都靠右端,0在中部。注意问题的特点是:负数区域和正数区域内并不要求有序。可以利用这个特点通过1次线性扫描就结束战斗!!
以下的程序实现了该目标。
static void sort(int[] x)
{
int p = 0;
int left = 0;
int right = x.length-1;
while(p<=right){
if(x[p]<0){
int t = x[left];
x[left] = x[p];
x[p] = t;
left++;
p++;
}
else if(x[p]>0){
int t = x[right];
x[right] = x[p];
x[p] = t;
right--;
}
else{
_________________________; //代码填空位置 p++
}
}
}
如果给定数组:
25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0
则排序后为:
-3,-2,-16,-5,0,0,0,21,19,33,25,16,18,25
请分析代码逻辑,并推测划线处的代码,通过网页提交
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!
代码
public class _05三部排序 {
static void sort(int[] x)
{
int p = 0;
int left = 0;//最小下标
int right = x.length-1;//最大下标
// 猜测是三个指针
while(p<=right){
if(x[p]<0){
//元素小于0将元素和left位置交换
int t = x[left];
x[left] = x[p];
x[p] = t;
left++;
p++;
}
else if(x[p]>0){
// 元素大于0将元素和right位置交换
int t = x[right];
x[right] = x[p];
x[p] = t;
right--;
}
else{//元素等于0
// 不知道是快排的时候就猜测
p++;
// _________________________; //代码填空位置 p++
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] arr = {25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0};
sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
}
总结
1.思路:
①极限思维三个指针全部为0,那么p也得往前走去追right,这样才能不满足while里面的条件,退出循环(p>right)所以p++;
然后再去测试right需不需要动,left需不需要动,分别测试。
多数情况下蓝桥杯的一个横线只有一个语句。
②或者你看上面第一个<0是
left++;
p++;
大于0是
right--;
等于0
应该得挪p了
③p一定要比left要快,要不怎么将其划分为三段呢?
2.验证时候将代码粘过来,然后将横线注释,再去猜测,猜测好了添上然后在main方法里创建数组测试。
3.这个题类似快排的三指针,p是一个哨兵往前跑的一个指针。
快速排序之三指针分区法
算法思想
适用于选取主元时,所选主元在数组中有多个相同值。若数组中没有主元的相同值,没有必要用三指针扫描分区法。
当所有数的左边的数据都小于等于这个数,右边的数据都大于这个数时,数组就有序了
三指针分区法
- 先初始化主元为首元,然后初始化三个指针:smaller和equal都初始化为下标为数组第二个、bigger初始化为尾元。smaller为扫描指针,每次移动smaller
- 当smaller扫描到的数字小于主元,则下标为smaller和equal的需要交换数据,这样就又将小于主元的放在一起了,然后smaller和equal都要自增
当smaller扫描到的数字等于主元,直接将smaller自增
当smaller扫描到的数字大于主元,就将小标为smaller和bigger上的数据交换,bigger再自减(和单向扫描分区法处理一样) - 直到smaller已经超过了bigger,这时候(equal-1)指向的是最后一个小于等于主元的数,交换首元与(equal-1)上的数据
- 将小于主元和大于主元的两个数组进行快速排序
- 32分16,16分8,8分4,4分2,2分1,当左右两边数组的长度为0时,这个数组就已经排好序了
代码
private static void sort(int[] array, int start, int end) {
//初始化三指针
//小于主元的指针和等于主元的指针都初始化为下标为数组第二个
//大于主元的指针初始化为尾元
int smaller = start+1;
int equal = smaller;
int bigger = end;
//设置退出条件
if(smaller > bigger) return ;
//选择主元,一般为首元
int num = array[start];
/*
* 当smaller扫描到的数字小于主元,则下标为smaller和equal的需要交换数据,这样就又将小于主元的放在一起了,然后smaller和equal都要自增
* 当smaller扫描到的数字等于主元,直接将smaller自增
* 当smaller扫描到的数字大于主元,就将小标为smaller和bigger上的数据交换,bigger再自减(和单向扫描分区法处理一样)
*/
while(smaller <= bigger) {
if(array[smaller] < num) {
swap(array, equal, smaller);
equal++;
smaller++;
}
else if(array[smaller] == num) {
smaller++;
}
else if(array[smaller] > num) {
swap(array, smaller, bigger);
bigger--;
}
}
//交换首元与right上的数
swap(array, start, equal-1);
//继续将right两边的数组进行快速排序
sort(array, start, equal-2);
sort(array, bigger+1, end);
}
private static void swap(int[] array, int sc, int r) {
int num = array[sc];
array[sc] = array[r];
array[r] = num;
}
时间复杂度
每次处理数组的一半,一共要处理次,每次处理要扫描个数据,所以时间复杂度为