快速排序之螺钉螺母匹配

螺钉和螺母排序问题:现给定一堆螺钉和螺母(螺母和螺钉一对一唯一配对,即不存在相同大小的螺钉或螺母),现需要对螺母和螺钉按尺寸的大小排序。但是要求是不能直接根据螺钉或螺母尺寸大小进行排序,仅能通过给定的一个match函数比较大小(match()方法比较螺母和螺钉的尺寸大小,0表示两者匹配,1表示螺母尺寸大于螺钉尺寸,-1表示螺母尺寸小于螺钉尺寸)。
给定的螺钉螺母类:

class Nuts_Screws {
	
	class Nuts {
		private int id;//螺母标号
		private int size;//螺母尺寸

		public void setId(int id) {
			this.id = id;
		}

		public void setSize(int size) {
			this.size = size;
		}

		@Override
		public String toString() {
			return "Nuts [id=" + id + ", size=" + size + "]";
		}
	}

	class Screws {
		private int id;//螺钉标号
		private int size;//螺钉尺寸

		public void setId(int id) {
			this.id = id;
		}

		public void setSize(int size) {
			this.size = size;
		}

		@Override
		public String toString() {
			return "Screws [id=" + id + ", size=" + size + "]";
		}
	}
	
	public void print(Nuts[] nuts, Screws[] screws) {
		for (Nuts nut : nuts) {
			System.out.print(nut.size + " ");
		}
		System.out.println();
		for (Screws screw : screws) {
			System.out.print(screw.size + " ");
		}
		System.out.println();
	}
	
	public int MatchNS(Nuts nut, Screws screw) {
		int res = nut.size - screw.size;
		return res > 0 ? 1 : res < 0 ? -1 : 0;
	}
}

基于上述问题,很自然的想到一种暴力的方法,就是用两个指针分别遍历螺钉数组和螺母数组,用螺钉匹配螺母,match返回0就是匹配,但是这样无法排序,因为我们无法获得螺钉之间的大小关系。
我们应该如何让获取螺钉之间的大小关系呢?答案是通过match方法获得,题目给定的唯一条件表明,当我们使用螺钉请匹配螺母的时候,除了获得一个匹配的螺母外,还会获得一堆偏小的螺母,一堆偏大的螺母,这样其实就获得了当前螺母在整个数组中排完序后的位置。想到这里是不是想到一种排序?对的,就是快排,快排的核心操作就是获取当前元素在排序后数组的位置。
通过上述分析,我们用一个螺钉去匹配螺母,可以获取匹配螺母在排序后所在位置,那么接下来该如何处理大的螺母堆和小的螺母堆呢?我们可以尝试故技重施,再选择一个螺钉去匹配大的一堆,这样有可能会选择到小堆的螺母匹配的螺钉,就是说对大堆螺母没有起到效果。
在这里插入图片描述
那我们改如何解决这个问题呢?答案还是快排,既然螺钉匹配螺母可以获取螺母的排序后位置,那么我们也可以采用同样的方法获取螺钉的排序后位置,这样匹配的螺钉和螺母就会将两个数组分别分割成小堆和大堆,那么从小堆取螺钉自然就能对小堆螺母起作用,同理大堆亦然。说到这我们就能明白,这道题使用的是快排的思想,通过间接比较的方法实现快排,不过要注意左右哨兵快排的写法,要注意若选择起始元素作为基准,要让右哨兵先走,不然两个哨兵相遇的地方就是偏小的位置,而不是偏大的位置。
以下是笔者书写的解法:

import java.util.*;

import AtLeisure.Nuts_Screws.Nuts;
import AtLeisure.Nuts_Screws.Screws;

/**
 * 螺钉和螺母排序问题
 * 现给定一堆螺钉和螺母(螺母和螺钉一对一唯一配对),现需要对螺母和螺钉按尺寸的大小排序。
 * 基于给定一个Nuts类和Screws类,螺钉和螺母需要size一样才能匹配。
 * 但是只能使用提供的方法match()方法比较螺母和螺钉的尺寸大小,
 * 0表示两者匹配,1表示螺母尺寸大于螺钉尺寸,-1表示螺母尺寸小于螺钉尺寸
 * @author brooke
 *
 */
