首页 > 编程语言 >c#数据结构06_队列

c#数据结构06_队列

时间:2024-10-20 17:45:36浏览次数:3  
标签:06 c# 链表 队列 int IsEmpty 数据结构 data public

说明
(此系列帖子记录数据结构学习成果,如侵犯视频作者权益,立删)
视频链接:离忧夏天C#数据结构

本文实现队列需要用到动态数组ArrayList和单链表的相关知识,详细请看:C#数据结构专栏

文章目录

一:队列的基本概念

队列是一种遵循先进先出原则的线性表
在这里插入图片描述

二:队列的基本操作

  • 入队(Enqueue)
  • 出队(DeQueue)
  • 获取队列的大小(Count)
  • 检查队列是否为空(IsEmpty)
  • 查看队头元素(GetFirst)

实现队列的接口

public interface IQueue<T>
    {
        //查看元素数量
        int Count { get; }
        //判空
        bool IsEmpty { get; }
        //入队
        void Enqueue(T data);
        //出队
        T Dequeue();
        //查看队首元素
        T Peek();
    }

三:队列的实现

1.数组队列(由动态数组实现)

数组队列时间复杂度
入队(Enqueue)O(1)
出队(DeQueue)O(n)
获取队列的大小(Count)O(1)
检查队列是否为空(IsEmpty)O(1)
查看队头元素(GetFirst)O(1)
public class Array1Queue<T> : IQueue<T>
    {
        private Array1<T> arr;
        public int Count => arr.Count;

        public bool IsEmpty => arr.IsEmpty;

        public Array1Queue(int capacity)
        {
            arr = new Array1<T>(capacity);
        }
        public Array1Queue()
        {
            arr = new Array1<T>();    
        }
        //入队
        public void Enqueue(T data)
        {
            arr.AddLast(data);
        }
        //出队
        public T Dequeue()
        {
            return arr.RemoveFirst();
        }
        //查看队首元素
        public T Peek()
        {
            return arr.GetFirst();
        }
        public override string ToString()
        {
            return "Queue: Head" + arr.ToString() + "Tail";
        }
    }

2.循环队列(由循环数组实现)

循环数组其中的元素可以在数组的末端重新改回到起始位置,其头尾相连形成一个闭环。

  1. 头部由fist指针标识
  2. 尾部由last指针标识
  3. 当到达数组的末尾时,last指针会重写指向数组的起始位置,从而充分利用整个数组空间,避免了不必要的内存浪费。
  4. 其中需要取余操作是必须的,这样可以保证数组的循环,即==(last+1)% data.length==

循环数组的实现

public class Array2<T>
    {
        private T[] data; //存储元素
        private int first;//记录头部
        private int last;//记录尾部
        private int size;//当前元素个数
        
        public Array2(int capacity)
        {
            data = new T[capacity];
            
            first = 0;
            last = 0;
            size = 0;
        }
        public Array2() : this(10) { }
        
        public int Count => size;
        public bool IsEmpty => size == 0;
        
        //添加元素
        public void AddLast(T newData)
        {
            if (size == data.Length)
                ResetCapacity(2 * data.Length);
                
            data[last] = newData;
            last = (last+1)%data.Length;
            size++;
        }
        //删除元素
        public T RemoveFirst()
        {
            if (IsEmpty)
                throw new Exception("数组为空");
                
            T res = data[first];
            data[first] = default(T);
            
            first = (first + 1) % data.Length;
            size--;
            
            if (size == data.Length / 4)
                ResetCapacity(data.Length / 2);
                
            return res;
        }
        //获取元素
        public T GetFirst()
        {
            if (IsEmpty)
                throw new Exception("数组为空");

            return data[first];
        }
        
        private void ResetCapacity(int newCapacity)
        {
            T[] newData = new T[newCapacity];
            
            for (int i = 0; i < size; i++)
                newData[i] = data[(first + i) % data.Length];
                
            data = newData;
            first = 0;
            last = size;
        }
        public override string ToString()
        {
            StringBuilder res = new StringBuilder();
            res.Append("[");
            for(int i = 0; i < size; i++)
            {
                res.Append(data[(first + i) % data.Length]);
                if((first + i + 1) % data.Length != last)
                    res.Append(", ");
            }
            res.Append("]");
            return res.ToString();
        }
    }

循环队列的实现

循环队列时间复杂度
入队(Enqueue)O(1)
出队(DeQueue)O(1)
获取队列的大小(Count)O(1)
检查队列是否为空(IsEmpty)O(1)
查看队头元素(GetFirst)O(1)
public class Array2Queue<T> : IQueue<T>
    {
        private Array2<T> arr;
        
        public Array2Queue(int capacity)
        {
            arr = new Array2<T>(capacity);
        }
        
        public Array2Queue()
        {
            arr = new Array2<T>();
        }
        public int Count => arr.Count;

        public bool IsEmpty => arr.IsEmpty;
        
        //入队,往队尾添加元素O(1)
        public void Enqueue(T data)
        {
            arr.AddLast(data);
        }

        //出队,删除队首的元素O(1)
        public T Dequeue()
        {
            return arr.RemoveFirst();
        }

        //查看队首元素O(1)
        public T Peek()
        {
            return arr.GetFirst();
        }
        public override string ToString()
        {
            return "Queue: front" + arr.ToString() + "Tail";
        }
    }

