说是一面试题,原题如下:
给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】
举一个例子,
数值: 0,1,2,3,4,5,6,7,8,9
结果: 6,2,1,0,0,0,1,0,0,0
我这里做一个扩展,给出上排一组数字,得出下排对应个数的数字,要求下排每个数都是上排每个数在下排出现的次数。
解题:
1、下排所有的数字之和等于所有数字的个数,因为是出现的次数,且每个数都 >= 0;
2、当上排数字没有0时,下排所有数字都为 0 。是唯一的解法(目前还没有找到更多的解法);
3、不是所有的一排数字都有解
当然还有其它特点,根据这几个特点,写了个解法:
package com.jandmin.demo.leetcode;
import com.alibaba.fastjson.JSON;
/**
* @description:
* 根据上排给出的一排数字,在其下排填出对应的个数的数字,
* 要求下排每个数都是先前上排每个数在下排出现的次数。
* @author: JandMin
**/
public class BottomIsUpTimes {
/**
* 初始化数组:不可重复
* @param len 数组长度
* @param begin 数组的初始值
* @return
*/
private static int[] init(int len,int begin) {
int[] up = new int[len];
for(int i=0; i<len; i++){
up[i] = begin + i;
}
return up;
}
public static void main(String[] args) {
// 数组最大长度,这里为了测试,多用了几个
int length = 11;
// 数组的初始值:可以是负数
int begin = 0;
for(int len=2; len < length; len++ ) {
// 初始化数组
int[] up = init(len, begin);
System.out.println(" begin:" + JSON.toJSONString(up));
// 假设初始结果都是 0
int[] bottom = new int[len];
// 根据前一个数组计算结果
int[] result = countUpArrayTimes(up, bottom, len);
System.out.println("result:" + JSON.toJSONString(result));
System.out.println();
}
}
/**
* 计算上一个数组在下一个数组中出现的次数
* @param upArray 已知的前一个数组
* @param bottom 结果数组
* @param len 数组长度
* @return
*/
private static int[] countUpArrayTimes(int[] upArray, int[] bottom, int len) {
// 获取 0 的位置
Integer zeroIndex = getIndex(upArray, len,0);
if(null == zeroIndex){
// 如果数组中不存在0,那结果数组的所有元素都为0
return bottom;
}
// 从 0 开始计算,假设所有都是 0,然后以 0 的数量依次减少来遍历
int num = count(bottom, 0, len);
for (int n=num; n>0; n--) {
int[] temp = bottom.clone();
temp[zeroIndex] = n;
// n 在数组中的位置对应在 结果集中出现的次数
temp = update(upArray, temp, n, len,0);
if (null != temp) {
return temp;
}
}
return null;
}
/**
* 根据 num 在数组中的位置,修改结果集中对应的数量
* @param array 初始数组
* @param result 结果数组
* @param num 数组中的元素
* @param len 长度
* @param count 统计循环的次数,防止死循环,如果已经遍历超过了 len 说明次数不符合要求,重新设置 0 的次数
* @return 返回结果,不符合要求则直接返回 null,重新新一轮的计算
*/
private static int[] update(int[] array, int[] result, int num, int len,int count) {
for (int i=0; i<len; i++){
if(array[i] == num){
int times = count(result, num, len);
// 如果num出现的次数正好等于当前元素,次数加1
if(times == num){
times += 1;
}
result[i] = times;
// 结果数组所以值的和 等于数组长度
int sum = sum(result);
if(sum > len) {
return null;
}
// 如果已经符合则返回
if(check(array,result,len) && sum == len){
return result;
}
// 防止死循环
if(count > len){
return null;
}
// 继续对新出现的数字进行统计
return update(array,result,times,len,++count);
}
}
return null;
}
/**
* 检查结果数组是否满足条件:要求结果集中的每个数都是原数组每个数在结果集中出现的次数
* @param array 原数组
* @param result 结果集
* @param len 长度
* @return
*/
private static boolean check(int[] array, int[] result,int len) {
for (int i=0; i<len; i++){
int n = count(result, array[i], len);
if(n != result[i]){
return false;
}
}
return true;
}
/**
* 统计结果数组的和
* @param array
* @return
*/
private static int sum(int[] array) {
int sum = 0;
for(int arr : array){
sum += arr;
}
return sum;
}
/**
* 计算数组元素在结果集中出现的次数
* @param result 结果集
* @param num 数组元素
* @param len 数组长度
* @return
*/
private static int count(int[] result,int num, int len) {
int count = 0;
for (int j=0; j<len; j++){
if(num == result[j]){
count++;
}
}
return count;
}
/**
* 获取元素在数组中的位置,如果不存在返回 null
* @param array 数组
* @param len 长度
* @param num 数组元素
* @return
*/
private static Integer getIndex(int[] array, int len, int num) {
for (int i=0; i<len; i++){
if(num == array[i]){
return i;
}
}
return null;
}
}
打印结果如下:
begin:[0,1]
result:null
begin:[0,1,2]
result:null
begin:[0,1,2,3]
result:[1,2,1,0]
begin:[0,1,2,3,4]
result:null
begin:[0,1,2,3,4,5]
result:null
begin:[0,1,2,3,4,5,6]
result:[3,2,1,1,0,0,0]
begin:[0,1,2,3,4,5,6,7]
result:[4,2,1,0,1,0,0,0]
begin:[0,1,2,3,4,5,6,7,8]
result:[5,2,1,0,0,1,0,0,0]
begin:[0,1,2,3,4,5,6,7,8,9]
result:[6,2,1,0,0,0,1,0,0,0]