public class Nuts_Screws_Match {
	public static void main(String[] args) {
		int num = (int)(Math.random()*100);
//		int num = 10;
		Nuts_Screws nuts_screw = new Nuts_Screws();
		Nuts[] nuts = new Nuts[num];
		Screws[] screws = new Nuts_Screws.Screws[num];
		List<Integer> list_nut = new ArrayList<>();
		List<Integer> list_screw = new ArrayList<>();
		for(int i=1;i<=num;i++) {
			list_nut.add(i);
			list_screw.add(i);
		}
		Collections.shuffle(list_nut);
		Collections.shuffle(list_screw);
		for(int i=0;i<num;i++) {
			nuts[i] = nuts_screw.new Nuts();
			screws[i] = nuts_screw.new Screws();
			nuts[i].setId(i+1);
			nuts[i].setSize(list_nut.get(i));
			screws[i].setId(i+1);
			screws[i].setSize(list_screw.get(i));
		}
		System.out.println("Before sort:");
//		System.out.println(Arrays.toString(nuts));
//		System.out.println(Arrays.toString(screws));
		nuts_screw.print(nuts, screws);
		
		sort_order(nuts, screws, nuts_screw);
		System.out.println("After sort:");
//		System.out.println(Arrays.toString(nuts));
//		System.out.println(Arrays.toString(screws));
		nuts_screw.print(nuts, screws);
	}

	/**
	 * 利用快排的思想,用第一个螺母去匹配螺钉,必然只有一个匹配上为0,其他都是-1或1,
	 * 这样我们就能锁定为0 的一对螺母螺钉在整个数组中的位置,然后调整位置,-1的放左边,1的放右边
	 * 这个时候要调整螺母和螺钉的位置,用螺钉去找螺母的位置,螺母去找螺钉的位置。
	 * 先用螺母找螺钉,然后-1放左边,1放右边。调整好螺钉后,用0的螺钉去调整螺母,螺母也一样调整。剩下就是递归。
	 * @param nuts_screw 
	 * 
	 */
	
	public static void sort_order(Nuts[] nuts, Screws[] screws, Nuts_Screws nuts_screw) {
		int n = nuts.length;
		sort_order(nuts, screws, nuts_screw, 0, n-1);
	}
	
	private static void sort_order(Nuts[] nuts, Screws[] screws, 
			Nuts_Screws nuts_screw, int start, int end) {
		if(start >= end) {
			return ;
		}
		
		int i = start;
		int j = end;
		int mat = i;//记录匹配的螺钉位置
		while(i <= j) {
			while(i <= j && nuts_screw.MatchNS(nuts[start], screws[j]) <= 0) {
				if(nuts_screw.MatchNS(nuts[start], screws[j]) == 0) {
					mat = j;
				}
				j--;
			}
			while(i <= j && nuts_screw.MatchNS(nuts[start], screws[i]) > 0) i++;
			
			if(i <= j) {
				Screws temp = screws[i];
				screws[i] = screws[j];
				screws[j] = temp;
			}
		}
		if(mat != i) {
			Screws temp = screws[i];
			screws[i] = screws[mat];
			screws[mat] = temp;
			mat = i;
		}
		
		i = start;
		j = end;
		int mmat = i;
		while(i <= j) {
			while(i <= j && nuts_screw.MatchNS(nuts[j], screws[mat]) >= 0) {
				if(nuts_screw.MatchNS(nuts[j], screws[mat]) == 0) {
					mmat = j;
				}
				j--;
			}
			while(i <= j && nuts_screw.MatchNS(nuts[i], screws[mat]) < 0) i++;
			
			if(i <= j) {
				Nuts temp = nuts[i];
				nuts[i] = nuts[j];
				nuts[j] = temp;
			}
		}
		if(mmat != i) {
			Nuts temp = nuts[i];
			nuts[i] = nuts[mmat];
			nuts[mmat] = temp;
			mmat = i;
		}
		if(mmat != mat) {
			System.out.println("Error:mat = " + mat + " mmat = " + mmat);
		}

		sort_order(nuts, screws, nuts_screw, start, mat - 1);
		sort_order(nuts, screws, nuts_screw, mat + 1, end);
	}
	
}

随机一次运行结果如下:

Before sort:
21 34 2 23 28 31 16 11 20 25 26 19 4 7 13 33 17 10 35 27 22 3 8 24 5 6 9 14 12 18 1 15 36 32 29 30 
19 1 8 32 20 18 33 14 7 30 3 16 2 23 17 24 13 4 35 12 5 29 22 25 9 26 6 34 31 11 21 10 15 27 36 28 
After sort:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值