前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2023年11月 数据结构(六)-双向队列

【愚公系列】2023年11月 数据结构(六)-双向队列

原创
作者头像
愚公搬代码
发布2023-11-05 19:33:19
3350
发布2023-11-05 19:33:19
举报
文章被收录于专栏:历史专栏历史专栏

? 作者简介,愚公搬代码 ?《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。 ?《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。

?《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。

??欢迎 ?点赞?评论?收藏

?前言

数据结构是计算机科学中的一个重要概念,它描述了数据之间的组织方式和关系,以及对这些数据的访问和操作。常见的数据结构有:数组、链表、栈、队列、哈希表、树、堆和图。

  1. 数组(Array):是一种线性数据结构,它将一组具有相同类型的数据元素存储在一起,并为每个元素分配一个唯一的索引。数组的特点是具有随机访问的能力。
  2. 链表(Linked List):也是一种线性数据结构,它由一系列的节点组成,每个节点包含数据和指向下一个节点的引用。链表的特点是可以动态地插入或删除节点,但访问某个节点时需要从头开始遍历。
  3. 栈(Stack):是一种后进先出(LIFO)的数据结构,它只能在栈顶进行插入和删除操作。栈通常用于实现递归算法、表达式求值和内存管理等场景。
  4. 队列(Queue):是一种先进先出(FIFO)的数据结构,它可以在队尾插入元素,在队头删除元素。队列通常用于数据的缓存、消息队列和网络通信等场景。
  5. 哈希表(Hash Table):也称为散列表,它是一种根据关键字直接访问数据的数据结构。哈希表通常由数组和散列函数组成,可以在常数时间内进行插入、删除和查找操作。
  6. 树(Tree):是一种非线性数据结构,它由一系列的节点组成,每个节点可以有若干个子节点。树的特点是可以动态地插入或删除节点,常见的树结构包括二叉树、平衡树和搜索树等。
  7. 堆(Heap):是一种特殊的树结构,它通常用于实现优先队列和堆排序等算法。堆分为最大堆和最小堆,最大堆的每个节点的值都大于等于其子节点的值,最小堆则相反。
  8. 图(Graph):是一种由节点和边组成的非线性数据结构,它可以用来表示各种实体之间的关系,如社交网络、路线图和电路图等。图的遍历和最短路径算法是常见的图算法。

?一、双向队列

?1.基本思想

双向队列是一种具有前后端两个指针的特殊队列,可以在两端进行入队和出队操作。其基本思想是,使用两个指针指向双向队列的头尾,通过对头部和尾部的指针进行灵活的操作,实现对队列的操作。

双向队列可以在队列头部和尾部进行插入和删除操作,因此可以实现更加灵活的数据操作。比如,在需要实现“滑动窗口”这样的场景中,双向队列可以快速地进行插入和删除操作,从而快速地计算出窗口内的最大值或者最小值。

双向队列的实现方法有很多种,常用的有基于数组和基于链表的实现方法。数组实现的双向队列的优点是,支持随机访问,因此可以根据索引直接访问队列中的元素;链表实现的双向队列的优点是,可以支持动态扩容和缩容,适合于动态变化的数据。

综上所述,双向队列是一种非常实用的数据结构,可以在很多场景中灵活地应用,提高数据处理的效率和精度。

在这里插入图片描述
在这里插入图片描述

?2.双向队列常用操作

C#中双向队列(Deque)是一种支持在两端进行元素插入和删除操作的数据结构。常用的操作有:

  1. EnqueueFront(item):在队首插入元素item。
  2. EnqueueBack(item):在队尾插入元素item。
  3. DequeueFront():删除队首元素并返回其值。
  4. DequeueBack():删除队尾元素并返回其值。
  5. PeekFront():返回队首元素,但不删除。
  6. PeekBack():返回队尾元素,但不删除。
  7. Count:返回队列中元素的个数。
  8. Clear():清空队列中所有元素。
  9. Contains(item):判断队列中是否包含元素item。
  10. CopyTo(array, index):将队列中的所有元素复制到指定数组中的指定位置开始的位置。

示例代码:

代码语言:c#
复制
using System.Collections.Generic;

