从(排序的)数字列表中获取数字范围

时间:2017-08-15 12:53:29

标签: c++ arrays vector

我遇到了以下问题: 考虑有一个递增的数字列表(例如1,2,3,7,8,10), 收集(连续数字)范围并以紧凑的形式打印(如上面给出的数字):

1→3 7-> 8 10

特别是,该解决方案也应该适用于“退化情况”,例如只有一个元素的列表,或者像-2,1这样的列表。

我提出了一个解决方案,我尝试将数字分成不同的向量,但这看起来非常麻烦。所以,我想知道是否有更好的替代方法解决这个问题。

这是我的方法:

vector< vector<int> > result(numbers.size()*numbers.size(), vector<int>());

int last_range;
vector<int> range;
vector<string> result_string;

//1,2,3,7,8,10

if(numbers.size() == 1) 
{
    stringstream ss; 

    ss << numbers[0];

    result_string.push_back(ss.to_string());

    return result_string;

}


for (int i = 1; i < numbers.size(); i++ )
{

    if( numbers[i] - numbers[i-1] == 1 ) 
    {
        range.push_back(numbers[i]);
    }

    if ( i == numbers.size()-1 ) 
    {
        last_range = numbers[i];
        range.push_back(last_range);
    }

    result[i-1].push_back(range);
} 

if( result.size() < 1 )
{
    range.push_back(numbers[0]);

    result[0].push_back(range);

    stringstream ss; 
    ss << result[0][0];

    result_string.push_back(ss.to_string());

    return result_string;
}

//result[0] - > array of numbers  1,2,3 
// result [1] - > array of numbers


for (int i = 0; i < result.size(); i++ )
{
        int start_index = result[i][0];
        int last_index  = result[i][result.size()-1]; 

        stringstream ss_startindex; 
        stringstream ss_endindex; 

        ss_startindex << result[i][0];
        ss_endindex  << result[i][result.size()-1];
        string result = ss_startindex.to_string() + "->" + ss_endindex.to_string();
        result_string.push_back(result);

}       

return result_string;
}

1 个答案:

答案 0 :(得分:0)

你需要的是一个对象,它跨越一个序列来携带上下文('下一个整数是一个范围的一部分?')。因此,使用std::for_each算法作为一元函数的函数对象可以最好地解决这类问题。

一个工作的例子是:

#include <algorithm>
#include <limits>
#include <utility>
#include <vector>

#include <iostream>

using IntCollection = std::vector<int>;

IntCollection is = {1,2,3,7,8,10}, is2 = {-2,1}, is3 = {20};

using Range = std::pair<int,int>;

class IntRangePrinter
{
public:
    void operator() (int i)
    {
        if (r.first == inval)
            r.first = i;
        else if (r.second == inval && r.first + 1 == i)
            r.second = i;
        else if (r.second + 1 == i)
            r.second = i;
        else {
            print_range_or_value();
            // reset after printing
            reset();
            // and re-init range with current value
            r.first = i;
        }
    }

    // There might be "pending" data to be printed
    void flush()
    {
        print_range_or_value();
        std::cout << std::endl;
        reset();
    }

private:
    static int inval; // some value not to be expected in IntCollection
    void print_range_or_value()
    {
        if (r.first != inval && r.second != inval)
            std::cout << ' ' << r.first << "->" << r.second;
        else // if (r.second == inval)
            std::cout << ' ' << r.first;
    }
    void reset ()
    {
        r.first = inval;
        r.second = inval;
    }

    Range r = {inval,inval};

};

int IntRangePrinter::inval = std::numeric_limits<int>::max();

int main () {
    IntRangePrinter p;
    p = std::for_each(is.begin(), is.end(), std::move(p));
    p.flush();

    p = std::for_each(is2.begin(), is2.end(), std::move(p));
    p.flush();

    p = std::for_each(is3.begin(), is3.end(), std::move(p));
    p.flush();

    return 0;
}