C#算法系列(10)——循环队列、链队列

       队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。它是一种先进先出的线性表,简称FIFO。允许插入的一端称为队尾,允许删除称为队头。
       循环队列的提出主要是用于解决顺序队列的假溢出问题。解决假溢出的办法就是后面满了,再从头开始,头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。本文将从循环队列的顺序存储和链队列出发,讲解常用的出队与入队操作。下面先看下循环队列。

一、循环队列

1.顺序存储结构
private T[] datas;
private static int MAXSIZE = 10;
private int front; //头指针
private int rear; //尾指针,若队列不为空,指向队列尾元素的下一个位置
2.队空与队满判断
队空条件:front == rear;
队满条件:(rear+1)%MAXSIZE = front;
队列长度计算:(rear + MAXSIZE - front) % MAXSIZE;
3.入队操作
//初始化队列
private void InitQueue()
{
    datas = new T[MAXSIZE];
    front = 0;
    rear = 0;
}

//入队操作
public void EnQueue(T value)
{
    if (datas == null)
        InitQueue();
    //队列满判断
    if ((rear + 1) % MAXSIZE == front)
        return;
    datas[rear] = value;
    //rear指针后移一位置,若到最后则转到数组头部
    rear = (rear + 1) % MAXSIZE;
}
4.出队操作
//出队操作
public T DeQueue()
{
    T value = default(T);
    //队空判断
    if (front == rear)
        return value;
    value = datas[front];
    //front指针向后移一位置,若到最后则转到数组头部
    front = (front + 1) % MAXSIZE;
    return value;
}
5.求队列长度
//求队列长度
public int QueueLength()
{
    return (rear + MAXSIZE - front) % MAXSIZE;
}
6.测试
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 循环队列_顺序存储
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] datas = new int[] { 10, 20, 30, 40, 50, 60, 70, 80 };
            foreach (int temp in datas)
            {
                //入队
                SqQueueTest<int>.Instance.EnQueue(temp);
            }
            //队列长度
            int length = SqQueueTest<int>.Instance.QueueLength(),i=0;
            while (i < length)
            {
                Console.Write(SqQueueTest<int>.Instance.DeQueue()+" ");
                i++;
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

       实验截图:

这里写图片描述

二、链队列

       链队列,其本质是带头结点的单链表尾插法

1.链式存储结构

       结点类QNode.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 循环队列_链式存储
{
    class QNode<T>
    {
        public T data;
        public QNode<T> next;

        public QNode()
        { }

        public QNode(T value,QNode<T> nextNode)
        {
            this.data = value;
            this.next = nextNode;
        }
    }
}
2.出队与入队操作示意图
2.1入队


这里写图片描述

       由上图可知,入队分为两步,第一步,把新节点s赋值给原队尾结点的后继,即rear.next = s;第二步,把当前s设置为队尾结点,即rear = s;

2.2出队

这里写图片描述

       此时,出队有两种情形。第一种,出队后,队尾不指向头结点;第二种,出队后,队尾指向头结点,此时删除后,需要将rear指向头结点如图中第三步。基本出队操作也分为两步:1.将要删除的对头结点暂存给临时结点,即tempNode = front.next,如图中第一步;2.将原队头结点后继front.next赋值给头结点的后继结点,即front.next = tempNode.next,如图中第二步;

3.链式队列实现类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 循环队列_链式存储
{
    class LinkQueueTest<T>
    {
        private QNode<T> front; //指向头结点
        private QNode<T> rear; //队尾指针
        public int COUNT = 0;//队列中的元素个数

        //单例模式
        private static LinkQueueTest<T> _instance = null;
        public static LinkQueueTest<T> Instance {
            get {
                if (_instance == null)
                    _instance = new LinkQueueTest<T>();
                return _instance;
            }
        }

        //初始化队列
        public QNode<T> InitInitQueue()
        {
            if (front == null)
                front = new QNode<T>();
            rear = front;
            front.next = rear;
            return front;
        }

        //入队操作 与单链表尾插入类似
        public void EnQueue(T value)
        {
            //队列不存在
            if (front == null)
            {
                front = InitInitQueue();
            }
            //根据元素,实例化新的结点参数
            QNode<T> newNode = new QNode<T>(value,null);
            //将新结点插入到队列中
            rear.next = newNode;
            rear = newNode;
            COUNT++;
        }

        //出队操作
        public T DeQueue()
        {
            T value = default(T);
            QNode<T> tempNode = new QNode<T>();
            //队空判断
            if (front == rear)
                return value;
            tempNode = front.next;
            value = tempNode.data;
            front.next = tempNode.next;
            //若队头是队尾,则删除后将rear指向头结点
            if (rear == tempNode)
                rear = front;
            COUNT--;
            return value;
        }

    }
}
4.测试
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 循环队列_链式存储
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] datas = new int[] { 10, 20, 30, 40, 50, 60, 70, 80 };
            foreach (int temp in datas)
            {
                //入队
                LinkQueueTest<int>.Instance.EnQueue(temp);
            }
            int count = LinkQueueTest<int>.Instance.COUNT,i = 0;
            Console.WriteLine("队列中的元素的个数:"+ count);
            //输出队列
            while (i < count)
            {
                //出队
                Console.Write(LinkQueueTest<int>.Instance.DeQueue()+" ");
                i++;
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

实验截图:

这里写图片描述

三、循环队列与链队列的比较

       从时间上,二者的基本操作都是O(1)。但循环队列是事先申请好空间,使用期间不会被GC机制回收,而链队列,每次申请和GC机制回收结点会存在一些时间开销,如果入队出队频繁,二者还是有细微差异。
       从空间上,循环队列必须要有一个固定的长度,即MAXSIZE,所以就会存在存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但影响不大,因此,空间上,链队列更加灵活。

四、栈和队列的比较

       它们均可以用线性表的顺序存储结构来实现,但都存在着顺序存储的一些弊端,但都利用各自的技巧来解决这个问题。
       对于栈来说,如果是两个相同数据类型的栈,则可以用数组的两端作栈底的方法来让栈共享数据,这就可以最大化利用数组的空间。对于队列来说,为了避免数组插入和删除时需要移动数据,于是就引入了循环队列,使得对头和队尾可以在数组中循环变化。解决了移动数据的时间损耗,使得本来插入和删除是O(n)的时间复杂度变成了O(1)。它们也都可以通过链式存储结构来实现,实现原则上与线性表基本相同。
       以上就是今天队列的介绍,如有疑问,欢迎私信我!!!

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值