原因:最近公司组织了一次算法考试,在规定的时间内(3个小时)没有完成考题,于是潜心研究一下。
题目:给定A、B两个二维数组(列数相等),数组中存放的是二进制数,数组的列代表属性。请找出最小的属性个数以区别A、B两个二维数组的行。(公司原题叙述要夸张很多,如同四六级的阅读理解。本人在此只叙述了题目的梗概。)
举例说明:
数组A属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
0 | 0 | 1 | 1 | 1 |
0 | 0 | 0 | 1 | 1 |
属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 0 |
1 | 0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 | 0 |
数组A、B分别是4行5列的二维数组,从示例数据可以得出,属性3、属性4、属性5即可区别数组A、B中的行(在属性3、4、5列,数组A的第2行数据与第4行数据重复,这个是允许的。在选定属性列的情况下,只要数组A、B中不存在重复数据即可):
属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
0 | 0 | 1 | 1 | 1 |
0 | 0 | 0 | 1 | 1 |
属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 0 |
1 | 0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 | 0 |
数组A、B分别是4行5列的二维数组,从示例数据可以得出,属性1、属性2、属性3无法区别数组A、B中的行,不满足题目要求:
属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
0 | 0 | 1 | 1 | 1 |
0 | 0 | 0 | 1 | 1 |
属性1 | 属性2 | 属性3 | 属性4 | 属性5 |
1 | 0 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 0 |
1 | 0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 | 0 |
答案:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class MainClass {
public static void main(String[] args) {
int N = 4;
int M = 5;
int[][] Input1 = new int[][] { { 1, 0, 1, 1, 0 }, { 1, 0, 0, 1, 1 }, { 0, 0, 1, 1, 1 }, { 0, 0, 0, 1, 1 } };
int[][] Input2 = new int[][] { { 1, 0, 0, 1, 0 }, { 0, 1, 1, 0, 0 }, { 1, 0, 0, 0, 1 }, { 1, 1, 0, 0, 0 } };
List<String> list = new ArrayList<String>();
List<String> temp = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map1 = new HashMap<String, String>();
Map<String, String> map2 = new HashMap<String, String>();
boolean bool = false;
for (int k = 0; k < M; k++) {
if (bool) {
break;
}
if (k == 0) {
for (int i = 0; i < M; i++) {
list.add(i + "");
}
} else {
int size = list.size();
temp.clear();
for (int i = 0; i < size; i++) {
if (list.get(i).replace(",", "").length() == k) {
for (int j = 0; j < M; j++) {
if (!list.get(i).contains(j + "") && judge(list.get(i) + "," + j, temp)) {
temp.add(list.get(i) + "," + j);
// 核心代码
map1.clear();
map2.clear();
String[] arr = (list.get(i) + "," + j).split(",");
for (int y = 0; y < N; y++) {
String key1 = "";
for (int t = 0; t < arr.length; t++) {
key1 = key1 + Input1[y][Integer.parseInt(arr[t])];
}
map1.put(key1, null);
}
for (int y = 0; y < N; y++) {
String key2 = "";
for (int t = 0; t < arr.length; t++) {
key2 = key2 + Input2[y][Integer.parseInt(arr[t])];
}
map2.put(key2, null);
}
map.clear();
for (Entry<String, String> entry : map1.entrySet()) {
map.put(entry.getKey(), null);
}
for (Entry<String, String> entry : map2.entrySet()) {
map.put(entry.getKey(), null);
}
if (map.size() == map1.size() + map2.size()) {
System.out.println("result:" + (k + 1));
bool = true;
}
// 核心代码
}
}
}
}
size = temp.size();
for (int i = 0; i < size; i++) {
list.add(temp.get(i));
}
}
}
}
public static boolean judge(String str, List<String> temp) {
Map<String, String> map = new HashMap<String, String>();
int length = str.split(",").length;
int size = temp.size();
for (int i = 0; i < size; i++) {
map.clear();
for (int j = 0; j < length; j++) {
map.put(str.split(",")[j], null);
}
for (int j = 0; j < temp.get(i).split(",").length; j++) {
map.put(temp.get(i).split(",")[j], null);
}
if (map.size() == length) {
return false;
}
}
return true;
}
}
解题思路:
1. 给定的两个二维数组,列数相等,行数可以不等。该题的考点就是对数组列的组合算法,即将数组的列进行组合,找出最小个数列使得两个数组在最小个数列中的数据不重复。
2. 在确定数组列的情况下比较两个数组对应列的行数据是否重复。
组合的定义:
从n个不同元素中,任取m(m≤n)个元素合并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。
组合的实现:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
String[] arr = { "0", "1", "2", "3", "4" };
int len = arr.length;
// 组合的结果集
List<String> list = new ArrayList<String>();
// 組合:从5个数理选择1个数、2个数、3个数、4个数、5个数
for (int k = 0; k < len; k++) { // k为选择数的个数(编码习惯,没有将k初值设置为1,即k=0表示选择1个数,以此类推)
if (k == 0) { // 选择1个数进行组合的时候
for (int i = 0; i < len; i++) {
list.add(arr[i]);
}
System.out.println(toString(list));
} else { // 选择多个数进行组合的时候
int size = list.size();
for (int i = 0; i < size; i++) {
for (int j = 0; j < len; j++) {
if (list.get(i).replace(",", "").length() == k && !list.get(i).contains(arr[j])) {
if (judge((list.get(i) + "," + arr[j]).split(","), list, k)) {
continue;
}
list.add(list.get(i) + "," + arr[j]);
}
}
}
System.out.println(toString(list));
}
}
}
// 判断新增的组合在组合结果集中是否存在
public static boolean judge(String[] arr, List<String> list, int k) {
Arrays.sort(arr);
int size = list.size();
boolean bool = false;
for (int l = 0; l < size; l++) {
if (list.get(l).replace(",", "").length() == k + 1) {
String[] tmp = list.get(l).split(",");
Arrays.sort(tmp);
if (Arrays.equals(arr, tmp)) {
return true;
}
}
}
return bool;
}
// 字符串格式化(对解题没有意义)
public static String toString(List<String> list) {
StringBuilder sb = new StringBuilder();
int size = list.size();
for (int l = 0; l < size; l++) {
if (sb.length() == 0) {
sb.append(list.get(l));
} else {
sb.append(" " + list.get(l));
}
}
return sb.toString();
}
}
测试结果:
0 1 2 3 4
0 1 2 3 4 0,1 0,2 0,3 0,4 1,2 1,3 1,4 2,3 2,4 3,4
0 1 2 3 4 0,1 0,2 0,3 0,4 1,2 1,3 1,4 2,3 2,4 3,4 0,1,2 0,1,3 0,1,4 0,2,3 0,2,4 0,3,4 1,2,3 1,2,4 1,3,4 2,3,4
0 1 2 3 4 0,1 0,2 0,3 0,4 1,2 1,3 1,4 2,3 2,4 3,4 0,1,2 0,1,3 0,1,4 0,2,3 0,2,4 0,3,4 1,2,3 1,2,4 1,3,4 2,3,4 0,1,2,3 0,1,2,4 0,1,3,4 0,2,3,4 1,2,3,4
0 1 2 3 4 0,1 0,2 0,3 0,4 1,2 1,3 1,4 2,3 2,4 3,4 0,1,2 0,1,3 0,1,4 0,2,3 0,2,4 0,3,4 1,2,3 1,2,4 1,3,4 2,3,4 0,1,2,3 0,1,2,4 0,1,3,4 0,2,3,4 1,2,3,4 0,1,2,3,4
虽然本次题目只涉及到组合的知识,但是,为了知识体系的完整性,顺便给出排列的的定义和实现:
排列的定义:
从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数。
排列的实现:
import java.util.ArrayList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
String[] arr = { "0", "1", "2", "3", "4" };
int len = arr.length;
// 排列的结果集
List<String> list = new ArrayList<String>();
// 排列:从5个数理选择1个数、2个数、3个数、4个数、5个数
for (int k = 0; k < len; k++) { // k为选择数的个数(编码习惯,没有将k初值设置为1,即k=0表示选择1个数,以此类推)
if (k == 0) { // 选择1个数进行排列的时候
for (int i = 0; i < len; i++) {
list.add(arr[i]);
}
System.out.println(list);
} else { // 选择多个数进行排列的时候
int size = list.size();
for (int i = 0; i < size; i++) {
for (int j = 0; j < len; j++) {
if (list.get(i).length() == k && !list.get(i).contains(arr[j])) {
// 去重判断
if (list.contains(list.get(i) + arr[j])) {
continue;
}
list.add(list.get(i) + arr[j]);
// 去重判断
if (list.contains(arr[j] + list.get(i))) {
continue;
}
list.add(arr[j] + list.get(i));
}
}
}
System.out.println(list);
}
}
}
}
总结:本题考查的核心是能否实现组合算法,这也是个人理解。本题可能存在N种解法,目前我只找到此种做法,既然有二进制数,也曾考虑过二进制数移位等算法,可惜囿于天分,尚不能实现。