Deque<int> deque = new Deque<int>();
deque.EnqueueFront(1);
deque.EnqueueBack(2);
deque.EnqueueBack(3);
int count = deque.Count; // count为3
int front = deque.PeekFront(); // front为1
int back = deque.PeekBack(); // back为3
deque.DequeueFront(); // 队列变为2, 3
deque.DequeueBack(); // 队列变为2
bool contains = deque.Contains(2); // contains为true
int[] array = new int[3];
deque.CopyTo(array, 0); // 数组变为2
deque.Clear(); // 清空队列中的所有元素

也可以使用LinkedList来实现双向队列

代码语言:c#
复制
/* 初始化双向队列 */
// 在 C# 中,将链表 LinkedList 看作双向队列来使用
LinkedList<int> deque = new LinkedList<int>();
/* 元素入队 */
deque.AddLast(2); // 添加至队尾
deque.AddLast(5);
deque.AddLast(4);
deque.AddFirst(3); // 添加至队首
deque.AddFirst(1);
/* 访问元素 */
int peekFirst = deque.First.Value; // 队首元素
int peekLast = deque.Last.Value; // 队尾元素
/* 元素出队 */
deque.RemoveFirst(); // 队首元素出队
deque.RemoveLast(); // 队尾元素出队
/* 获取双向队列的长度 */
int size = deque.Count;
/* 判断双向队列是否为空 */
bool isEmpty = deque.Count == 0;

?3.队列的实现

?3.1 基于链表的实现

图解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

示例:

代码语言:c#
复制
/* 双向链表节点 */
class ListNode {
	public int val; // 节点值
	public ListNode? next; // 后继节点引用
	public ListNode? prev; // 前驱节点引用
	public ListNode(int val) {
		this.val = val;
		prev = null;
		next = null;
	}
}
/* 基于双向链表实现的双向队列 */
class LinkedListDeque {
	private ListNode? front, rear; // 头节点 front, 尾节点 rear
	private int queSize = 0; // 双向队列的长度
	public LinkedListDeque() {
		front = null;
		rear = null;
	}
	/* 获取双向队列的长度 */
	public int size() {
		return queSize;
	}
	/* 判断双向队列是否为空 */
	public bool isEmpty() {
		return size() == 0;
	}
	/* 入队操作 */
	private void push(int num, bool isFront) {
		ListNode node = new ListNode(num);
		// 若链表为空,则令 front, rear 都指向 node
		if (isEmpty()) {
			front = node;
			rear = node;
		}
		// 队首入队操作
		else if (isFront) {
			// 将 node 添加至链表头部
			front.prev = node;
			node.next = front;
			front = node; // 更新头节点
		}
		// 队尾入队操作
		else {
			// 将 node 添加至链表尾部
			rear.next = node;
			node.prev = rear;
			rear = node; // 更新尾节点
		}
		queSize++; // 更新队列长度
	}
	/* 队首入队 */
	public void pushFirst(int num) {
		push(num, true);
	}
	/* 队尾入队 */
	public void pushLast(int num) {
		push(num, false);
	}
	/* 出队操作 */
	private int? pop(bool isFront) {
		// 若队列为空,直接返回 null
		if (isEmpty()) {
			return null;
		}
		int val;
		// 队首出队操作
		if (isFront) {
			val = front.val; // 暂存头节点值
			// 删除头节点
			ListNode fNext = front.next;
			if (fNext != null) {
				fNext.prev = null;
				front.next = null;
			}
			front = fNext; // 更新头节点
		}
		// 队尾出队操作
		else {
			val = rear.val; // 暂存尾节点值
			// 删除尾节点
			ListNode rPrev = rear.prev;
			if (rPrev != null) {
				rPrev.next = null;
				rear.prev = null;
			}
			rear = rPrev; // 更新尾节点
		}
		queSize--; // 更新队列长度
		return val;
	}
	/* 队首出队 */
	public int? popFirst() {
		return pop(true);
	}
	/* 队尾出队 */
	public int? popLast() {
		return pop(false);
	}
	/* 访问队首元素 */
	public int? peekFirst() {
		return isEmpty() ? null : front.val;
	}
	/* 访问队尾元素 */
	public int? peekLast() {
		return isEmpty() ? null : rear.val;
	}
	/* 返回数组用于打印 */
	public int[] toArray() {
		ListNode node = front;
		int[] res = new int[size()];
		for (int i = 0; i < res.Length; i++) {
			res[i] = node.val;
			node = node.next;
		}
		return res;
	}
}

?3.2 基于数组的实现表

图解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

示例:

