30

如何实现一个循环列表,当它已满时覆盖最旧的条目?

对于一点背景,我想在 GWT 中使用循环列表;所以使用 3rd 方库不是我想要的。

4

8 回答 8

46

一个非常简单的实现,用 C 表示。实现一个循环缓冲区样式的 FIFO 队列。可以通过创建一个包含队列大小、队列数据和队列索引(输入和输出)的结构来使其更通用,这些索引将与数据一起传入以添加或从队列中删除。然后这些相同的例程可以处理多个队列。另请注意,这允许任何大小的队列,但如果您使用 2 的幂并进一步自定义代码,则可以使用加速。

/* Very simple queue
 * These are FIFO queues which discard the new data when full.
 *
 * Queue is empty when in == out.
 * If in != out, then 
 *  - items are placed into in before incrementing in
 *  - items are removed from out before incrementing out
 * Queue is full when in == (out-1 + QUEUE_SIZE) % QUEUE_SIZE;
 *
 * The queue will hold QUEUE_ELEMENTS number of items before the
 * calls to QueuePut fail.
 */

/* Queue structure */
#define QUEUE_ELEMENTS 100
#define QUEUE_SIZE (QUEUE_ELEMENTS + 1)
int Queue[QUEUE_SIZE];
int QueueIn, QueueOut;

void QueueInit(void)
{
    QueueIn = QueueOut = 0;
}

int QueuePut(int new)
{
    if(QueueIn == (( QueueOut - 1 + QUEUE_SIZE) % QUEUE_SIZE))
    {
        return -1; /* Queue Full*/
    }

    Queue[QueueIn] = new;

    QueueIn = (QueueIn + 1) % QUEUE_SIZE;

    return 0; // No errors
}

int QueueGet(int *old)
{
    if(QueueIn == QueueOut)
    {
        return -1; /* Queue Empty - nothing to get*/
    }

    *old = Queue[QueueOut];

    QueueOut = (QueueOut + 1) % QUEUE_SIZE;

    return 0; // No errors
}
于 2008-10-18T21:12:55.863 回答
2

使用链表。为头部和尾部保持单独的指针。从列表的头部弹出,推到尾部。如果你想要它是圆形的,只要确保新的尾巴总是指向头部。

我可以理解为什么您可能想要使用链表来实现 FIFO,但为什么要让它成为一个循环列表呢?

于 2008-10-18T21:12:22.013 回答
2

如果你想要一个固定长度的循环列表。您可以使用(动态)数组。使用两个变量进行管理。一个用于下一个元素的位置,一个用于计算元素的数量。

放置:将元素放在空闲的地方。移动位置(模长度)。将 1 添加到计数,除非计数等于列表的长度。获取:仅当计数>0 时。将位置向左移动(模长度)。减少计数。

于 2008-10-18T21:16:08.947 回答
1

使用数组并在第一个可用位置保留变量 P。

每次添加新元素时增加 P。

要知道数组中 P 的等效索引,请执行 (P % n) 其中 n 是数组的大小。

于 2008-10-18T21:12:48.923 回答
1

我将它用于我的微控制器。为简化代码,一个字节将不填。Aka size - 1 实际上是全部容量。

fifo_t* createFifoToHeap(size_t size)
{
    byte_t* buffer = (byte_t*)malloc(size);

    if (buffer == NULL)
        return NULL;

    fifo_t* fifo = (fifo_t*)malloc(sizeof(fifo_t));

    if (fifo == NULL)
    {
       free(buffer);
       return NULL;
    }

    fifo->buffer = buffer;
    fifo->head = 0;
    fifo->tail = 0;
    fifo->size = size;

    return fifo;
}

#define CHECK_FIFO_NULL(fifo) MAC_FUNC(if (fifo == NULL) return 0;)

size_t fifoPushByte(fifo_t* fifo, byte_t byte)
{
    CHECK_FIFO_NULL(fifo);

    if (fifoIsFull(fifo) == true)
       return 0;

    fifo->buffer[fifo->head] = byte;

    fifo->head++;
    if (fifo->head == fifo->size)
       fifo->head = 0;

    return 1;
}

size_t fifoPushBytes(fifo_t* fifo, byte_t* bytes, size_t count)
{
    CHECK_FIFO_NULL(fifo);

    for (uint32_t i = 0; i < count; i++)
    {
        if (fifoPushByte(fifo, bytes[i]) == 0)
            return i;
    }

    return count;
}

size_t fifoPopByte(fifo_t* fifo, byte_t* byte)
{
    CHECK_FIFO_NULL(fifo);

    if (fifoIsEmpty(fifo) == true)
        return 0;

    *byte = fifo->buffer[fifo->tail];

    fifo->tail++;
    if (fifo->tail == fifo->size)
        fifo->tail = 0;

    return 1;
}

