探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用

马肤

温馨提示:这篇文章已超过445天没有更新,请注意相关的内容是否还可用!

摘要:本文探索数据结构中的特殊双向队列。双向队列,也称为双端队列,是一种具有队列和栈性质的数据结构。它允许在队列的两端进行插入和删除操作。这种数据结构在需要高效访问队列两端元素的场景中非常有用。本文详细讨论双向队列的特点、操作及应用,展示其在计算机科学中的重要作用。

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第1张

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法

贝蒂的主页:Betty’s blog

1. 双向队列的定义

**双向队列(double‑ended queue)**是一种特殊的队列,它允许在队列的队尾与队头插入与删除元素。根据其定义,我们也可以理解为两个栈在栈底相连。

  1. 队尾入队

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第2张

  1. 队首入队

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第3张

  1. 队尾出队

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第4张

  1. 队尾出队

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第5张

2. 双向队列的分类

双向队列也是线性表的一种,所以也可以分别用链表与数组实现。基于链表实现:为了方便双向队列在尾部的插入与删除操作,所以我们选用双向链表。基于数组实现:与队列实现类似,需要用循环数组(原因参考队列实现)。

探索数据结构,特殊的双向队列,探索数据结构,特殊双向队列的特性与应用 第6张

3. 双向队列的功能

  1. 队列的初始化。
  2. 判断队列是否为空。。
  3. 返回队头与队尾的元素。
  4. 返回队列的大小。
  5. 入队与出队。
  6. 打印队列的元素。
  7. 销毁队列。

4. 双向队列的声明

4.1. 链式队

双向队列与普通队列的声明区别就在于双向队列是基于双向链表的方式实现。

typedef int QDataType;
typedef struct DuListNode
{
	QDataType data;
	struct Node* prev;
	struct Node* next;
}DuListNode;
typedef struct Deque
{
	size_t size;
	DuListNode* front;
	DuListNode* rear;
}Deque;

4.2. 循环队

循环队列的实现方式与普通队列差不多。

typedef int QDataType;
#define MAXSIZE 50  //定义元素的最大个数
typedef struct {
    QDataType *data;
    int front;  //头指针
    int rear;   //尾指针
}Deque;

5. 队列的初始化

5.1. 链式队

void DequeInit(Deque* d)//初始化
{
	assert(d);
	d->front = NULL;
	d->rear = NULL;
	d->size = 0;
}

5.2. 循环队

void DequeInit(Deque* d)//初始化
{
	d->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE);
	if (d->data == NULL)
	{
		perror("malloc fail:");
		return;
	}
	d->front = 0;
	d->rear = 0;
}

