拍集体照时队形很重要,这里对给定的N个人K排的队形设计排队规则如下:
- 每排人数为N/K(向下取整),多出来的人全部站在最后一排;
- 后排所有人的个子都不比前排任何人矮;
- 每排中最高者站中间(中间位置为m/2+1,其中m为该排人数,除法向下取整);
- 每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧(例如5人身高为190、188、186、175、170,则队形为175、188、190、186、170。这里假设你面对拍照者,所以你的左边是中间人的右边);
- 若多人身高相同,则按名字的字典序升序排列。这里保证无重名。
现给定一组拍照人,请编写程序输出他们的队形。
输入格式:
每个输入包含1个测试用例。每个测试用例第1行给出两个正整数N(<=10000,总人数)和K(<=10,总排数)。随后N行,每行给出一个人的名字(不包含空格、长度不超过8个英文字母)和身高([30, 300]区间内的整数)。
输出格式:
输出拍照的队形。即K排人名,其间以空格分隔,行末不得有多余空格。注意:假设你面对拍照者,后排的人输出在上方,前排输出在下方。
输入样例:
10 3
Tom 188
Mike 170
Eva 168
Tim 160
Joe 190
Ann 168
Bob 175
Nick 186
Amy 160
John 159
输出样例:
Bob Tom Joe Nick
Ann Mike Eva
Tim Amy John
---------------------------------------------------------------------------------------
具体代码实现:
1 package com.hone.basical; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import java.util.Comparator; 6 import java.util.List; 7 import java.util.Scanner; 8 9 /** 10 * 原题目:https://www.patest.cn/contests/pat-b-practise/1055 11 * @author Xia 12 * 可以利用字符串拼接来处理 13 * 而且决定了得先从最后一排开始战队 14 */ 15 16 public class basicalLevel1055groupPic { 17 18 public static void main(String[] args) { 19 Scanner in = new Scanner(System.in); 20 int N = in.nextInt(); //总人数 21 int k = in.nextInt(); //k表示排数 22 23 List<Person> persons = new ArrayList<>(); 24 int rowPeo = N/k; //第一排的人数 25 int lastPeo = N-(k-1)*rowPeo; //最后一排的人数 26 for (int i = 0; i < N; i++) { 27 Person p = new Person(); 28 p.name = in.next(); 29 p.height = in.nextInt(); 30 persons.add(p); 31 } 32 33 Collections.sort(persons,new Person()); 34 35 int index = persons.size()-1; 36 String result = ""; 37 //先从最后一排开始战队 38 int i = k; 39 result = persons.get(index).name; 40 index--; 41 for (int j = 1; j < lastPeo; j++) { 42 if (j%2==1) { 43 result=persons.get(index--).name+" "+result; 44 }else { 45 result=result+" "+persons.get(index--).name; 46 } 47 } 48 System.out.println(result); 49 50 for (int h = k-1; h > 0; h--) { 51 result = persons.get(index--).name; 52 for (int j = 1; j < rowPeo; j++) { 53 if (j%2==1) { 54 result=persons.get(index--).name+" "+result; 55 }else { 56 result=result+" "+persons.get(index--).name; 57 } 58 } 59 System.out.println(result); 60 } 61 } 62 } 63 64 //创建一个person类 并且将所有的人按照身高排序(从小到大) 65 class Person implements Comparator { 66 String name; 67 int height; 68 69 @Override 70 public int compare(Object o1, Object o2) { 71 Person p1 = (Person)o1; 72 Person p2 = (Person)o2; 73 return p1.height-p2.height; 74 } 75 }
从这个实例介绍comparator和comparable的区别
首先给出两者的区别:
1:Comparable 自然排序。(实体类实现)
2:Comparator 是定制排序。(无法修改实体类时,直接在调用方创建)
对于一些普通的数据类型(比如 String, Integer, Double…),它们默认实现了Comparable 接口,实现了 compareTo 方法,我们可以直接使用。可以直接通过Collections.Sort() 或者通过 Arrars.sort()实现对数组的排序。
而对于一些自定义类,它们可能在不同情况下需要实现不同的比较策略,我们可以新创建 Comparator 接口,然后使用特定的 Comparator 实现进行比较。
Comparable 自然排序
Comparable 在 java.lang 包下,是一个接口,内部只有一个方法 compareTo():
Modifier and Type | Method and Description |
---|---|
int |
compareTo(T o)
Compares this object with the specified object for order.
|
阅读API知道在comparable中只有一个方法 compareTo()
compareTo 方法的返回值有三种情况:
- e1.compareTo(e2) > 0 即 e1 > e2
- e1.compareTo(e2) = 0 即 e1 = e2
- e1.compareTo(e2) < 0 即 e1 < e2
实际上所有实现了 Comparable 接口的 Java 核心类的结果都和 equlas 方法保持一致。
实现了 Comparable 接口的 List 或则数组可以使用 Collections.sort()
或者 Arrays.sort()
方法进行排序。
API中有两点要求:
1.由于 null 不是一个类,也不是一个对象,因此在重写 compareTo 方法时应该注意 e.compareTo(null) 的情况,即使 e.equals(null) 返回 false,compareTo 方法也应该主动抛出一个空指针异常 NullPointerException。
2.Comparable 实现类重写 compareTo 方法时一般要求 e1.compareTo(e2) == 0 的结果要和 e1.equals(e2) 一致。这样将来使用 SortedSet 等根据类的自然排序进行排序的集合容器时可以保证保存的数据的顺序和想象中一致。(如果这个条件不满足,则必须使用comparator接口)
Comparator 定制排序
Comparator 在 java.util 包下,也是一个接口,
Modifier and Type | Method and Description |
---|---|
int |
compare(T o1, T o2)
Compares its two arguments for order.
|
boolean |
equals(Object obj)
Indicates whether some other object is "equal to" this comparator.
|
Comparator 是在外部制定排序规则,然后作为排序策略参数传递给某些类,若一个类要实现Comparator接口:
它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。
Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。
使用方式主要分三步:
- 创建一个 Comparator 接口的实现类,并赋值给一个对象 将 Comparator 对象作为参数传递给 排序类的某个方法
- 在 compare 方法中针对自定义类写排序规则
- 向排序类中添加 compare 方法中使用的自定义类
上面的例子中,自定义通过height来自定义排序。
下面借鉴一位博主的代码 http://www.cnblogs.com/skywang12345/p/3324788.html
1 import java.util.*; 2 import java.lang.Comparable; 3 4 /** 5 * @desc "Comparator"和“Comparable”的比较程序。 6 * (01) "Comparable" 7 * 它是一个排序接口,只包含一个函数compareTo()。 8 * 一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort() 或 Collections.sort()进行排序。 9 * (02) "Comparator" 10 * 它是一个比较器接口,包括两个函数:compare() 和 equals()。 11 * 一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。 12 * 13 * 综上所述:Comparable是内部比较器,而Comparator是外部比较器。 14 * 一个类本身实现了Comparable比较器,就意味着它本身支持排序;若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。 15 */ 16 public class CompareComparatorAndComparableTest{ 17 18 public static void main(String[] args) { 19 // 新建ArrayList(动态数组) 20 ArrayList<Person> list = new ArrayList<Person>(); 21 // 添加对象到ArrayList中 22 list.add(new Person("ccc", 20)); 23 list.add(new Person("AAA", 30)); 24 list.add(new Person("bbb", 10)); 25 list.add(new Person("ddd", 40)); 26 27 // 打印list的原始序列 28 System.out.printf("Original sort, list:%s ", list); 29 30 // 对list进行排序 31 // 这里会根据“Person实现的Comparable<String>接口”进行排序,即会根据“name”进行排序 32 Collections.sort(list); 33 System.out.printf("Name sort, list:%s ", list); 34 35 // 通过“比较器(AscAgeComparator)”,对list进行排序 36 // AscAgeComparator的排序方式是:根据“age”的升序排序 37 Collections.sort(list, new AscAgeComparator()); 38 System.out.printf("Asc(age) sort, list:%s ", list); 39 40 // 通过“比较器(DescAgeComparator)”,对list进行排序 41 // DescAgeComparator的排序方式是:根据“age”的降序排序 42 Collections.sort(list, new DescAgeComparator()); 43 System.out.printf("Desc(age) sort, list:%s ", list); 44 45 // 判断两个person是否相等 46 testEquals(); 47 } 48 49 /** 50 * @desc 测试两个Person比较是否相等。 51 * 由于Person实现了equals()函数:若两person的age、name都相等,则认为这两个person相等。 52 * 所以,这里的p1和p2相等。 53 * 54 * TODO:若去掉Person中的equals()函数,则p1不等于p2 55 */ 56 private static void testEquals() { 57 Person p1 = new Person("eee", 100); 58 Person p2 = new Person("eee", 100); 59 if (p1.equals(p2)) { 60 System.out.printf("%s EQUAL %s ", p1, p2); 61 } else { 62 System.out.printf("%s NOT EQUAL %s ", p1, p2); 63 } 64 } 65 66 /** 67 * @desc Person类。 68 * Person实现了Comparable接口,这意味着Person本身支持排序 69 */ 70 private static class Person implements Comparable<Person>{ 71 int age; 72 String name; 73 74 public Person(String name, int age) { 75 this.name = name; 76 this.age = age; 77 } 78 79 public String getName() { 80 return name; 81 } 82 83 public int getAge() { 84 return age; 85 } 86 87 public String toString() { 88 return name + " - " +age; 89 } 90 91 /** 92 * 比较两个Person是否相等:若它们的name和age都相等,则认为它们相等 93 */ 94 boolean equals(Person person) { 95 if (this.age == person.age && this.name == person.name) 96 return true; 97 return false; 98 } 99 100 /** 101 * @desc 实现 “Comparable<String>” 的接口,即重写compareTo<T t>函数。 102 * 这里是通过“person的名字”进行比较的 103 */ 104 @Override 105 public int compareTo(Person person) { 106 return name.compareTo(person.name); 107 //return this.name - person.name; 108 } 109 } 110 111 /** 112 * @desc AscAgeComparator比较器 113 * 它是“Person的age的升序比较器” 114 */ 115 private static class AscAgeComparator implements Comparator<Person> { 116 117 @Override 118 public int compare(Person p1, Person p2) { 119 return p1.getAge() - p2.getAge(); 120 } 121 } 122 123 /** 124 * @desc DescAgeComparator比较器 125 * 它是“Person的age的升序比较器” 126 */ 127 private static class DescAgeComparator implements Comparator<Person> { 128 129 @Override 130 public int compare(Person p1, Person p2) { 131 return p2.getAge() - p1.getAge(); 132 } 133 } 134 135 }
下面对这个程序进行说明。
a) Person类定义。如下:
private static class Person implements Comparable<Person>{ int age; String name; ... /** * @desc 实现 “Comparable<String>” 的接口,即重写compareTo<T t>函数。 * 这里是通过“person的名字”进行比较的 */ @Override public int compareTo(Person person) { return name.compareTo(person.name); //return this.name - person.name; } }
说明:
(01) Person类代表一个人,Persong类中有两个属性:age(年纪) 和 name“人名”。
(02) Person类实现了Comparable接口,因此它能被排序。
b) 在main()中,我们创建了Person的List数组(list)。如下:
// 新建ArrayList(动态数组) ArrayList<Person> list = new ArrayList<Person>(); // 添加对象到ArrayList中 list.add(new Person("ccc", 20)); list.add(new Person("AAA", 30)); list.add(new Person("bbb", 10)); list.add(new Person("ddd", 40));
c) 然后,我们通过Collections的sort()函数,对list进行排序。
由于Person实现了Comparable接口,因此通过sort()排序时,会根据Person支持的排序方式,即 compareTo(Person person) 所定义的规则进行排序。如下:
1 // 对list进行排序 2 // 这里会根据“Person实现的Comparable<String>接口”进行排序,即会根据“name”进行排序 3 Collections.sort(list); 4 System.out.printf("Name sort, list:%s ", list);
e) 对比Comparable和Comparator
我们定义了两个比较器 AscAgeComparator 和 DescAgeComparator,来分别对Person进行 升序 和 降低 排序。
e.1) AscAgeComparator比较器
它是将Person按照age进行升序排序。代码如下:
1 /** 2 * @desc AscAgeComparator比较器 3 * 它是“Person的age的升序比较器” 4 */ 5 private static class AscAgeComparator implements Comparator<Person> { 6 7 @Override 8 public int compare(Person p1, Person p2) { 9 return p1.getAge() - p2.getAge(); 10 } 11 }
e.2) DescAgeComparator比较器
它是将Person按照age进行降序排序。代码如下:
1 /** 2 * @desc DescAgeComparator比较器 3 * 它是“Person的age的升序比较器” 4 */ 5 private static class DescAgeComparator implements Comparator<Person> { 6 7 @Override 8 public int compare(Person p1, Person p2) { 9 return p2.getAge() - p1.getAge(); 10 } 11 }