size_t fifoPopBytes(fifo_t* fifo, byte_t* bytes, size_t count)
{
    CHECK_FIFO_NULL(fifo);

    for (uint32_t i = 0; i < count; i++)
    {
        if (fifoPopByte(fifo, bytes + i) == 0)
            return i;
    }

    return count;
}

bool fifoIsFull(fifo_t* fifo)
{
    if ((fifo->head == (fifo->size - 1) && fifo->tail == 0) || (fifo->head == (fifo->tail - 1)))
        return true;
    else
        return false;
}

bool fifoIsEmpty(fifo_t* fifo)
{
    if (fifo->head == fifo->tail)
        return true;
    else
        return false;
}

size_t fifoBytesFilled(fifo_t* fifo)
{
    if (fifo->head == fifo->tail)
        return 0;
    else if ((fifo->head == (fifo->size - 1) && fifo->tail == 0) || (fifo->head == (fifo->tail - 1)))
        return fifo->size;
    else if (fifo->head < fifo->tail)
        return (fifo->head) + (fifo->size - fifo->tail);
    else
        return fifo->head - fifo->tail; 
}
于 2013-10-31T11:04:50.313 回答
0

我不认为队列是制作缓存的最佳方式。你想成为你的缓存真的很快!除非您希望缓存非常小或内存非常有限,否则对队列进行线性扫描不是可行的方法。

假设您不想要一个非常小的缓存或一个缓慢的缓存,使用具有值的哈希映射的链表到链表中的节点是一个不错的方法。您总是可以逐出头部,并且每当访问一个元素时,您都可以将其移除并将其放在列表的头部。对于访问,您可以直接获取它或检查它是否在 O(1) 中的缓存中。驱逐一个元素也是 O(1),更新列表也是如此。

例如,查看 Java 中的 LinkedHashMap。

http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html

于 2013-11-04T04:24:19.817 回答
0

这是循环缓冲区 (FIFO) 的简单模板解决方案。它确实使一个存储空间空着,但我认为这对性能和简单性来说是一个小小的损失。我包括了一个简单的压力测试。

#include <iostream>
#include <string>

using namespace std;

class E: public std::exception {

    const char *_msg;
    E(){}; //no default constructor

public:

    explicit E(const char *msg) throw(): _msg(msg) {};
    const char * what() const throw() {return(_msg);};

};

const int min_size = 2;
const int max_size = 1000;

template<typename T>
class Fifo{

    int _head;
    int _tail;
    int _size;

    T* _storage;

public:

    explicit Fifo(int size = min_size);
    ~Fifo(){ delete [] _storage;};

    bool is_full() const{
        return(((_head+1)%_size) == _tail);
    };
    bool is_empty() const{
        return(_head == _tail);
    };

    void add_item(const T& item);
    const T& get_item();

};

template<typename T>
Fifo<T>::Fifo(int size): _size(size){
    
    if (size < min_size) throw E("Cannot create Fifo less than 2\n");

    _head = _tail = 0;

    try{

        _storage = new T[_size];
    }
    catch (std::bad_alloc &ba)
    {
        char e_string[500];
        sprintf(e_string, "Cannot allocate memory (%s)\n", ba.what());
        throw E(e_string);
    }

    printf("Constructing Fifo of size %d\n", _size);

}

template <typename T>
void Fifo<T>::add_item(const T& item)
{
    if (this->is_full()) throw E("Fifo is full.\n");

    _storage[_head] = item;

    _head = (_head + 1)%_size;
}

template <typename T>
const T& Fifo<T>::get_item()
{
    if (this->is_empty()) throw E("Fifo is empty.\n");

    int temp = _tail; //save the current tail

    _tail = (_tail+1)%_size; //update tail

    return(_storage[temp]);
}

int main()
{
    Fifo<int> my_fifo(3);

    for (int i = 1, item; i < 50; i++)
    {
        my_fifo.add_item(i);
        my_fifo.add_item(i*10);
        item = my_fifo.get_item();
        printf("Item: %d\n", item);
        item = my_fifo.get_item();
        printf("Item: %d\n", item);
    }


    return 0;
}
于 2020-07-21T02:43:30.480 回答
-2

这是一种使用java创建动态增加/减少循环队列的优雅方法。

