从输入的数字中删除k个数字后如何获得最少的数字

时间:2015-01-29 20:03:48

标签: c algorithm data-structures

例如,如果输入的数字为24635,删除任意3位数后的最小数字为23

这与取两个最小的数字不同,因为必须保持数字的顺序。

11 个答案:

答案 0 :(得分:17)

删除k位数意味着保留n - k位数,其中n是总位数。

使用您按升序排序的堆栈。只要您仍然可以将其设为n - k位且当前元素小于堆栈顶部,您就可以从中删除元素:

push(2) => 2
push(4) because 2 < 4 => 24
push(6) because 4 < 6 => 246
pop() because 3 < 6 and we can still end up with 2 digits => 24
pop() for the same reason => 2
push(3) => 23
push(5) => 235

然后只取第一个k数字=&gt; 23。或者你可以确保永远不要超过k位数,然后最终的堆栈就是你的解决方案。

请注意,如果这意味着您将无法构建k位数的解决方案,则无法弹出元素。为此,您需要检查堆栈中当前的元素数以及输入数字上当前位置右侧的位数。

伪代码:

stack = []
for each d in digits:
  while !stack.empty() and d < stack.top() and (*):
    stack.pop()

  if stack.size() < n - k:
    stack.push(d)

 (*) - exercise, fill in the condition that prevents you 
       from popping elements if you still want to be able to get to a solution.
 Hint: count how many elements the for loop went over already
       and see how many are left. Also consider how many you have left in the stack.

由于每个元素最多进入和离开堆栈一次,因此复杂度为O(n)

答案 1 :(得分:3)

提供递归方法。

在每次成功的迭代测试中k == 0 ...
或者失败num == 0,因为没有数字要删除 (返回10比返回num的其他路径更差。)

否则以两种方式递归:
1)保持最低位数并尝试使用高位数 2)删除最低位数并尝试使用高位数k--
返回更好的一个。

unsigned reduce(unsigned num, unsigned k) {
  if (k <= 0) {
    return num;  // Success
  }
  if (num == 0) {
    return 10;  // Fail
  }
  unsigned path1 = reduce(num/10, k)*10 + num%10;
  unsigned path2 = reduce(num/10, k-1);
  return path1 < path2 ? path1 : path2;
}

int main(void) {
  printf("%u\n", reduce(246, 2));
  printf("%u\n", reduce(24635, 3));
  printf("%u\n", reduce(53642, 3));
  printf("%u\n", reduce(21, 1));
}

2
23
32
1

此解决方案不依赖于知道数字位数,只取决于删除所需的数字。

答案 2 :(得分:2)

非常简单的解决方案。

我们有n个数字,我们必须删除k个数字才能离开n-k。我们可以很容易地确定第一个数字是什么。

最终的n-k-1数字显然不能是答案的第一个数字,只是没有足够的数字来制作足够长的数字。

因此,我们只是忽略最后的n-k数字,并专注于第一个k数字,并找到该k个数字集合中的最小数字。这将是我们答案的第一位数字。例如无论X是什么,所有三位数字5XX都小于6XX形式的所有数字。 (如果有一个平局,两位数字分享最小的荣誉,那么选择左边的数字,因为它会为你提供更大的灵活性。)

现在你知道第一个数字是什么,你可以忽略它和左边的一切,并用剩余的数字递归地重复整个事情 - 我可以从剩余的数字中得出的最小数字是多少?

答案 3 :(得分:2)

要解决此问题,您可以按照以下步骤操作-

  • 定义堆栈st,创建一个空字符串ret

  • n:= num的大小

  • 对于i,范围为0到n – 1

  • 而k为非零且堆栈不为空且堆栈顶部> num [i]

  • 从堆栈中删除并将k减1

  • 将num [i]插入st

  • 当k不为0时,从堆栈中删除元素

  • 当堆栈不为空

  • ret:= ret +栈顶,从栈中删除元素

  • 现在反转ret字符串

  • ans:=一个空字符串,而i:= 0

  • 而我

  • 将i增加1

  • 对于我

  • ans:= ans + ret [i]

  • ret:= ans

  • 如果ret的大小为0,则返回“ 0”,否则,返回

C ++解决方案:

class Solution {
public:
   string removeKdigits(string num, int k) {
      stack st;
      string ret = "";
      int n = num.size();
      for(int i = 0; i < n; i++){
         while(k && !st.empty() && st.top() > num[i]){
            st.pop();
            k--;
         }
         st.push(num[i]);
      }
      while(k--)st.pop();
      while(!st.empty()){
         ret += st.top();
         st.pop();
      }
      reverse(ret.begin(), ret.end());
      string ans = "";
      int i = 0;
      while(i <ret.size() && ret[i] == '0')i++;
      for(; i < ret.size(); i++)ans += ret[i];
      ret = ans;
      return ret.size() == 0? "0" : ret;
   }
};