5.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:链式队空间是一个固定大小,空间复杂度为O(1)。而需要开辟整个队列的大小,空间复杂度为O(N)。

    6. 判断队列是否为空

    6.1. 链式队

    bool DequeEmpty(Deque* d)//判断是否为空
    {
    	assert(d);
    	return (d->front == NULL) && (d->rear == NULL);
    }
    

    6.2. 循环队

    bool DequeEmpty(Deque* d)//判断是否为空
    {
    	assert(d);
    	return d->front == d->rear;
    }
    

    6.3. 复杂度分析

    • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
    • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

      7. 判断队列是否为满

      7.1. 链式队

      链式队并不需要判断。

      7.2. 循环队

      为什么要取模操作,可以参考一下上一篇普通队列的实现,同下。

      bool DequeFull(Deque* d)//判断队列是否满
      {
      	assert(d);
      	return (d->rear + 1) % MAXSIZE == d->front;
      }
      

      8. 返回队头与队尾的元素

      8.1. 链式队

      QDataType DequeFront(Deque* d)//获取队头元素
      {
      	assert(d);
      	assert(!DequeEmpty(d));
      	return d->front->data;
      }
      QDataType DequeBack(Deque* d)//获取队尾元素
      {
      	assert(d);
      	assert(!DequeEmpty(d));
      	return d->rear->data;
      }
      

      8.2. 循环队

      QDataType DequeFront(Deque* d)//获取队头元素
      {
      	assert(d);
      	assert(!DequeEmpty(d));
      	return d->data[d->front];
      }
      QDataType DequeBack(Deque* d)//获取队尾元素
      {
      	assert(d);
      	assert(!DequeEmpty(d));
      	return d->data[(d->rear-1+MAXSIZE)%MAXSIZE];
      }
      

      8.3. 复杂度分析

      • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
      • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

        9. 返回队列的大小

        9.1. 链式队

        size_t DequeSize(Deque* d)//队列长度
        {
        	return d->size;
        }
        

        9.2. 循环队

        size_t DequeSize(Deque* d)//获取队列长度
        {
        	assert(d);
        	return (d->rear - d->front + MAXSIZE) % MAXSIZE;
        }
        

        9.3. 复杂度分析

        • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
        • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

          10. 入队

          10.1. 链式队

          10.1.1. 队头入队
          void DequeFrontPush(Deque* d, QDataType x)//队首入队
          {
          	assert(d);
          	DuListNode* newnode = (DuListNode*)malloc(sizeof(DuListNode));
          	if (newnode == NULL)
          	{
          		perror("malloc fail");
          		return;
          	}
          	newnode->data = x;
          	newnode->next = NULL;
          	newnode->prev = NULL;
          	if (d->front == NULL)
          	{
          		d->front = d->rear = newnode;
          	}
          	else
          	{
          		d->front->prev = newnode;
          		newnode->next = d->front;
          		d->front = newnode;
          	}
              d->size++;
          }
          
          10.1.2. 队尾入队
          void DequeRearPush(Deque* d, QDataType x)//队尾入队
          {
          	assert(d);
          	DuListNode* newnode = (DuListNode*)malloc(sizeof(DuListNode));
          	if (newnode == NULL)
          	{
          		perror("malloc fail");
          		return;
          	}
          	newnode->data = x;
          	newnode->next = NULL;
          	newnode->prev = NULL;
          	if (d->front == NULL)
          	{
          		d->front = d->rear = newnode;
          	}
          	else
          	{
          		d->rear->next = newnode;
          		newnode->prev = d->rear;
          		d->rear = newnode;
          	}
          	d->size++;
          }
          

          10.2. 循环队

          入队需要提前判断队列是否为满。

          10.2.1. 队头入队
          void DequeFrontPush(Deque* d, QDataType x)//队首入队
          {
          	assert(d);
          	if (DequeFull(d))
          	{
          		printf("队列已满\n");
          		return;
          	}
          	d->data[(d->front - 1 + MAXSIZE) % MAXSIZE]=x;
          	d->front = (d->front - 1 + MAXSIZE) % MAXSIZE;
          }
          
          10.2.2. 队尾入队
          void DequeRearPush(Deque* d, QDataType x)//队尾入队
          {
          	assert(d);
          	if (DequeFull(d))
          	{
          		printf("队列已满\n");
          		return;
          	}
          	d->data[d->rear] = x;
          	d->rear = (d->rear + 1) % MAXSIZE;
          }
          

          10.3. 复杂度分析

          • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
          • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

            11. 出队

            11.1. 链式队

            出队需要提前判断队列是否为空。

            11.1.1. 队头出队
            void DequeFrontPop(Deque* d)//队首出队
            {
            	assert(d);
            	assert(!DequeEmpty(d));
            	//1.只有一个结点
            	if (d->front == d->rear)
            	{
            		free(d->front);
            		d->front = d->rear = NULL;
            	}
            	//2.有多个结点
            	else
            	{
            		DuListNode* next = d->front->next;
            		next->prev = NULL;
            		d->front->next = NULL;
            		free(d->front);
            		d->front = next;
            	}
            	d->size--;
            }
            
            11.1.2. 队尾出队
            void DequeRearPop(Deque* d)//队尾出队
            {
            	assert(d);
            	assert(!DequeEmpty(d));
            	//1.只有一个结点
            	if (d->front == d->rear)
            	{
            		free(d->front);
            		d->front = d->rear = NULL;
            	}
            	else
            	{
            		DuListNode* prev = d->rear->prev;
            		prev->next = NULL;
            		d->rear->prev = NULL;
            		free(d->rear);
            		d->rear = prev;
            	}
                d->size--;
            }
            

            11.2. 循环队

            11.2.1. 队头出队
            void DequeFrontPop(Deque* d)//队首出队
            {
            	assert(d);
            	assert(!DequeEmpty(d));
            	d->front = (d->front + 1) % MAXSIZE;
            }
            
            11.2.2. 队尾出队
            void DequeRearPop(Deque* d)//队尾出队
            {
            	assert(d);
            	assert(!DequeEmpty(d));
            	d->rear = (d->rear - 1+MAXSIZE) % MAXSIZE;
            }
            

            11.3. 复杂度分析

            • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
            • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

              12. 打印队列元素

              12.1. 链式队

              void DequePrint(Deque* d)//打印队列元素
              {
              	assert(d);
              	DuListNode* cur = d->front;
              	DuListNode* tail = d->rear;
              	printf("队头:");
              	while (cur != tail->next)
              	{
              		printf("%d", cur->data);
              		cur = cur->next;
              	}
              	printf("队尾\n");
              }
              

              12.2. 循环队

              void DequePrint(Deque* d)//打印队列元素
              {
              	assert(d);
              	int cur = d->front;
              	printf("队头->");
              	while (cur != d->rear)
              	{
              		printf("%d->", d->data[cur]);
              		cur = (cur + 1) % MAXSIZE;
              	}
              	printf("队尾\n");
              }
              

              12.3. 复杂度分析

              • 时间复杂度:无论是链式队还是循环队列都需要遍历这个队列,所以时间复杂度为O(N)。
              • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

                13. 销毁队列

                13.1. 链式队

                void DequeDestroy(Deque* d)//销毁队列
                {
                	assert(d);
                	DuListNode* cur = d->front;
                	while (cur)
                	{
                		DuListNode* del = cur;
                		cur = cur->next;
                		free(del);
                		del = NULL;
                	}
                	d->front = d->rear = NULL;
                }
                

                13.2. 循环队

                void DequeDestroy(Deque* d)//销毁队列
                {
                	assert(d);
                	free(d->data);
                	d->data = NULL;
                	d->front = d->rear = 0;
                }
                

                13.3. 复杂度分析

                • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
                • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)

                  14. 对比与应用

                  14.1. 对比

                  双向队列的两种实现方式的效果与普通队列实现差不多,这里就不在一一赘述。

                  14.2. 应用

                  双向队列兼备队列与栈的性质,所以可以应用于这两种数据结构的所有应用场景。

                  此外它应用于撤销的一种情景:通常情况下,撤销是以栈的方式实现,当我们每次更改时就入栈,撤销就出栈。但是我们知道系统给与栈的空间是有限的,我们不可能一直入栈。当入栈超过一个限度时,我们就用过删除栈底的数据,这时栈这个数据结构就无法满足需求。所以这时我们可以使用双向队列来实现。

                  15. 完整代码

                  15.1. 链式队

                  15.1.1. Deque.h
                  #include
                  #include
                  #include
                  #include
                  typedef int QDataType;
                  typedef struct DuListNode
                  {
                  	QDataType data;
                  	struct Node* prev;
                  	struct Node* next;
                  }DuListNode;
                  typedef struct Deque
                  {
                  	size_t size;
                  	DuListNode* front;
                  	DuListNode* rear;
                  }Deque;
                  void DequeInit(Deque* d);//初始化
                  bool DequeEmpty(Deque* d);//判断是否为空
                  QDataType DequeFront(Deque* d);//获取队头元素
                  QDataType DequeBack(Deque* d);//获取队尾元素
                  size_t DequeSize(Deque* d);//获取队列长度
                  void DequeFrontPush(Deque* d, QDataType x);//队首入队
                  void DequeRearPush(Deque* d, QDataType x);//队尾入队
                  void DequeFrontPop(Deque* d);//队首出队
                  void DequeRearPop(Deque* d);//队尾出队
                  void DequePrint(Deque* d);//打印队列元素
                  void DequeDestroy(Deque* d);//销毁队列
                  
                  15.1.2. Deque.c
                  #define _CRT_SECURE_NO_WARNINGS 1
                  #include"Deque.h"
                  void DequeInit(Deque* d)//初始化
                  {
                  	assert(d);
                  	d->front = NULL;
                  	d->rear = NULL;
                  	d->size = 0;
                  }
                  bool DequeEmpty(Deque* d)//判断是否为空
                  {
                  	assert(d);
                  	return (d->front == NULL) && (d->rear == NULL);
                  }
                  QDataType DequeFront(Deque* d)//获取队头元素
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	return d->front->data;
                  }
                  QDataType DequeBack(Deque* d)//获取队尾元素
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	return d->rear->data;
                  }
                  size_t DequeSize(Deque* d)//队列长度
                  {
                  	return d->size;
                  }
                  void DequeFrontPush(Deque* d, QDataType x)//队首入队
                  {
                  	assert(d);
                  	DuListNode* newnode = (DuListNode*)malloc(sizeof(DuListNode));
                  	if (newnode == NULL)
                  	{
                  		perror("malloc fail");
                  		return;
                  	}
                  	newnode->data = x;
                  	newnode->next = NULL;
                  	newnode->prev = NULL;
                  	if (d->front == NULL)
                  	{
                  		d->front = d->rear = newnode;
                  	}
                  	else
                  	{
                  		d->front->prev = newnode;
                  		newnode->next = d->front;
                  		d->front = newnode;
                  	}
                      d->size++;
                  }
                  void DequeRearPush(Deque* d, QDataType x)//队尾入队
                  {
                  	assert(d);
                  	DuListNode* newnode = (DuListNode*)malloc(sizeof(DuListNode));
                  	if (newnode == NULL)
                  	{
                  		perror("malloc fail");
                  		return;
                  	}
                  	newnode->data = x;
                  	newnode->next = NULL;
                  	newnode->prev = NULL;
                  	if (d->front == NULL)
                  	{
                  		d->front = d->rear = newnode;
                  	}
                  	else
                  	{
                  		d->rear->next = newnode;
                  		newnode->prev = d->rear;
                  		d->rear = newnode;
                  	}
                  	d->size++;
                  }
                  void DequeFrontPop(Deque* d)//队首出队
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	//1.只有一个结点
                  	if (d->front == d->rear)
                  	{
                  		free(d->front);
                  		d->front = d->rear = NULL;
                  	}
                  	//2.有多个结点
                  	else
                  	{
                  		DuListNode* next = d->front->next;
                  		next->prev = NULL;
                  		d->front->next = NULL;
                  		free(d->front);
                  		d->front = next;
                  	}
                  	d->size--;
                  }
                  void DequeRearPop(Deque* d)//队尾出队
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	//1.只有一个结点
                  	if (d->front == d->rear)
                  	{
                  		free(d->front);
                  		d->front = d->rear = NULL;
                  	}
                  	else
                  	{
                  		DuListNode* prev = d->rear->prev;
                  		prev->next = NULL;
                  		d->rear->prev = NULL;
                  		free(d->rear);
                  		d->rear = prev;
                  	}
                      d->size--;
                  }
                  void DequePrint(Deque* d)//打印队列元素
                  {
                  	assert(d);
                  	DuListNode* cur = d->front;
                  	DuListNode* tail = d->rear;
                  	printf("队头:");
                  	while (cur != tail->next)
                  	{
                  		printf("%d", cur->data);
                  		cur = cur->next;
                  	}
                  	printf("队尾\n");
                  }
                  void DequeDestroy(Deque* d)//销毁队列
                  {
                  	assert(d);
                  	DuListNode* cur = d->front;
                  	while (cur)
                  	{
                  		DuListNode* del = cur;
                  		cur = cur->next;
                  		free(del);
                  		del = NULL;
                  	}
                  	d->front = d->rear = NULL;
                  }
                  

                  15.2. 循环队

                  15.2.1. Deque.h
                  #include
                  #include
                  #include
                  #include
                  typedef int QDataType;
                  #define MAXSIZE 50  //定义元素的最大个数
                  typedef struct {
                      QDataType *data;
                      int front;  //头指针
                      int rear;   //尾指针
                  }Deque;
                  void DequeInit(Deque* d);//初始化
                  bool DequeEmpty(Deque* d);//判断是否为空
                  bool DequeFull(Deque* d);//判断队列是否满
                  QDataType DequeFront(Deque* d);//获取队头元素
                  QDataType DequeBack(Deque* d);//获取队尾元素
                  size_t DequeSize(Deque* d);//获取队列长度
                  void DequeFrontPush(Deque* d, QDataType x);//队首入队
                  void DequeRearPush(Deque* d, QDataType x);//队尾入队
                  void DequeFrontPop(Deque* d);//队首出队
                  void DequeRearPop(Deque* d);//队尾出队
                  void DequePrint(Deque* d);//打印队列元素
                  void DequeDestroy(Deque* d);//销毁队列
                  
                  15.2.2. Deque.c
                  void DequeInit(Deque* d)//初始化
                  {
                  	d->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE);
                  	if (d->data == NULL)
                  	{
                  		perror("malloc fail:");
                  		return;
                  	}
                  	d->front = 0;
                  	d->rear = 0;
                  }
                  bool DequeEmpty(Deque* d)//判断是否为空
                  {
                  	assert(d);
                  	return d->front == d->rear;
                  }
                  bool DequeFull(Deque* d)//判断队列是否满
                  {
                  	assert(d);
                  	return (d->rear + 1) % MAXSIZE == d->front;
                  }
                  QDataType DequeFront(Deque* d)//获取队头元素
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	return d->data[d->front];
                  }
                  QDataType DequeBack(Deque* d)//获取队尾元素
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	return d->data[(d->rear-1+MAXSIZE)%MAXSIZE];
                  }
                  size_t DequeSize(Deque* d)//获取队列长度
                  {
                  	assert(d);
                  	return (d->rear - d->front + MAXSIZE) % MAXSIZE;
                  }
                  void DequeFrontPush(Deque* d, QDataType x)//队首入队
                  {
                  	assert(d);
                  	if (DequeFull(d))
                  	{
                  		printf("队列已满\n");
                  		return;
                  	}
                  	d->data[(d->front - 1 + MAXSIZE) % MAXSIZE]=x;
                  	d->front = (d->front - 1 + MAXSIZE) % MAXSIZE;
                  }
                  void DequeRearPush(Deque* d, QDataType x)//队尾入队
                  {
                  	assert(d);
                  	if (DequeFull(d))
                  	{
                  		printf("队列已满\n");
                  		return;
                  	}
                  	d->data[d->rear] = x;
                  	d->rear = (d->rear + 1) % MAXSIZE;
                  }
                  void DequeFrontPop(Deque* d)//队首出队
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	d->front = (d->front + 1) % MAXSIZE;
                  }
                  void DequeRearPop(Deque* d)//队尾出队
                  {
                  	assert(d);
                  	assert(!DequeEmpty(d));
                  	d->rear = (d->rear - 1+MAXSIZE) % MAXSIZE;
                  }
                  void DequePrint(Deque* d)//打印队列元素
                  {
                  	assert(d);
                  	int cur = d->front;
                  	printf("队头->");
                  	while (cur != d->rear)
                  	{
                  		printf("%d->", d->data[cur]);
                  		cur = (cur + 1) % MAXSIZE;
                  	}
                  	printf("队尾\n");
                  }
                  void DequeDestroy(Deque* d)//销毁队列
                  {
                  	assert(d);
                  	free(d->data);
                  	d->data = NULL;
                  	d->front = d->rear = 0;
                  }
                  


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码