对目标数进行遍历,以贪心的方式在所给数组nums中找出小于等于当前位并且最大的数。
I.能找到这样的数
①第一种情况为找到这个最大数,且最大数等于当前位的数,则将数压入结果栈中。
②第二种情况为找到这个最大数,且最大数小于当前位的数,则将数压入结果栈,并且后续的位应全部用所给数组nums中的最大元素来作为选择,然后退出最外层循环,返回结果。
II.不能找到这样的数
①出栈回溯前面的位,找到最大且小于当前回溯位数字的元素(而不包括等于当前回溯位数字),如果找到这样的元素,压入结果栈,后续位全部用最大元素填充。
②如果所有栈中的位都出栈,栈为空也没有找到满足①的元素。则放弃在第一位填充数字,后续位全部用最大元素代替。
public class max_n {
/**
* 公司:字节
* 小于n的最大数
* 给定一个数 n,如 23121;给定一组数字 A 如 {2,4,9},求由 A 中元素组成的、小于 n 的最大数,如小于 23121 的最大数为 22999
* 23.5.16 边缘case target=2 nums={2} 结果为无解,因此对最后一位的取值要求必须小于该最后一位
*/
public static void main(String[] args) {
int[]nums=new int[]{2,4,9};
int target=22222;
Arrays.sort(nums);
System.out.println(compute(nums,target));
}
public static String compute(int[]nums,int target ){
String t=String.valueOf(target);
Stack<Integer> stack=new Stack<>();
int len=t.length();
int n=nums.length;
for(int i=0;i<len;i++){
int cur=t.charAt(i)-'0';
int add=max_containsCurNum(nums,cur);
//针对 边缘case target=2 nums={2} 结果为无解
if(i==len-1) add=max_containsNoCurNum(nums,cur);
if(add>=0){
stack.push(add);
if(add<cur){
int j=i+1;
while(j<len){
stack.push(nums[n-1]);
j++;
}
break;
}
}
else{
int j=i-1;
while(j>=0){
int prePop=stack.pop();
int preAdd=max_containsNoCurNum(nums,prePop);
if(preAdd>=0){
stack.add(preAdd);
j++;
while(j<len){
stack.add(nums[n-1]);
j++;
}
break;
}
else{
j--;
}
}
if(stack.isEmpty()){
j=1;
while(j<len){
stack.push(nums[n-1]);
j++;
}
}
break;
}
}
String ret="";
while(!stack.isEmpty()) ret=(char)(stack.pop()+'0')+ret;
return ret;
}
public static int max_containsCurNum(int[]nums,int cur){
int n=nums.length;
int i=0,j=n-1;
int ret=-1;
while(i<=j){
int mid=i+(j-i)/2;
if(nums[mid]<=cur){
ret=nums[mid];
i=mid+1;
}
else{
j=mid-1;
}
}
return ret;
}
public static int max_containsNoCurNum(int[]nums,int cur){
int n=nums.length;
int i=0,j=n-1;
int ret=-1;
while(i<=j){
int mid=i+(j-i)/2;
if(nums[mid]<cur){
ret=nums[mid];
i=mid+1;
}
else{
j=mid-1;
}
}
return ret;
}
}