数据结构算法之队列和双向队列

2019-03-27  本文已影响0人  Peakmain

队列

队列(Queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。允许插入的端是队尾,允许删除的端是队头(先进先出)。
那么我们会发现,插入的时间复杂度是O(1)级别的,但是删除是O(n)级别的,因为删除对头之后,其它数据需要向前移动。那么怎么让删除的时间复杂度也变成O(1)级别呢,这就引出双向队列

双向队列

image.png

思路:我们首先会有个head和tail,分别代表队头和对尾,很明显当head==tail的时候代表该队列已经满了,这时候就需要扩容。
扩容前我们首先做几件事情
一、构造函数中数组的大小是2的幂次方,主要目的就是保证数据的分布均匀,具体原因可以继续往下看

template<class E>
ArrayQueue<E>::ArrayQueue(int size) {// 3 -> 4  6 ->8  17 ->32
    // 保证是 2 的幂次
    int init_size = size;
    if (init_size >= 4) {
        init_size |= init_size >> 1;
        init_size |= init_size >> 2;
        init_size |= init_size >> 4;
        init_size |= init_size >> 8;
        init_size |= init_size >> 16;
        // 比如你传入的是4,上面步骤结束后的大小是7所以需要+1
        init_size += 1;

    }
    this->size = init_size;
    array = (E *) malloc(sizeof(E) * size);
}

二、既然是扩容,肯定要是在添加数据的时候进行扩容

template<class E>
void ArrayQueue<E>::push(E e) {
    //实际大小就是size-1
    head = (head - 1) & (size - 1);// -1
    //直接赋值就可以了
    array[head] = e;
    if (tail == head) {
        // 扩容
        growArray();
    }
}
template<class E>
void ArrayQueue<E>::growArray() {
    // 大小扩大两倍
    int new_size = size << 1;

    // 重新开辟一块内存
    E *new_array = (E *) malloc(sizeof(E) * new_size);

    // 对数据进行 copy ,将 tail 后面的拷贝到前面,将tail前面的拷贝到后面
    int r = size - tail;
    copyElement(array, tail, new_array, 0, r);
    copyElement(array, 0, new_array, r, tail);

    free(array);
    head = 0;
    tail = size;
    size = new_size;
    array = new_array;
}
/**
 * 赋值
 * @param src 原数组
 * @param sPo  原数组开始的位置
 * @param dest  目标数组
 * @param dPo  目标数组开始的位置
 * @param len  原数组结束的位置
 */
template<class E>
void ArrayQueue<E>::copyElement(E *src, int sPo, E *dest, int dPo, int len) {
    for (int i = 0; i < len; ++i) {
        dest[dPo + i] = src[sPo + i];
    }
}

上面代码解释下:

三、判断队列是否满了和释放内存

template<class E>
bool ArrayQueue<E>::isEmpty() {
    return tail == head;
}
template<class E>
ArrayQueue<E>::~ArrayQueue() {
    free(array);
}

四、获取队首的位置,但是不移除

template<class E>
E ArrayQueue<E>::peek() {
    return array[(tail - 1) & (size - 1)];
}

实际我们的队首是head所指向的位置,那么如何获得队首head的下标呢


image.png

假设是上述这种情况,实际队首是这个队列的大小


image.png
上述这种情况下,队首实际也是这个队列的大小,可是怎么获得这个队列的大小呢,我们在默认数组大小是4的情况下,第一次扩容后,tail由0→8,而size由8→16,所以下标实际就是7,由此得出
(tail - 1) & (size - 1)

五、移除队首的元素

template<class E>
E ArrayQueue<E>::pop() {
    tail = (tail - 1) & (size - 1);
    return array[tail];
}

上一篇下一篇

猜你喜欢

热点阅读