三部排序

三部排序

题干

题目描述
一般的排序有许多经典算法,如快速排序、希尔排序等。

但实际应用时,经常会或多或少有一些特殊的要求。我们没必要套用那些经典算法,可以根据实际情况建立更好的解法。

比如,对一个整型数组中的数字进行分类排序:

使得负数都靠左端,正数都靠右端,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是一个哨兵往前跑的一个指针。

快速排序之三指针分区法

算法思想

适用于选取主元时,所选主元在数组中有多个相同值。若数组中没有主元的相同值,没有必要用三指针扫描分区法。

当所有数的左边的数据都小于等于这个数,右边的数据都大于这个数时,数组就有序了

三指针分区法

  1. 先初始化主元为首元,然后初始化三个指针:smaller和equal都初始化为下标为数组第二个、bigger初始化为尾元。smaller为扫描指针,每次移动smaller
  2. 当smaller扫描到的数字小于主元,则下标为smaller和equal的需要交换数据,这样就又将小于主元的放在一起了,然后smaller和equal都要自增
    当smaller扫描到的数字等于主元,直接将smaller自增
    当smaller扫描到的数字大于主元,就将小标为smaller和bigger上的数据交换,bigger再自减(和单向扫描分区法处理一样)
  3. 直到smaller已经超过了bigger,这时候(equal-1)指向的是最后一个小于等于主元的数,交换首元与(equal-1)上的数据
  4. 将小于主元和大于主元的两个数组进行快速排序
  5. 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;
	}

时间复杂度

每次处理数组的一半,一共要处理logn次,每次处理要扫描n个数据,所以时间复杂度为O(nlogn)

posted @ 2021-03-10 16:34  记录学习Blog  阅读(184)  评论(0编辑  收藏  举报