小于 n 的最大数

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

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值