面试算法题
题目
给一个数组nums=[5,4,8,2],给一个n=5416, 让你从nums中选出一些元素,使得组成的数字是小于n的最大数。
代码解析思路
该题使用到的算法主要是贪心、回溯以及二分,首先将nums数组进行排序方便进行二分查找,之后将给定数字n分解成一个列表,注意最高位在列表的最后一位,需要从前往后尽心寻找。
首先判断能取的最大数跟N的位数是否相同,不同直接取数组中的最大值即可。
注意如果出现前面取跟n一样的数,而后面出现没有数可以取的情况需要回溯减小前面的数。
代码
package com.byte_mianshi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Nmin {
private static int ret = 0;
private static boolean over = false;
public static void resolve(int n,int[] data){
Arrays.sort(data);
List<Integer> list = new ArrayList<Integer>();
int n_copy = n;
int tmp = 0;
while (n_copy >0){
tmp = n_copy % 10;
n_copy = n_copy / 10;
list.add(tmp);
}
System.out.println("数据打印开始");
for (int d: list) {
System.out.println(d);
}
System.out.println("数据打印开始");
System.out.println("数组打印开始");
for(int d : data){
System.out.println(d);
}
System.out.println("数组打印完毕");
System.out.println("数据大小:" + list.size());
int minData = 0;
for (int d: list) {
minData = minData * 10 + data[0];
}
System.out.println("最小值"+minData);
if(minData >= n){
//只能取比n少一位
dfs(data,list,n,list.size()-2,0,true);
}else{
dfs(data,list,n,list.size()-1,0,false);
}
System.out.println(ret);
}
/**
*
* @param data 可选数组数据
* @param list 分解后的n值
* @param n
* @param index 当前正在选择的位数
* @param ans 已经选择的数组成的值
* @param flag 是否可以无脑选择最大数的标志位
*/
public static void dfs(int[] data,List<Integer> list,int n,int index,int ans,boolean flag){
int len = list.size();
if(over == true){
return ;
}
if(index < 0){
//选择完毕
if(ans < n){
ret = Math.max(ret,ans);
over = true;
return ;
}
return ;
}
if(flag == false){
//找到最大的不大于list.get(index)的坐标
int f = find(list.get(index) , data);
//当前所有的数都比需要小于的数大,说明前面的数取大了,直接返回
if( f < 0){
return;
}else{
for(int i = f;i>=0;i--){
ans = ans * 10 + data[i];
if(data[i] < list.get(index)){
flag = true;
dfs(data,list,n,index-1,ans,flag);
}else{
//相等的情况
dfs(data,list,n,index-1,ans,flag);
}
ans = (ans - data[i])/10;
}
}
}else{
//说明前面有数字比n对应的数字小,可以一直取最大值
ans = ans * 10 + data[data.length-1];
dfs(data,list,n,index-1,ans,flag);
}
}
/**
* 找到不大于min的第一个序列号
* @param min
* @param data 查找的数组
* @return 数组中第一个小于等于min的序号
*/
public static int find(int min,int[] data){
int left = 0;
int right = data.length-1;
while (left <= right){
int mid = left + ((right - left)>>1);
if( data[mid] <= min){
left = mid + 1;
}else{
right = mid - 1;
}
}
if( right <0 ){
return -1;
}
return right;
}
}
测试案例
public class NminTest {
@Test
public void Test(){
Nmin nmin = new Nmin();
int[] data = new int[] {5,6,7,8};
nmin.resolve(2333,new int[]{2,3});
nmin.resolve(233332,new int[]{2,3});
nmin.resolve(500,new int[]{4,9});
nmin.resolve(5612,new int[] {5,6,7,8});
nmin.resolve(5416,new int[] {5,4,8,2});
nmin.resolve(123434,new int[] {1,4,6,3,8});
nmin.resolve(32,new int[] {2,3});
}
}
输出结果
2332
233323
499
5588
5288
118888
23
Process finished with exit code 0