螺钉和螺母排序问题:现给定一堆螺钉和螺母(螺母和螺钉一对一唯一配对,即不存在相同大小的螺钉或螺母),现需要对螺母和螺钉按尺寸的大小排序。但是要求是不能直接根据螺钉或螺母尺寸大小进行排序,仅能通过给定的一个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