答案 4 :(得分:0)

知道你想保持数字顺序肯定会使这个解决方案更加困难。

我同意IVlad char[]不可行。

我认为这是一个相当不错的解决方案:

#include <stdio.h>
#include <math.h>
#include <limits.h>

#define DIGITS_TO_REMOVE 3 // Assumed to be positive

int recurse(int* foo, int begin, int end, int previous, int max){
    int i;
    int min = begin;

    for (i = begin; i <= end; ++i){
        if (foo[min] > foo[i]){
            min = i;
        }
    }

    return previous * pow(10, max - end + 1) + (max > end ? recurse(foo, min + 1, end + 1, foo[min], max) : foo[min]);
}

int main(void) {
    int foo[(const int)ceil(log10(INT_MAX))];
    int bar = 24635; // Assumed to be larger than pow(10, DIGITS_TO_REMOVE) - 1
    int size = ceil(log10(bar));
    int i;
    int min = size - DIGITS_TO_REMOVE;

    for (i = 1; bar > 0; bar /= 10, ++i){
        foo[size - i] = bar % 10;

        if (i >= DIGITS_TO_REMOVE && foo[size - i] <= foo[min]){
            min = size - i;
        }
    }

    printf("%d", recurse(foo, min + 1, DIGITS_TO_REMOVE + 1, foo[min], size - 1));
    return 0;
}

修改

IVlad还建议我让解决方案返回一个数组而不仅仅是int,因此不会将其约束为返回类型的大小。显然有一些工作需要用于准备输入和输出数组,这可能不是OP的目标,但这是一个有趣的问题。

#include <stdio.h>

#define DIGITS_TO_REMOVE 3 // Assumed to be positive
#define INPUT_SIZE 5 // Assumed to be greater than DIGITS_TO_REMOVE

void recurse(int* input, int* output, int begin, int end){
    int i;
    int min = begin;

    for (i = begin; i < end; ++i){
        if (input[min] > input[i]){
            min = i;
        }
    }
    output[end - DIGITS_TO_REMOVE - 1] = input[min];

    if (end < INPUT_SIZE){
        recurse(input, output, min + 1, end + 1);
    }
}

int main(void) {
    int foo[] = { 2, 4, 6, 3, 5 };
    int bar[INPUT_SIZE - DIGITS_TO_REMOVE];
    int i;

    recurse(foo, bar, 0, DIGITS_TO_REMOVE + 1);

    for (int i = 0; i < INPUT_SIZE - DIGITS_TO_REMOVE; ++i){
        printf("%d", bar[i]);
    }
    return 0;
}

答案 5 :(得分:0)

我认为这会解决问题:

让我们使用532874902352.如果您愿意,可以尝试任何您想要的东西。

使用532874902352,我们将删除4位数字,或任何您想要的数字。

移动4 + 1位数。53287|4902352

找到最小的数字。 2

删除所选数字之前的所有数字。我们现在有28749023532;我们删除了2位数。还有2个。

删除第一个后的2位数字。我们有249023532.我们去。

但是,如果要删除的数字是倒数第二个,请删除最后一个,如果它大于倒数第二个。

示例:

24635,删除3。

移入3 + 1。

2463 | 5

删除所有2之前的最小值。

24635

删除以满足所需的位数,3。 但删除5而不是3。

23

43331,删除3.

移入3 + 1。

4333 | 1

删除所有3之前的最小值。

31

删除以满足所需的位数,3。 我们没有更多数字要删除。

由您来实现此方法。

答案 6 :(得分:0)

private static int getLeastNumberAfterDelete(int num, int dighitsDel) {
 String s = "";
 char[] charArray = String.valueOf(num).toCharArray();
 Arrays.sort(charArray); // Sort charArray
 for (int i = 0; i < (charArray.length - dighitsDel); i++) {
 s += charArray[i];//concatenate 
 }
 return Integer.valueOf(s);
 }

答案 7 :(得分:0)

