文章目录
- 1.问题描述
- 2.难度等级
- 3.热门指数
- 4.解题思路
- 5.实现示例
- 5.1 C++
- 5.2 Golang
- 参考文献
1.问题描述
数组 A 中给定可以使用的 1~9 的数,返回由数组 A 中的元素组成的小于 n 的最大数。
示例 1:A={1, 2, 9, 4},n=2533,返回 2499。
示例 2:A={1, 2, 5, 4},n=2543,返回 2542。
示例 3:A={1, 2, 5, 4},n=2541,返回 2525。
示例 4:A={1, 2, 9, 4},n=2111,返回 1999。
示例 5:A={5, 9},n=5555,返回 999。
2.难度等级
medium。
3.热门指数
★★★★☆
出题公司:字节跳动。
4.解题思路
此题需要用到贪心和回溯。
从高位开始遍历,对每一位先尝试使用相同数字,除了最后一位。如果没有相同的数字时,尝试是否有比当前数字更小的,有的话选更小的数字里最大的,剩下的用最大数字。都没有就向前回溯看前一个有没有更小的。如果一直回溯到第一个数字都没有更小的数字,就用位数更少的全都是最大数字的数。
5.实现示例
5.1 C++
// getMaxDigitLtD 获取小于指定数字的最大数字。
int getMaxDigitLtD(const vector<int> &digits, int digit) {
for (auto it = digits.rbegin(); it != digits.rend(); it++) {
if (*it < digit) {
return *it;
}
}
return 0;
}
// getMaxNumLtN 获取小于 n 的最大数。
int getMaxNumLtN(vector<int> &digits, int n) {
// 获取 n 的每一位数字。
vector<int> ndigits;
while (n > 0) {
ndigits.push_back(n % 10);
n /= 10;
}
// 排序给定的数字数组。
sort(digits.begin(), digits.end());
// 数字写入 map 用于快速查看是否存在。
unordered_set<int> set;
for (auto v : digits) {
set.insert(v);
}
// 目标数的每一位数字。
vector<int> tdigits(ndigits.size());
// 从高位遍历,尽可能地取相同值,除了最后一位。
for (int i = ndigits.size() - 1; i >= 0; i--) {
// 存在相同的数字(最后一位除外)。
if (i > 0 && set.find(ndigits[i]) != set.end()) {
tdigits[i] = ndigits[i];
continue;
}
// 存在小于当前位的最大数字。
auto d = getMaxDigitLtD(digits, ndigits[i]);
if (d > 0) {
tdigits[i] = d;
break;
}
// 需要回溯。
for (i++; i < ndigits.size(); i++) {
tdigits[i] = 0;
// 小于当前位的最大数字。
d = getMaxDigitLtD(digits, ndigits[i]);
if (d > 0) {
tdigits[i] = d;
break;
}
// 最高位也没有小于其的最大数字。
if (i == ndigits.size() - 1) {
tdigits.resize(tdigits.size() - 1);
}
}
break;
}
// 拼装目标数。
int target = 0;
for (int i = tdigits.size() - 1; i >= 0; i--) {
target *= 10;
if (tdigits[i] > 0) {
target += tdigits[i];
continue;
}
// 取数字数组中最大的数字。
target += digits.back();
}
return target;
}
验证代码:
#include <algorithm>
#include <unordered_set>
#include <vector>
#include <iostream>
using namespace std;
int main() {
auto digits = vector<int>{1, 2, 9, 4};
cout << getMaxNumLtN(digits, 2533) << endl;
digits = vector<int>{1, 2, 5, 4};
cout << getMaxNumLtN(digits, 2543) << endl;
digits = vector<int>{1, 2, 5, 4};
cout << getMaxNumLtN(digits, 2541) << endl;
digits = vector<int>{1, 2, 9, 4};
cout << getMaxNumLtN(digits, 2111) << endl;
digits = vector<int>{5, 9};
cout << getMaxNumLtN(digits, 5555) << endl;
}
运行输出:
2499
2542
2525
1999
999
5.2 Golang
// getMaxDigitLtD 获取小于指定数字的数字。
func getMaxDigitLtD(digits []int, digit int) int {
for i := len(digits) - 1; i>=0; i-- {
if digits[i] < digit {
return digits[i]
}
}
return 0
}
// getMaxNumLtN 获取小于 n 的最大数。
func getMaxNumLtN(digits []int, n int) int {
var ndigits []int
// 获取 n 的每一位数字。
for n > 0 {
ndigits = append(ndigits, n%10)
n /= 10
}
// 排序给定的数字数组。
sort.Ints(digits)
// 数字写入 map 用于查看是否存在。
m := make(map[int]struct{})
for _, v := range digits {
m[v] = struct{}{}
}
// 目标数的每一位数字。
tdigits := make([]int, len(ndigits))
// 从高位遍历,尽可能地取相同值,除了最后一位。
for i:= len(ndigits) - 1; i >= 0; i-- {
// 存在相同数字(最后一位除外)。
if _, ok := m[ndigits[i]]; ok && i > 0 {
tdigits[i] = ndigits[i]
continue
}
// 存在小于当前位的最大数字。
if d := getMaxDigitLtD(digits, ndigits[i]); d > 0 {
tdigits[i] = d
break
}
// 回溯
for i++; i < len(ndigits); i++ {
tdigits[i] = 0
if d := getMaxDigitLtD(digits, ndigits[i]); d > 0 {
tdigits[i] = d
break
}
// 最高位也没有小于其的最大数字。
if i == len(ndigits) - 1 {
tdigits = tdigits[:len(tdigits)-1]
}
}
break
}
// 组装目标数。
var target int
for i := len(tdigits) - 1; i >= 0; i-- {
target *=10
if tdigits[i] > 0 {
target += tdigits[i]
continue
}
target += digits[len(digits)-1]
}
return target
}
验证代码:
package main
import (
"fmt"
"sort"
)
func main() {
fmt.Println(getMaxNumLtN([]int{1,2,9,4}, 2533))
fmt.Println(getMaxNumLtN([]int{1,2,5,4}, 2543))
fmt.Println(getMaxNumLtN([]int{1,2,5,4}, 2541))
fmt.Println(getMaxNumLtN([]int{1,2,9,4}, 2111))
fmt.Println(getMaxNumLtN([]int{5,9}, 5555))
}
运行输出:
2499
2542
2525
1999
999
参考文献
小于 n 的最大数 - leetcode