3.链表队列(由只带头指针的链表实现)

因为在向单链表添加元素的过程中,每次添加元素,我们都需要用一层for循环遍历到链表尾部,然后再添加元素,因此入队的时间复杂度是O(n)

链表队列时间复杂度
入队(Enqueue)O(n)
出队(DeQueue)O(1)
获取队列的大小(Count)O(1)
检查队列是否为空(IsEmpty)O(1)
查看队头元素(GetFirst)O(1)
public class LinkedList1Queue<T> : IQueue<T>
    {
        private LinkedList1<T> list;
        
        public LinkedList1Queue()
        {
            list = new LinkedList1<T>();
        }
        
        public int Count => list.Count;

        public bool IsEmpty => list.IsEmpty;
        
        //删除队列头部=>删除链表头部O(1)
        public T Dequeue()
        {
            return list.RemoveFirst();
        }
        //添加到队列尾部=>添加链表尾部O(n)
        public void Enqueue(T data)
        {
            list.AddLast(data);
        }
        //查看队列头部==查看链表头部O(1
        public T Peek()
        {
            return list.GetFirst();
        }
        
        public override string ToString()
        {
            return "Queue front" + list.ToString() +"tail";
        }
    }

4.链表队列(由带头尾指针的链表实现)

和只有头指针的单链表类似,在尾指针添加元素,只需要让尾指针的下一个节点为newNode,并让newNode为尾指针即可
即tail.next = node; tail = node;

链表队列(带尾指针)时间复杂度
入队(Enqueue)O(1)
出队(DeQueue)O(1)
获取队列的大小(Count)O(1)
检查队列是否为空(IsEmpty)O(1)
查看队头元素(GetFirst)O(1)

带尾指针的单链表的实现

public class LinkedList02<T>
    {
        private class Node
        {
            public T data;
            public Node next;
            public Node(T data)
            {
                this.data = data;
                this.next = null;
            }
            public Node(T data , Node next)
            {
                this.data = data;
                this.next = next;
            }
            public override string ToString()
            {
                return base.ToString();
            }
        }
        
        private Node head;
        private Node tail;
        private int size;
        
        public LinkedList02()
        {
            this.head = null;
            this.tail = null;
            this.size = 0;
        }
        
        public int Count => size;
        public bool IsEmpty => size == 0;
        
        //链表尾部添加元素
        public void AddLast(T newData)
        {
            Node node = new Node(newData);
            if (IsEmpty)
            {
                head = node;
                tail = node;
            }
            else
            {
                tail.next = node;
                tail = node;
            }
            size++;
        }
        //链表头部删除元素
        public T RemoveFirst()
        {
            if (IsEmpty)
                throw new InvalidOperationException("链表为空");
            
            T deldata = head.data;
            head = head.next;
            size--;
            
            if(head == null)
                tail = null;

            return deldata;
        }
        //查看头部
        public T GetFirst()
        {
            if (IsEmpty)
                throw new InvalidOperationException("链表为空");

            return head.data;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            Node cur = head;
            while (cur != null)
            {
                sb.Append(cur.data + "->");
                cur = cur.next;
            }
            sb.Append("Null");
            return sb.ToString();
        }
    }

通过带有尾指针的链表实现的队列

public class LinkedList2Queue<T> : IQueue<T>
    {
        private LinkedList02<T> list;
        
        public LinkedList2Queue()
        {
            list = new LinkedList02<T>();
        }
        public int Count => list.Count;

        public bool IsEmpty => list.IsEmpty;
        
        //入队
        public void Enqueue(T data)
        {
            list.AddLast(data);
        }
        //出队
        public T Dequeue()
        {
            return list.RemoveFirst();
        }
        //查看队首元素
        public T Peek()
        {
            return list.GetFirst();
        }
        public override string ToString()
        {
            return "Queue front" + list.ToString() +"tail";
        }
    }

四:性能分析比较

对于队列的测试,编写TestQueue方法

 //测试队列的性能
 private static long TestQueue(IQueue<int> queue, int N)
 {
     Stopwatch t = new Stopwatch();
     t.Start();
     for (int i = 0; i < N; i++)
     {
         queue.Enqueue(i);
     }
     for (int i = 0; i < N; i++)
     {
         queue.Dequeue();
     }
     t.Stop();
     return t.ElapsedMilliseconds;
 }

数组队列 VS 循环队列(win)

int N = 100000;
//数组队列
Array1Queue<int> array1Queue = new Array1Queue<int>();
long t1 = TestQueue(array1Queue, N);
Console.WriteLine("Array1Queue Time:" + t1+"ms");
//循环队列
Array2Queue<int> array2Queue = new Array2Queue<int>();
long t2 = TestQueue(array2Queue, N);
Console.WriteLine("Array2Queue Time:" + t2 + "ms");
Console.ReadKey();