我已经注释了大部分代码以便于快速理解。希望能帮助到你 :)

    public class CircularQueueDemo {
    public static void main(String[] args) throws Exception {

        CircularQueue queue = new CircularQueue(2);
        /* dynamically increasing/decreasing circular queue */
        System.out.println("--dynamic circular queue--");
        queue.enQueue(1);
        queue.display();
        queue.enQueue(2);
        queue.display();
        queue.enQueue(3);
        queue.display();
        queue.enQueue(4);
        queue.display();
        queue.deQueue();
        queue.deQueue();
        queue.enQueue(5);
        queue.deQueue();    
        queue.display();

    }
}

class CircularQueue {
    private int[] queue;
    public int front;
    public int rear;
    private int capacity;

    public CircularQueue(int cap) {
        front = -1;
        rear = -1;
        capacity = cap;
        queue = new int[capacity];
    }

    public boolean isEmpty() {
        return (rear == -1);
    }

    public boolean isFull() {
        if ((front == 0 && rear == capacity - 1) || (front == rear + 1))
            return true;
        else
            return false;
    }

    public void enQueue(int data) { 
        if (isFull()) {            //if queue is full then expand it dynamically   
            reSize();                    
            enQueue(data);
        } else {                                 //else add the data to the queue
            if (rear == -1)                      //if queue is empty
                rear = front = 0;
            else if (rear == capacity)          //else if rear reached the end of array then place rear to start (circular array)
                rear = 0;
            else
                rear++;                         //else just incement the rear 
            queue[rear] = data;                 //add the data to rear position
        }
    }

    public void reSize() {
        int new_capacity = 2 * capacity;                  //create new array of double the prev size
        int[] new_array = new int[new_capacity];          

        int prev_size = getSize();                        //get prev no of elements present
        int i = 0;                                        //place index to starting of new array

        while (prev_size >= 0) {                          //while elements are present in prev queue
            if (i == 0) {                                 //if i==0 place the first element to the array
                new_array[i] = queue[front++];
            } else if (front == capacity) {               //else if front reached the end of array then place rear to start (circular array) 
                front = 0;
                new_array[i] = queue[front++];
            } else                                        //else just increment the array
                new_array[i] = queue[front++];
            prev_size--;                                  //keep decreasing no of element as you add the elements to the new array
            i++;                                          //increase the index of new array
        }
        front = 0;                                        //assign front to 0
        rear = i-1;                                       //assign rear to the last index of added element
        capacity=new_capacity;                            //assign the new capacity
        queue=new_array;                                  //now queue will point to new array (bigger circular array)
    }

    public int getSize() {
        return (capacity - front + rear) % capacity;                  //formula to get no of elements present in circular queue
    }

    public int deQueue() throws Exception {
        if (isEmpty())                                       //if queue is empty
            throw new Exception("Queue is empty");
        else {
            int item = queue[front];                        //get item from front
            if (front == rear)                              //if only one element
                front = rear = -1;
            else if (front == capacity)                     //front reached the end of array then place rear to start (circular array)
                front = 0;
            else
                front++;                                    //increment front by one
            decreaseSize();                                 //check if size of the queue can be reduced to half
            return item;                                    //return item from front
        }

    }

    public void decreaseSize(){                           //function to decrement size of circular array dynamically
        int prev_size = getSize();
        if(prev_size<capacity/2){                         //if size is less than half of the capacity
            int[] new_array=new int[capacity/2];          //create new array of half of its size
            int index=front;                              //get front index
            int i=0;                                      //place an index to starting of new array (half the size)
            while(prev_size>=0){                          //while no of elements are present in the queue
                if(i==0)                                  //if index==0 place the first element
                    new_array[i]=queue[front++];
                else if(front==capacity){                 //front reached the end of array then place rear to start (circular array)      
                    front=0;
                    new_array[i]=queue[front++];
                }
                else
                    new_array[i]=queue[front++];         //else just add the element present in index of front
                prev_size--;                             //decrease the no of elements after putting to new array 
                i++;                                     //increase the index of i
            }
            front=0;                                     //assign front to 0
            rear=i-1;                                    //assign rear to index of last element present in new array(queue)
            capacity=capacity/2;                         //assign new capacity (half the size of prev)
            queue=new_array;                             //now queue will point to new array (or new queue)
        }
    }

    public void display() {                           //function to display queue
        int size = getSize();
        int index = front;

        while (size >= 0) {
            if (isEmpty())
                System.out.println("Empty queue");
            else if (index == capacity)
                index = 0;
            System.out.print(queue[index++] + "=>");
            size--;
        }
        System.out.println("  Capacity: "+capacity);

    }

}

输出:

--动态循环队列--

1=> 容量:2

1=>2=>容量:2

1=>2=>3=>容量:4

1=>2=>3=>4=>容量:4

4=>5=>容量:2

于 2016-01-06T01:12:37.713 回答