zoukankan      html  css  js  c++  java
  • 第八节:队列简介、手撸顺序队列、手撸链队列和队列的应用

    一. 队列简介

     1. 什么是队列

     队列(Queue)只允许在一端进行插入,在另一端进行删除的线性表。(队尾入队,队头出队),可见队列具有先进先出(First In First Out)或后进后出( Last In Last Out)的特性。

     C#中提供Queue队列类,它不是线程安全的; 如需要使用线程安全的队列类,则使用:ConcurrentQueue

    2. 名词

     队头( Front):队列中允许数据删除的那一端。

     队尾( Rear):队列中允许数据插入的那一端。

     队上溢(Full):存储空间已满,仍希望入队操作,会产生上溢出,是一种空间不足的出错状态。

     队下溢(Empty):队内空间无数据,仍希望出队操作,会产生下溢出,是一种数据不足的出错状态。

     空队列(Empty Queue):队列中没有数据元素.

     入队(Enqueue):将一个数据插入到队尾的操作。

     出队(Dequeue):读取队头节点并删除该节点的操作

    3. 常用Api

     Enqueue()入队(放在队尾)

     Dequeue()出队(移除队首元素,并返回被移除的元素)

     Peek()取得队首的元素,不移除

     Clear()清空元素

     Count获取队列中元素的个数

    测试代码:

             {
                    Console.WriteLine("--------------C#提供的Queue---------------------");
                    Queue<int> s1 = new Queue<int>();
                    s1.Enqueue(1);
                    s1.Enqueue(2);
                    s1.Enqueue(3);
                    s1.Enqueue(4);
                    Console.WriteLine($"元素的个数为:{s1.Count}");
                    int p1 = s1.Dequeue();     //取出并删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p1}");
                    int p2 = s1.Peek();     //取出不删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p2}");
                    s1.Clear();
                    Console.WriteLine($"元素的个数为:{s1.Count}");
                }
    View Code

    运行结果:

    4. 分类

     队列存储结构的实现有以下两种方式:

     顺序队列:在顺序表的基础上实现的队列结构。

     链队列:在链表的基础上实现的队列结构。

    两者的区别仅是顺序表和链表的区别,即在实际的物理空间中,数据集中存储的队列是顺序队列,分散存储的队列是链队列。

    二. 顺序队列

    1. 顺序队列

     顺序队列(Sequence Queue)用一片连续的存储空间来存储队列中的数据元素.用一维数组来存放顺序队列中的数据元素。队头位置设在数组下标为 0 的端,用 front 表示;队尾位置设在数组的另一端,用 tail 表示。 front 和 tail 随着插入和删除而变化。当队列为空时, front=tail=0。因为在出队列(删除元素)的时候,需要花费大量的时间移动大量元素,速度很慢,所以很少有实际应用.

    2. 顺序循环队列

     为了避免大量数据的移动,通常将一维数组的各个元素看成一个收尾相接的封闭的圆环,即第一个元素是最后一个元素的下一个元素,这种形式的顺序队列称为循环顺序队列(Circular sequence Queue)。

    注:C#为我们提供的Queue类就是循环队列。

    3. 手撸循环队列

    接口

        /// <summary>
        /// 声明队列接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public interface IQueue<T>
        {
            int Count { get; }//获取元素个数
            bool IsEmpty(); //是否为空队列
            void Clear(); //清空队列
            void Enqueue(T item); //入队
            T Dequeue(); //出队
            T Peek(); //取队头元素
        }

    代码

        /// <summary>
        /// 循环队列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class SeqQueue<T> : IQueue<T>
        {
            //存放元素的数组
            public T[] _array;
            //增长因子 1-10之间
            private int _growfactor;
            //最小增长值
            private const int _MinimumGrow = 4;
            //默认队列空间的大小
            private const int _defaultCapacity = 8;
            //元素的个数
            private int _size = 0;
            //队头指针  指向队头的第一个元素
            private int _head;
            //队尾指针  指向队尾的最后一个元素索引+1
            private int _tail;
            public SeqQueue() : this(_defaultCapacity, 2) { }
    
            public SeqQueue(int capacity, float growFactor)
            {
                if (capacity < 0)
                {
                    throw new ArgumentOutOfRangeException("capacity", "初识容量不能小于0");
                }
                if (capacity < _defaultCapacity)
                {
                    capacity = _defaultCapacity;
                }
                if (growFactor < 1.0 || growFactor > 10.0)
                {
                    throw new ArgumentOutOfRangeException("growFactor", "增长因子必须在1-10之间");
                }
                this._array = new T[capacity];  //初始化数组
                this._head = this._tail = 0;
                this._size = 0;
                this._growfactor = (int)(growFactor * 100f);
            }
            //获取元素个数
            public int Count
            {
                get
                {
                    return this._size;
                }
            }
    
            public void Clear()
            {
                this._head = this._tail = this._size = 0;
            }
            //出队操作
            public T Dequeue()
            {
                if (this._size == 0)
                {
                    throw new InvalidOperationException("队列下溢,队里里没有数据");
                }
                T obj = this._array[_head]; //出队数据
                this._array[_head] = default(T);        //在数组里删除出队元素
                this._head = (this._head + 1) % this._array.Length;   //循环队列  处理机制,保证下标是循环的
                this._size--;
                return obj;
            }
            //入队
            public void Enqueue(T item)
            {
                if (this._size == this._array.Length)
                {
                    //计算新的容量
                    int capacity = (int)(this._array.Length * this._growfactor / 100f);
                    if (capacity < this._array.Length + _MinimumGrow)
                    {
                        //最少要增长四个元素
                        capacity = this._array.Length + _MinimumGrow;
                    }
                    //调整容量
                    SetCapacity(capacity);
                }
                this._array[_tail] = item;  //入队
                this._tail = (this._tail + 1) % this._array.Length;     //移动尾巴指针
                this._size++;
            }
            private void SetCapacity(int capacity)
            {    //内存搬家
                T[] destinationArray = new T[capacity];
                if (this._head < this._tail)
                {
                    //头指针在尾指针的前面
                    Array.Copy(this._array, this._head, destinationArray, 0, this._size);
                }
                else
                {
                    //头指针在尾指针的后面
                    Array.Copy(this._array, this._head, destinationArray, 0, this._array.Length - this._head);
                    Array.Copy(this._array, 0, destinationArray, this._array.Length - this._head, this._tail);
                }
                this._array = destinationArray;
                this._head = 0;
                this._tail = (this._size == capacity) ? 0 : this._size;
            }
            public bool IsEmpty()
            {
                return this._size == 0;
            }
            //获取队头元素,不删除
            public T Peek()
            {
                if (this._size == 0)
                {
                    throw new InvalidOperationException("队列下溢,队里里没有数据");
                }
                T obj = this._array[_head]; //出队数据
                return obj;
            }
        }
    View Code

    测试

         {
                    Console.WriteLine("--------------手撸循环队列---------------------");
                    IQueue<int> s1 = new SeqQueue<int>();
                    s1.Enqueue(1);
                    s1.Enqueue(2);
                    s1.Enqueue(3);
                    s1.Enqueue(4);
                    Console.WriteLine($"元素的个数为:{s1.Count}");
                    int p1 = s1.Dequeue();     //取出并删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p1}");
                    int p2 = s1.Peek();     //取出不删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p2}");
                    s1.Clear();
                    Console.WriteLine($"元素的个数为:{s1.Count}");
         }

    运行结果

    三. 链队列

     1. 含义

     队列的另外一种存储方式是链式存储,这样的队列称为链队列(Linked Queue)。同链栈一样,链队列通常用单链表来表示,它的实现是单链表的简化。

    2. 手撸链队列

    接口

         /// <summary>
        /// 声明队列接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public interface IQueue<T>
        {
            int Count { get; }//获取元素个数
            bool IsEmpty(); //是否为空队列
            void Clear(); //清空队列
            void Enqueue(T item); //入队
            T Dequeue(); //出队
            T Peek(); //取队头元素
        }

    代码

     /// <summary>
        /// 链队列
        /// (队尾tail入队,队头front出队)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class LinkedQueue<T> : IQueue<T>
        {
            public QueueNode<T> front;     //队头节点
            public QueueNode<T> tail;      //队尾节点
            public int count;              //元素个数
    
    
            public LinkedQueue()
            {
                Clear();
            }
    
            public int Count
            {
                get
                {
                    return count;
                }
            }
    
            /// <summary>
            /// 清空所有元素
            /// </summary>
            public void Clear()
            {
                front = null;
                tail = null;
                count = 0;
            }
    
            /// <summary>
            /// 判空
            /// </summary>
            /// <returns></returns>
            public bool IsEmpty()
            {
                return count == 0;
            }
    
            /// <summary>
            /// 出队(不删除元素)
            /// </summary>
            /// <returns></returns>
            public T Peek()
            {
                if (front == null)
                {
                    throw new ArgumentOutOfRangeException("队列下溢,队列中没有元素");
                }
                T data = front.data;
                return data;
            }
    
            /// <summary>
            /// 出队(删除元素)
            /// </summary>
            /// <returns></returns>
            public T Dequeue()
            {
                if (front == null)
                {
                    throw new ArgumentOutOfRangeException("队列下溢,队列中没有元素");
                }
                T data = front.data;
                if (count == 1)
                {
                    front = null;
                    tail = null;
                }
                else
                {
                    front = front.next;
                }
                count--;
                return data;
            }
    
            /// <summary>
            /// 入队
            /// </summary>
            /// <param name="item"></param>
            public void Enqueue(T item)
            {
                QueueNode<T> newNode = new QueueNode<T>(item);
                if (count == 0)
                {
                    front = newNode;
                    tail = newNode;
                }
                else
                {
                    tail.next = newNode;
                    tail = newNode;
                }
                count++;
            }
        }
    View Code

    测试

     {
                    Console.WriteLine("--------------手撸链队列---------------------");
                    IQueue<int> s1 = new LinkedQueue<int>();
                    s1.Enqueue(1);
                    s1.Enqueue(2);
                    s1.Enqueue(3);
                    s1.Enqueue(4);
                    Console.WriteLine($"元素的个数为:{s1.Count}");
                    int p1 = s1.Dequeue();     //取出并删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p1}");
                    int p2 = s1.Peek();     //取出不删除
                    Console.WriteLine($"元素的个数为:{s1.Count},取出的数据为:{p2}");
                    s1.Clear();
                    Console.WriteLine($"元素的个数为:{s1.Count}");
    }

    运行结果

    四. 应用

     1.  流量削峰

      高并发场景,比如秒杀,可以把下单请求存放到队列中,然后消费者从队列中依次取出来进行实际下单业务。

    2. 应用解耦(异步)

     比如登录成功后,要增加积分或者发送邮件,可以引入消息队列进行解耦,异步处理增加积分 或者 发送邮件的请求。

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    SICP学习笔记 第二章 (2.3)
    SICP学习笔记 第二章 (2.4)
    SICP学习笔记 第二章 (2.2)(上)
    SICP学习笔记 第一章 (1.3)
    SICP学习笔记 第二章 (2.1)
    sql server 获取服务器中数据库的大小
    vss File for <file> (<physical file>) Was Not Found
    Linq 中的获取最大值得记录
    silverlight 报错超时
    asp 中的getChunk(img_size)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/14336953.html
Copyright © 2011-2022 走看看