算法题目:小于n的最大数
问题描述:给一个数组nums=[5,4,8,2],给一个n=5416, 让你从nums中选出一些元素,使得组成的数字是小于n的最大数,比如这个例子应该返回5288
思想:贪心+二分
package 补充题目;
import java.util.Arrays;
/**
* description:code
*
* @author xiaozhenzhen001
* @date 2022/6/13
*/
public class 小于n的最大数 {
public static void main(String[] args) {
int[] nums = new int[]{4, 5};
System.out.println(build(4413, nums));
}
public static int build(int num, int[] arr) {
Arrays.sort(arr);
//查找目标最大值为 num-1 or 减一位后的最小值,
String maxStr = getFindMaxNum(num, arr[0]);
char[] res = new char[maxStr.length() - 1];
// 当前位真实数据与【maxStr当前位】比较,影响下一位数据的选择
boolean preIndexLess = false;
// key 贪心算法
for (int i = 0; i < maxStr.length() - 1; i++) {
// 要arr中的哪个位置数据,如果前一位比预期值小,则当前位可以取arr中最大值,否则二分查找
int index = preIndexLess ? arr.length - 1 : search(maxStr, i, arr);
// 赋值
res[i] = (char) (arr[index] + '0');
// 当前位真实数据与当前位比较,影响下一位数据的选择
if (res[i] < maxStr.charAt(i)) {
preIndexLess = true;
}
}
return Integer.parseInt(new String(res));
}
/**
* 获取要查找元素的最大值
* minValue是数组中的最小值
*/
public static String getFindMaxNum(int num, int minValue) {
/**
* 正常目标要找的最大值应是 num-1
* 特殊:但有种情况不满足。这种情况拼不出来当前长度的数字,只能(长度-1)后的最大数
* 如arr=[5, 4](最小值是4),num= 4413(最小值1) 。拼不出来(num-1),因为 4 < 1,所以查找的最大值应是 999 (num长度减一位后的最大值)
*/
boolean flag = false;
String numStr = String.valueOf(num);
for (int i = 0; i < numStr.length(); i++) {
if (minValue > (numStr.charAt(i) - '0')) {
flag = true;
break;
}
}
int maxNum = flag ? (int) (Math.pow(10, (numStr.length() - 1)) - 1) : (num - 1);
String maxStr = String.valueOf(maxNum);
// 加一是为了方便最后一位的运算
return maxStr + maxStr.charAt(maxStr.length() - 1);
}
/**
* key 二分查找
*/
public static int search(String str, int i, int[] arr) {
// 1.确定当前位置的数据,选择的数与下一个数字有关(如,下一位数字比arr的最小值还小,说明只能当前位减一)
int currentMaxNum = str.charAt(i) - '0';
int arrMinNum = arr[0];
int findNum = (str.charAt(i + 1) - '0') >= arrMinNum ? currentMaxNum : currentMaxNum - 1;
// 2.使用二分法进行查找
int left = 0, right = arr.length - 1;
int index = -1;
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (arr[middle] == findNum) {
index = middle;
break;
} else if (arr[middle] < findNum) {
left = middle + 1;
} else {
right = middle - 1;
}
}
// 3.找到就返回middle,否则返回right(arr[right]是比findNum小的最大值)
// -1 表示表示arr中没有比findNum小的值
return (index == (left + ((right - left) >> 1))) ? index : right;
}
}