在这里插入图片描述
我们可以清楚的看到:循环队列的性能对于数组队列是大幅度提升的。

由只有头指针链表实现的队列 VS 由具有头尾指针链表实现的队列(win)

int N = 100000;
//链表队列(头指针)
LinkedList1Queue<int> linkedList1Queue = new LinkedList1Queue<int>();
long t1 = TestQueue(linkedList1Queue, N);
Console.WriteLine("linkedList1Queue Time:" + t1+"ms");
//链表队列(头尾指针)
LinkedList2Queue<int> linkedList2Queue = new LinkedList2Queue<int>();
long t2 = TestQueue(linkedList2Queue, N);
Console.WriteLine("linkedList2Queue Time:" + t2 + "ms");
Console.ReadKey();

在这里插入图片描述
我们可以清楚的看到,通过优化链表,使得具有尾指针,并通过尾指针添加元素,使得性能对于普通的单链表是大幅度提升的。

标签:06,c#,链表,队列,int,IsEmpty,数据结构,data,public
From: https://blog.csdn.net/2301_78736909/article/details/143095920

相关文章

  • abc358E Alphabet Tiles
    现有大写英文字母A-Z,个数分别为C[i],总共可以组成多少个长度在[1,K]之间的不同串?答案对998244353取模。1<=K<=1000,0<=C[i]<=1000分析:记dp[i][k]表示前i类字母构成长度为k的不同方案数,枚举第i类字母的个数j进行转移。#include<bits/stdc++.h>usingi64=longlong;templat......
  • C++ constexp vs const
    C++constexpvsconstconstexpr是在C++11标准中引入的关键字,目的是为编译时常量提供更强大的支持。它允许某些表达式在编译期进行求值,从而提高性能和优化能力。下面详细说明它与const的区别。constexpr和const的区别特性constexprconst引入版本C++11C++......
  • C系统编程通信方式——共享内存
        共享内存,标准IPC之一,也是进程间通信最快的一种方式。1.概念    所有的标准IPC都有一个内部ID作为唯一标识。内部ID的获取通过外部key,key的类型是key_t。key的获取方法有在头文件中定义所有key和通过ftok函数获取一个key。key_tftok(constchar*pathna......
  • 数据结构与算法
    数据结构:研究数据在内存中存储的结构算法:实现增删改查的方法解决问题的实际方法算法的好坏评价标准:时间复杂度和空间复杂度时间复杂度:算法所用时间的一个映射时间复杂度得出的是一个数学问题数据总量x(x足够大),从而消耗的执行次数y之间存在的关系LeetCode算法题......
  • React Spring实战之API以及animated 组件的运用
    ReactSpring实战之API以及animated组件的运用小熊码农2024-04-20109 浏览#前端江河入海,知识涌动,这是我参与江海计划的第7篇。APIreact-spring库中与动画相关的API支持两种不同的使用渲染道具和react钩子的方法。接下来,我们将介绍reacthook的一些动画相关API:reac......
  • electron disable inspect
     https://github.com/electron-userland/electron-builder/issues/6365 import{flipFuses,FuseVersion,FuseV1Options}from'@electron/fuses' asyncfunctioninitApp(){ constexePath=app.getPath('exe') awaitflipFuses(  exeP......
  • Using MATLAB with CANoe 快读
    近期领导交给了一个非常有意思的任务:尝试实现在不同工况下的HSI测试,并给了Matlab这个提示。当然我并不实现交互的具体算法,但是要懂得Matlab接口的测试调用和上层General测试框架的搭建。资料来源:UsingMATLABwithCANoe 1.0Overview目的是为了拓展CANoe的Node功能,支持节No......
  • Chromium 中chrome.contextMenus扩展接口实现分析c++
    一、chrome.contextMenus使用 chrome.contextMenus API向GoogleChrome的上下文菜单中添加项。您可以选择从右键菜单中添加的对象类型,例如图片、超链接和页面。权限contextMenus您必须在扩展程序的清单中声明 "contextMenus" 权限,才能使用该API。此外,您应指定一个......
  • 数码摄影师、图形设计师及高端用户设计的桌面图像编辑和管理软件下载Adobe Lightroom
    目录一、软件概述1.1名称与定位1.2版本与更新1.3系统兼容性二、系统要求2.1最低系统要求2.2推荐系统要求三、下载与安装3.1下载链接3.2安装步骤四、功能介绍4.1照片管理4.2照片编辑4.3高级功能一、软件概述1.1名称与定位AdobeLightroomClassic......
  • CSP 模拟 51
    A排列最小生成树(pmst)首先想到\(n^2\)建边的做法,然后最小生成树的所有边权都小于\(n\),直接从头到尾连都能轻松满足这个。所以很多边是无用的,只要找边权小于\(n\)的边即可。所以对于位置差和权值差在\(\sqrtn\)以下的都找一遍即可,然后桶排跑MST即可。赛时根号都写脸......