给定整数N,以字典顺序打印从1到N的数字

时间:2016-08-21 01:30:48

标签: c++ algorithm

我试图按字典顺序打印从1到N的数字,但输出失败。对于下面的输入100,我得到100,但是它被移位并且它与预期的输出不匹配,我的代码中有一个错误但是我无法回溯它。

class Solution {
public:
    vector<int> lexicalOrder(int n) {
         vector<int> result;
        for(int i = 1; i <= 9; i ++){
        int j = 1;
        while( j <= n){
            for(int m = 0; m < j ; ++ m){
                if(m + j * i <= n){

                    result.push_back(m+j*i);
                }
            }
            j *= 10;
        }
    }
    return result;

    }
};



Input:
100
Output:
[1,10,11,12,13,14,15,16,17,18,19,100,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,61,62,63,64,65,66,67,68,69,7,70,71,72,73,74,75,76,77,78,79,8,80,81,82,83,84,85,86,87,88,89,9,90,91,92,93,94,95,96,97,98,99]

Expected:
[1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47

5 个答案:

答案 0 :(得分:3)

考虑何时i=1j=10

会发生什么
for(int m = 0; m < j ; ++ m){
                if(m + j * i <= n){

                    result.push_back(m+j*i);
                }
            }

是的,result将推回10(0 + 10 * 1),11(1 + 10 * 1),12(2 + 10 * 1).. 这是一个解决方案:

#include <iostream>
#include <vector>
#include <string>
std::vector<int> fun(int n)
{
        std::vector<std::string> result;
        for (int i = 1; i <= n; ++i) {
            result.push_back(std::to_string(i));
        }
        std::sort(result.begin(),result.end());
        std::vector<int> ret;
        for (auto i : result) {
            ret.push_back(std::stoi(i));
        }
        return ret;
}
int main(int argc, char *argv[])
{
        std::vector<int> result = fun(100);
        for (auto i : result) {
            std::cout << i << ",";
        }
        std::cout << std::endl;
        return 0;
}

答案 1 :(得分:0)

在输出前3位数字之前,您正在循环显示从1开始的所有2位数字,因此您的方法无法正常工作。

这样做的一种方法是输出基数11中的数字,用前导空格填充到最大位数,在这种情况下3.输出0作为空格,1作为0,2作为1等。拒绝任何在此表示中具有任何非尾随空格的数字,或在解释为基数10时大于n。应该可以一次跳过多个拒绝,但这是不必要的优化。保持输出数字的计数,并在达到n时停止。这将为您提供基数10的词典排序。

使用O(1)空间的示例实现,在您输出第一个数字之前,您不必预先生成所有数字并对其进行排序:

void oneToNLexicographical(int n)
{
    if(n < 1) return;

    // count max digits
    int digits = 1, m = n, max_digit11 = 1, max_digit10 = 1;
    while(m >= 10)
    {
        m /= 10; digits++; max_digit11 *= 11; max_digit10 *= 10;
    }

    int count = 0;
    bool found_n = false;
    // count up starting from max_digit * 2 (first valid value with no leading spaces)
    for(int i = max_digit11 * 2; ; i++)
    {
        int val = 0, trailing_spaces = 0;
        int place_val11 = max_digit11, place_val10 = max_digit10;
        // bool valid_spaces = true;
        for(int d = 0; d < digits; d++)
        {
            int base11digit = (i / place_val11) % 11;
            if(base11digit == 0)
            {
                trailing_spaces++;
                val /= 10;
            }
            else
            {   
                // if we got a non-space after a space, it's invalid
                // if(trailing_spaces > 0)
                // {
                //  valid_spaces = false;
                //  break;  // trailing spaces only
                // }
                val += (base11digit - 1) * place_val10;
            }
            place_val11 /= 11;
            place_val10 /= 10;
        }
        // if(valid_spaces && (val <= n))
        {
            cout << val << ", ";
            count++;
        }
        if(val == n)
        {
            found_n = true;
            i += 10 - (i % 11); // skip to next number with one trailing space
        }

        // skip past invalid numbers:

        // if there are multiple trailing spaces then the next run of numbers will have spaces in the middle - invalid
        if(trailing_spaces > 1)
            i += (int)pow(11, trailing_spaces - 1) - 1;
        // if we have already output the max number, then all remaining numbers
        // with the max number of digits will be greater than n
        else if(found_n && (trailing_spaces == 1))
            i += 10;

        if(count == n)
            break;
    }
}

这会跳过所有无效的数字,因此在输出每个数字之前无需测试valid_spaces

通过执行base11 - &gt;可以删除内部循环。使用差异进行基数10转换,使算法O(N) - 内部while循环倾向于常数:

int val = max_digit10;
for(int i = max_digit11 * 2; ; i++)
{
    int trailing_spaces = 0, pow11 = 1, pow10 = 1;
    int j = i;
    while((j % 11) == 0)
    {
        trailing_spaces++;
        pow11 *= 11;
        pow10 *= 10;
        j /= 11;
    }

    int output_val = val / pow10;       
    if(output_val <= n)
    {
        cout << output_val << ", ";
        count++;
    }
    if(output_val == n)
        found_n = true;

    if(trailing_spaces > 1)
    {
        i += (pow11 / 11) - 1;
    }
    else if(found_n && (trailing_spaces == 1))
    {
        i += 10;
        val += 10;
    }
    else if(trailing_spaces == 0)  
        val++;

    if(count == n)
        break;
}

Demonstration

另一种更简单的方法就是从数字中生成N个字符串并对它们进行排序。

答案 2 :(得分:0)

也许更一般的解决方案?

#include <vector>
#include <algorithm>

using namespace std;

// returns true is i1 < i2 according to lexical order
bool lexicalLess(int i1, int i2) 
{
    int base1 = 1;
    int base2 = 1;
    for (int c = i1/10; c > 0; c/=10) base1 *= 10;
    for (int c = i2/10; c > 0; c/=10) base2 *= 10;
    while (base1 > 0 && base2 > 0) {
        int d1 = i1 / base1;
        int d2 = i2 / base2;
        if (d1 != d2) return (d1 < d2);
        i1 %= base1;
        i2 %= base2;
        base1 /= 10;
        base2 /= 10;
    }
    return (base1 < base2);
}

vector<int> lexicalOrder(int n) {
    vector<int> result;
    for (int i = 1; i <= n; ++i) result.push_back(i);
    sort(result.begin(), result.end(), lexicalLess);
    return result;
}

lexicalLess(...)的另一个想法是在比较之前将整数转换为字符串:

#include <vector>
#include <algorithm>
#include <string>    
#include <boost/lexical_cast.hpp>

using namespace std;

// returns true is i1 < i2 according to lexical order
bool lexicalLess(int i1, int i2) 
{
    string s1 = boost::lexical_cast<string>(i1);
    string s2 = boost::lexical_cast<string>(i2);
    return (s1 , s2);
}

您需要Boost才能运行第二个版本。

答案 3 :(得分:0)

一个简单的实现方法是将数字转换为字符串,它们在算法标题中使用std::sort对字符串数组进行排序,按字典顺序对字符串进行排序,然后再将数字转换为整数

  • 制作一个想要按字典顺序排序的整数向量,将其命名为数字。
  • 制作另一个向量,并在第一个向量中填充数字字符串。将它命名为strs。
  • 对strs数组进行排序。将strs向量的字符串转换为整数并将其放在向量中

列出项目

#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;


string int_to_string(int x){
    string ret;
    while(x > 0){
        ret.push_back('0' + x % 10);
        x /= 10;
    }
    reverse(ret.begin(), ret.end());
    return  ret;
}

int main(){
    vector<int> ints;
    ints.push_back(1);
    ints.push_back(2);
    ints.push_back(100);
    vector<string> strs;
    for(int i = 0; i < ints.size(); i++){
        strs.push_back(int_to_string((ints[i])));
    }
    sort(strs.begin(), strs.end());
    vector<int> sorted_ints;
    for(int i = 0; i < strs.size(); i++){
        sorted_ints.push_back(atoi(strs[i].c_str()));
    }
    for(int i = 0; i < sorted_ints.size(); i++){
        cout<<sorted_ints[i]<<endl;
    }
}

答案 4 :(得分:0)

由于数字从1到n是唯一的,因此可以使用大小为n的一组并将其全部插入其中,然后打印出来。 如果将数字存储为字符串,则set将自动按字典顺序对它们进行排序。 这是简短的代码:

 void lexicographicalOrder(int n){
     set<string> ans;
     for(int i = 1; i <= n; i++)
        ans.insert(to_string(i));
     for(auto ele : ans)
        cout <<ele <<"\n";
 }