代码语言:c#
复制
/* 基于环形数组实现的双向队列 */
class ArrayDeque {
	private readonly int[] nums; // 用于存储双向队列元素的数组
	private int front; // 队首指针,指向队首元素
	private int queSize; // 双向队列长度
	/* 构造方法 */
	public ArrayDeque(int capacity) {
		this.nums = new int[capacity];
		front = queSize = 0;
	}
	/* 获取双向队列的容量 */
	public int capacity() {
		return nums.Length;
	}
	/* 获取双向队列的长度 */
	public int size() {
		return queSize;
	}
	/* 判断双向队列是否为空 */
	public bool isEmpty() {
		return queSize == 0;
	}
	/* 计算环形数组索引 */
	private int index(int i) {
		// 通过取余操作实现数组首尾相连
		// 当 i 越过数组尾部后,回到头部
		// 当 i 越过数组头部后,回到尾部
		return (i + capacity()) % capacity();
	}
	/* 队首入队 */
	public void pushFirst(int num) {
		if (queSize == capacity()) {
			Console.WriteLine(" 双向队列已满");
			return;
		}
		// 队首指针向左移动一位
		// 通过取余操作,实现 front 越过数组头部后回到尾部
		front = index(front - 1);
		// 将 num 添加至队首
		nums[front] = num;
		queSize++;
	}
	/* 队尾入队 */
	public void pushLast(int num) {
		if (queSize == capacity()) {
			Console.WriteLine(" 双向队列已满");
			return;
		}
		// 计算尾指针,指向队尾索引 + 1
		int rear = index(front + queSize);
		// 将 num 添加至队尾
		nums[rear] = num;
		queSize++;
	}
	/* 队首出队 */
	public int popFirst() {
		int num = peekFirst();
		// 队首指针向后移动一位
		front = index(front + 1);
		queSize--;
		return num;
	}
	/* 队尾出队 */
	public int popLast() {
		int num = peekLast();
		queSize--;
		return num;
	}
	/* 访问队首元素 */
	public int peekFirst() {
		if (isEmpty()) {
			throw new InvalidOperationException();
		}
		return nums[front];
	}
	/* 访问队尾元素 */
	public int peekLast() {
		if (isEmpty()) {
			throw new InvalidOperationException();
		}
		// 计算尾元素索引
		int last = index(front + queSize - 1);
		return nums[last];
	}
	/* 返回数组用于打印 */
	public int[] toArray() {
		// 仅转换有效长度范围内的列表元素
		int[] res = new int[queSize];
		for (int i = 0, j = front; i < queSize; i++, j++) {
			res[i] = nums[index(j)];
		}
		return res;
	}
}

?4.优点和缺点

双向队列(Deque)是一种具有队列和栈的特性的数据结构,可以从两端进行插入和删除元素操作。其优点和缺点如下:

优点:

  • 支持在队列两端插入和删除元素,能够快速地进行队列和栈的操作。
  • 可以模拟更复杂的数据结构,例如双端队列可以用来实现双端搜索、某些情况下的最短路径算法等。
  • 在某些操作场景下比普通队列和栈更高效。

缺点:

  • 双向队列需要使用更多内存空间来维护指针,比普通队列和栈更占内存。
  • 插入和删除元素操作可能会引起指针的移动,导致时间复杂度比普通队列和栈更高。
  • 双向队列难以维护某些特定的性质,例如优先队列的排序特性,需要额外的实现方式。

?5.应用场景

双向队列可以在以下情况下被应用:

  1. 在需要同时在队列的前面和后面添加或删除元素的情况下,双向队列是一个很好的选择。例如,一个任务队列,需要在队列头部添加新任务,同时在队列尾部移除已完成任务时,双向队列就非常适合。
  2. 双向队列常用于存储、访问和管理数据的情况下。例如,在电子表格中,用户可以通过列扩展和缩小以调整其大小,这在本质上是一个双向队列操作。
  3. 在计算机科学中,双向队列也被广泛用于实现优先级队列,以便迅速处理任务或查询,添加和删除操作的效率都比较高。
  4. 双向队列的广泛应用之一是双端搜索算法。在搜索问题中,从两端同时遍历可以得到更快的结果,双向队列实现了这一点。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ?前言
  • ?一、双向队列
    • ?1.基本思想
      • ?2.双向队列常用操作
        • ?3.队列的实现
          • ?3.1 基于链表的实现
          • ?3.2 基于数组的实现表
        • ?4.优点和缺点
          • ?5.应用场景
          相关产品与服务
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com