如何开发一个类来返回指定范围内的唯一数字?

时间:2015-01-27 13:56:19

标签: c++ data-structures

我需要开发一个类来返回指定范围内的唯一数字。例如,如果指定的范围是1-4,则调用GetNextNumber()将按如下方式工作:

MyClass obj = new MyClass();
obj->min = 1;
obj->max = 4;
obj->expirationTime = 30; //seconds

int nextNumber = 0;
nextNumber = obj->GetNextNumber(); // returns 1
nextNumber = obj->GetNextNumber(); // returns 2
nextNumber = obj->GetNextNumber(); // returns 3
nextNumber = obj->GetNextNumber(); // returns 4
nextNumber = obj->GetNextNumber(); // returns -1 or throws an exception, becuase all numbers from 1 to 4 have been used.

obj->ReleaseNumber(2); // now that number 2 has been released, it can be used again
nextNumber = obj->GetNextNumber(); // returns 2
nextNumber = obj->GetNextNumber(); // returns -1 or throws an exception, becuase all numbers from 1 to 4 have been used.

如果我们不对ReleaseNumber()返回的号码拨打GetNextNumber(),则会ReleaseNextExpiredNumber()方法发布并返回该号码。

// after 10 seconds:
int nextExpiredNumber = 0;
nextExpiredNumber = obj->ReleaseNextExpiredNumber(); // returns -1 or throws and exeption because nothing has been expired after 10 seconds
// after 30 seconds
nextExpiredNumber = obj->ReleaseNextExpiredNumber(); // returns 1, because "ReleaseNumber(1)" has not been called within 30 seconds.
// now that number 1 has been released, the `GetNextNumber()` method will return 1:
nextNumber = obj->GetNextNumber(); // returns 1

我将自己开发这样的课程,但在此之前,我想确保我不重新发明轮子。我还需要这个类是线程安全的。谢谢你的任何建议!

3 个答案:

答案 0 :(得分:1)

处理数字只是推动和弹出容器的问题。我选择了deque,因为它在pop_frontpush_back处有效:

class MyClass {
    std::deque<int> numbers;

public:
    MyClass(int lo, int hi) {
        numbers.resize(hi - lo + 1);
        std::iota(numbers.begin(), numbers.end(), lo);
    }

    int getNextNumber() {
        if (!numbers.empty()) {
            int next = numbers[0];
            numbers.pop_front();
            return next;
        }
        else {
            return -1;
        }
    }

    void releaseNumber(int i) {
        numbers.push_back(i);
    }
};

在此基础上添加时间意味着您只有另一个带有时间戳的值容器:

using timestamp = std::chrono::system_clock::time_point; // or whatever
std::vector<std::pair<int, timestamp>> used;

getNextNumber()推到该向量上:

numbers.pop_front();
used.emplace_back(next, std::chrono::system_clock::now());
return next;

releaseNumber()从中删除:

auto it = std::find_if(used.begin(), used.end(), 
    [=](const std::pair<int, timestamp>& p){
        return p.first == i;
    });
used.erase(it);

有了这个,ReleaseNextExpiredNumber()只是检查used.front():如果它已过期,则返回releaseNumber(),否则返回-1。

答案 1 :(得分:1)

首先,由于您使用的是连续的数字序列,因此您只需使用数组将每个数字映射到一段数据:

dataArray[number - min] = whateverData;

其中dataArraymax - min - 1个元素(因为两个边界都包含在内)。

其次,您可以使用简单的bool数组来确定某个数字是否可用(bool[]std::vector<bool>是否有效。)

就跟踪到期而言,您需要使用time函数来获取时间戳,以及一组time_t结构,用于存储每个数字的到期时间。然后,您的ReleaseNextExpiredNumber可能看起来像这样:

time_t curTime = time(NULL); // get current time
for (int num = min; num <= max; num++) // or whatever number type you want
{
    if (available[num - min]) // skip available numbers
        continue;
    if (expirationTimes[num - min] > curTime) // you can compare via difftime
    {
        // release the number
        ReleaseNumber(num);
        return num;
    }
}
// nothing expired
return -1;

GetNextNumber非常简单:

time_t curTime = time();
for (int num = min; num <= max; num++)
{
    if (!available[num - min])) // skip unavailable numbers
        continue;
    // mark as used
    available[num - min] = false;
    // compute expiration time
    expirationTimes[num - min] = curTime + expirationTime; // or whatever function to add seconds to a time_t
    // return it
    return num;
}
// nothing available
return -1;

答案 2 :(得分:0)

这是一个有趣的问题,主要是因为很难维护时间队列和可用数字。我决定分开拿着它们。

此外,如果您期望有大量可用数字,那么各种循环缓冲区可能比我的实现更好。

您有minmax作为公共变量。我不认为你能做到这一点。如果其中一个发生了变化,您可能需要更新所有内容,可能会丢弃所有数字。在我的实施中,我刚刚制作了const

无论如何,这是我想出的:

class MyClass{
public:
    MyClass(int min, int max, int expirationTime) : _min(min), _max(max), _expirationTime(expirationTime), _isAvailable(_max - _min + 1, true) {}

    int getExpirationTime() const { return _expirationTime; }

    void setExpirationTime(int expirationTime){ _expirationTime = expirationTime; }

    int getNextNumber(){
        auto it = find(_isAvailable.begin(), _isAvailable.end(), true);
        auto result = it == _isAvailable.end() ? -1 : distance(_isAvailable.begin(), it) + _min;

        if (result > 0){
            *it = false;
            _fifo.push_back(make_pair(time(nullptr), result));
        }
        return result;
    }

    void releaseNumber(int number){
        assert(number >= _min && number <= _max);
        _isAvailable[number - _min] = true;
        _fifo.erase(find_if(_fifo.begin(), _fifo.end(), [&](const pair<time_t, int> i){ return i.second == number; }));
    }

    int releaseNextExpiredNumber(){
        auto result = -1;

        if(!_fifo.empty() && _fifo.front().first <= time(nullptr) + _expirationTime){
            result = _fifo.front().second;
            _isAvailable[result - _min] = true;
            _fifo.pop_front();
        }
        return result;
    }
private:
    const int _min;
    const int _max;
    int _expirationTime;
    vector<bool> _isAvailable;
    deque<pair<time_t, int>> _fifo;
};