非常简单的算法让我想起

  1. 将输入数字视为字符串
  2. 从数字9开始向后循环到0
  3. 检查9是否存在给定的数字(在java中我们可以用indexOf()执行),如果存在则删除。检查删除的数字的数量是否等于删除的输入数字的数量。如果没有,检查9存在gaain直到它不存在(indexOf()将在java下返回-1)以查看是否重复相同的数字。现在重复,如果是8,直到预期没有删除数字
  4. 直到现在我们已经删除了数字,这可能会导致更大的数字
  5. 现在从0到9循环,检查数字中是否有数字,并按升序重新排列
  6. 例如: -

    指定的数字为24635,要删除的数字不是2

    1. 从9开始,您会发现要删除的第一个数字是6然后是5
    2. 剩余数量为243
    3. 从0到9的循环重新按照234
    4. 的升序重新排列

答案 8 :(得分:0)

这个想法是基于这样一个事实:第一个(n + 1)个字符中的字符必须在结果数中。因此,我们选择最小的第一个(n + 1)数字并将其放入结果中,然后重复剩余的字符。以下是完整的算法。

  

将结果初始化为空字符串,即res =“”

     

buildLowestNumber(str,n,res)

     

1)如果n == 0,则无需删除任何内容。   将整个'str'附加到'res'并返回

     

2)让'len'为'str'的长度。如果'len'小于或等于   那么一切都可以删除。不附加'res'和   返回

     

3)找到第一个(n + 1)个字符中的最小字符   'STR'。设最小字符的索引是minIndex。附加   'str [minIndex]'到'res'并在minIndex和之后重复出现子串   对于n = n-minIndex

     

buildLowestNumber(str [minIndex + 1..len-1],n-minIndex)。

#include<iostream>
using namespace std;

// A recursive function that removes 'n' characters from 'str'
// to store the smallest possible number in 'res'
void buildLowestNumberRec(string str, int n, string &res)
{
    // If there are 0 characters to remove from str,
    // append everything to result
    if (n == 0)
    {
        res.append(str);
        return;
    }

    int len = str.length();

    // If there are more characters to remove than string
    // length, then append nothing to result
    if (len <= n)
        return;

    // Find the smallest character among first (n+1) characters
    // of str.
    int minIndex = 0;
    for (int i = 1; i<=n ; i++)
        if (str[i] < str[minIndex])
            minIndex = i;

    // Append the smallest character to result
    res.push_back(str[minIndex]);

    // substring starting from minIndex+1 to str.length() - 1.
    string new_str = str.substr(minIndex+1, len-minIndex);

    // Recur for the above substring and n equals to n-minIndex
    buildLowestNumberRec(new_str, n-minIndex, res);
}

// A wrapper over buildLowestNumberRec()
string buildLowestNumber(string str, int n)
{
    string res = "";

    // Note that result is passed by reference
    buildLowestNumberRec(str, n, res);

    return res;
}

// Driver program to test above function
int main()
{
    string str = "121198";
    int n = 2;
    cout << buildLowestNumber(str, n);
    return 0;
}

答案 9 :(得分:0)

#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <functional>
#include <iostream>
#include <utility>
#include <algorithm>
#define mod 1000000007
#define ll long long
using namespace std;
bool Compare (pair<int, int> p1, pair<int, int> p2)  {
    if (p1.first < p2.first) {
        return true;
    }
    return false;
}
int main() {
    priority_queue <pair<int, int>, vector <pair <int, int> >, function<bool (pair <int, int>, pair <int, int>) > > pq (Compare);
    int n, k;
    cin>>n>>k;
    int i = 1;
    while (n) {
        pq.push(make_pair(n%10, i));
        n = n/10;
        i++;
    }
    for(int j =1; j<=k;j++){
        pq.pop();
    }
    int number = pq.top().first;
    int index = pq.top().second;
    int digit = 1;
    pq.pop();
    while(!pq.empty()){
        int current_index = pq.top().second;
        digit = digit * 10;
        if(current_index < index) {
            number = number * 10 + pq.top().first;
        } else {
            number = digit * pq.top().first + number;
        }
        pq.pop();
    }
    cout<<number<<endl;
    return 0;
}

我使用priority_queue和pair解决了这个问题。如果有更好的解决方案,请告诉我

答案 10 :(得分:0)

这个想法是从头开始遍历字符串,并删除比后面的数字大的第一个数字。 如果没有这样的数字,请删除最后一位。

    class Solution {
    private:
        void removeOneDigit(string& s){
            for(int i=0; i<s.length()-1; i++){
                if(s[i+1]<s[i]){
                    s.erase(i,1);
                    return;
                }
            }
            s.erase(s.length()-1,1);
        }
    public:
        string removeKdigits(string num, int k) {
            for(int i=0; i<k; i++){
                removeOneDigit(num);
            }
            int i=0;
            while(num[i]=='0'){
                num.erase(0,1);
            }
            if(num.length()==0){return "0";}
            return num;
        }
    };