zoukankan      html  css  js  c++  java
  • 数据结构之队列

    队列可以通过数组和链表来实现, 就看内置的栈和队列类是用哪种方式实现了。

    队列:是一种先进先出的线性表,在队头删除元素(出队),在队尾添加元素(入队) 

    线性表:  是n个数据元素的有限序列,是连续的。    这也是一个例子

    下面是数组队列实现原理:

        #region 数组队列
        /// <summary>
        /// 数组队列
        /// </summary>
        /// <typeparam name="E"></typeparam>
        class MyArrayQueue<E> : IQueue<E>
        {
            //当前数组
            private MyArray<E> arr;
            /// <summary>
            /// 创建指定长度的数组
            /// </summary>
            /// <param name="capacity"></param>
            public MyArrayQueue(int capacity)
            {
                arr = new MyArray<E>(capacity);
            }
            public MyArrayQueue()
            {
                arr = new MyArray<E>();
            }
            public int Count
            {
                get
                {
                    return arr.Count;
                }
            }
    
            public bool IsEmpty
            {
                get
                {
                    return arr.IsNull;
                }
            }
            /// <summary>
            /// 移除队头的元素
            /// </summary>
            /// <returns></returns>
            public E Dequeue()
            {
                return arr.RemoveFirst();
            }
            /// <summary>
            /// 添加元素到队尾
            /// </summary>
            /// <param name="e"></param>
            public void Enqueue(E e)
            {
                arr.AddLast(e);
            }
            /// <summary>
            /// 获取对头元素
            /// </summary>
            /// <returns></returns>
            public E Peek()
            {
                return arr.GetFirst();
            }
        }
        #endregion

    复杂度:从图中我们可以看到Dequeue移除队头元素的方法是最复杂的,因为数组在移除头元素之后,后面的所有元素都要进行重新移位。

    循环队列

    在上面代码得知Dequeue方法复杂度太高,所以可以通过循环数组解决。

    比如下图:用2个字段记录头部和尾部的位置信息,当元素2后面添加新元素3的时候,last指向的位置添加新元素,last后移移位,这样下次再次添加新元素的时候可以直接根据last的位置进行插入。

     

    当我们移除元素0的时候,first++,后移一位。

    此时,我们在尾部添加元素4,按照上面逻辑来说last还要后移一位,但是此时已经到了最大长度了,所以可以将last按照图中的算式指向对应的位置。

    代码实现:

    class Array2<E>
        {
            private E[] data;
            private int first;
            private int last;
            //元素数量
            private int N;
            public Array2(int capacity)
            {
                data = new E[capacity];
                N = 0;
                first = 0;
                last = 0;
            }
    
            public Array2() : this(10)
            {
            }
            /// <summary>
            /// 数组实际存储数量
            /// </summary>
            public int Count
            {
                get
                {
                    return N;
                }
            }
            /// <summary>
            /// 可以看数组元素是否为空
            /// </summary>
            public bool IsNull
            {
                get
                {
                    return N == 0;
                }
            }
            /// <summary>
            /// 添加元素   队列只能在队尾添加,队头出
            /// </summary>
            /// <param name="e"></param>
            public void AddLast(E e)
            {
                data[last] = e;
                //移动last
                last = (last + 1) % data.Length;
                N++;
            }
    
            public E RemoveFirst()
            {
                if (IsNull)
                {
                    throw new Exception("数组为空");
                }
    
                E del = data[first];
                //设置为默认值,垃圾回收机制这样就可以自动回收值。
                data[first] = default(E);
                //移动first
                first = (first + 1) % data.Length;
                N--;
                return del;
            }
    
            public E GetFirst()
            {
                if (IsNull)
                {
                    throw new Exception("数组为空");
                }
    
                return data[first];
    
            }
    
    
        }

    但是循环数组同样需要扩容操作,比如下图,此时已经没有空间了,

    通过下面2图中的形式来扩容

    下面代码实现了循环数组的扩容和缩容,分别在添加和删除的时候进行的:

       class Array2<E>
        {
            private E[] data;
            private int first;
            private int last;
            //元素数量
            private int N;
            public Array2(int capacity)
            {
                data = new E[capacity];
                N = 0;
                first = 0;
                last = 0;
            }
    
            public Array2() : this(10)
            {
            }
            /// <summary>
            /// 数组实际存储数量
            /// </summary>
            public int Count
            {
                get
                {
                    return N;
                }
            }
            /// <summary>
            /// 可以看数组元素是否为空
            /// </summary>
            public bool IsNull
            {
                get
                {
                    return N == 0;
                }
            }
            /// <summary>
            /// 添加元素   队列只能在队尾添加,队头出
            /// </summary>
            /// <param name="e"></param>
            public void AddLast(E e)
            {
                if (N == data.Length)
                {
                    //扩容
                    ResetCapacity(2 * data.Length);
                }
    
                data[last] = e;
                //移动last
                last = (last + 1) % data.Length;
                N++;
            }
    
            public E RemoveFirst()
            {
                if (IsNull)
                {
                    throw new Exception("数组为空");
                }
    
                E del = data[first];
                //设置为默认值,垃圾回收机制这样就可以自动回收值。
                data[first] = default(E);
                //移动first
                first = (first + 1) % data.Length;
                N--;
                if (N == data.Length / 4)
                {
                    //扩容
                    ResetCapacity(data.Length / 2);
                }
    
                return del;
            }
    
            public E GetFirst()
            {
                if (IsNull)
                {
                    throw new Exception("数组为空");
                }
    
                return data[first];
    
            }
    
    
            private void ResetCapacity(int newCapacity)
            {
                E[] newData = new E[newCapacity];
                for (int i = 0; i < N; i++)
                {
                    newData[i] = data[(first + i) % data.Length];
                }
    
                data = newData;
                first = 0;
                last = N;
            } 
    
        }

    在上面已经实现了循环数组,所以根据循环数组实现的循环队列代码如下:

      #region 数组队列
        /// <summary>
        /// 循环数组队列
        /// </summary>
        /// <typeparam name="E"></typeparam>
        class  Array2Queue<E> : IQueue<E>
        {
            //当前数组
            private Array2<E> arr;
            /// <summary>
            /// 创建指定长度的数组
            /// </summary>
            /// <param name="capacity"></param>
            public Array2Queue(int capacity)
            {
                arr = new Array2<E>(capacity);
            }
            public Array2Queue()
            {
                arr = new Array2<E>();
            }
            public int Count
            {
                get
                {
                    return arr.Count;
                }
            }
    
            public bool IsEmpty
            {
                get
                {
                    return arr.IsNull;
                }
            }
            /// <summary>
            /// 移除队头的元素
            /// </summary>
            /// <returns></returns>
            public E Dequeue()
            {
                return arr.RemoveFirst();
            }
            /// <summary>
            /// 添加元素到队尾
            /// </summary>
            /// <param name="e"></param>
            public void Enqueue(E e)
            {
                arr.AddLast(e);
            }
            /// <summary>
            /// 获取对头元素
            /// </summary>
            /// <returns></returns>
            public E Peek()
            {
                return arr.GetFirst();
            }
        }
        #endregion

    此时Dequeue移除数组队列中的头元素复杂度有O(n)变为O(1),直接根据first便可以找到头元素,移除之后first后移一位,不会像普通数组还要重新排位。

     链表队列   

       一个普通的链表实现的队列:

     #region 链表队列
        /// <summary>
        /// 链表队列
        /// </summary>
        /// <typeparam name="E"></typeparam>
        class MyLinkedListQueue<E> : IQueue<E>
        {
            //当前链表
            private MyLinkedList<E> arr;
            
            public MyLinkedListQueue()
            {
                arr = new MyLinkedList<E>();
            }
            public int Count
            {
                get
                {
                    return arr.Count;
                }
            }
    
            public bool IsEmpty
            {
                get
                {
                    return arr.IsEmpty;
                }
            }
            /// <summary>
            /// 移除队头的元素
            /// </summary>
            /// <returns></returns>
            public E Dequeue()
            {
                return arr.RemoveFirst();
            }
            /// <summary>
            /// 添加元素到队尾
            /// </summary>
            /// <param name="e"></param>
            public void Enqueue(E e)
            {
                arr.AddLast(e);
            }
            /// <summary>
            /// 获取队头元素
            /// </summary>
            /// <returns></returns>
            public E Peek()
            {
                return arr.GetFirst();
            }
    
    
    
    
        }
        #endregion

    时间复杂度:

    通过上面得知Enqueue添加元素的时间复杂度最高,正常的链表:头->....->尾 ,从左往右2端分别是单链表的头与尾,而队列的规定的原理是要遵循先(尾进)进先出(头出),所以添加的时候是往尾部添加,单链表中从尾部添加元素就要从头往后一步一步的找到尾部节点,所以时间复杂度最高。

    要解决上面的的问题,可以通过实现一个具有尾指针的链表来解决,

        #region 具有尾指针的单链表
        public class MyLinkedList2<T>
        {
            //当前链表类记录这个链表的头部类
            private Node head;
            //链表的节点数量
            private int N;
            //尾指针
            private Node tail;
            public MyLinkedList2()
            {
                N = 0;
                //因为一开始链表没有元素,所以头部节点指向空。
                head = null;
                tail = null;
            }
    
    
            /// <summary>
            /// 链表节点数量
            /// </summary>
            public int Count
            {
                get { return N; }
            }
            /// <summary>
            /// 链表是否为空
            /// </summary>
            public bool IsEmpty
            {
                get { return N == 0; }
            }
            /// <summary>
            /// 队列是先进先出队尾进入队头出,所以
            /// 当前链表只保留队尾元素增加方法即可
            /// </summary>
            /// <param name="e"></param>
            public void AddLast(T e)
            {
                Node node = new Node(e);
                if (IsEmpty)
                {
                    //添加前链表为空
                    head = node;
                    tail = node;
                }
                else
                {
                    tail.next = node;
                    tail = node;
                }
                N++;
            }
            /// <summary>
            /// 删除
            /// </summary>
            /// <returns></returns>
            public T RemoveFirst()
            {
                if (IsEmpty)
                {
                    throw new Exception("链表为空");
                }
    
                T e = head.e;
                head = head.next;
                N--;
                if (head == null)
                {
                    tail = null;
                }
    
                return e;
            }
    
            public T GetFirst()
            {
                return head.e;
            }
             
    
            /// <summary>
            /// 节点类  不让外部知道此类,设为私有
            /// </summary>
            private class Node
            {
                //当前节点
                public T e;
                //当前节点的下一节点
                public Node next;
                public Node(T e, Node next)
                {
                    this.e = e;
                    this.next = next;
                }
                public Node(T e)
                {
                    this.e = e;
                }
    
            }
             
            /// <summary>
            /// 修改
            /// </summary>
            /// <param name="index"></param>
            /// <param name="e"></param>
            /// <returns></returns>
            public void Set(int index, T e)
            {
                if (index < 0 || index >= N)
                {
                    throw new Exception("非法索引");
                }
    
                Node currentNode = head;//从头结点开始查找
                for (int i = 0; i < index; i++)
                {
                    currentNode = currentNode.next;
                }
    
                currentNode.e = e; ;
            }
            /// <summary>
            /// 查找链表中是否存在此元素
            /// </summary>
            /// <param name="e"></param>
            /// <returns></returns>
            public bool Contains(T e)
            {
                Node current = head;
                while (current != null)
                {
                    if (current.e.Equals(e))
                    {
                        return true;
                    }
    
                    current = current.next;
                }
    
                return false;
            }
            /// <summary>
            /// 可以重写tostring打印方法
            /// 打印出链表信息
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                StringBuilder stringBuilder = new StringBuilder();
                Node cur = head;
                while (cur != null)
                {
                    stringBuilder.Append(cur.e + "->");
                    cur = cur.next;
                }
                stringBuilder.Append("null");
                return stringBuilder.ToString();
    
            }
             
        }
        #endregion

    代码如上,在队列中使用带有尾指针的类就可以解决上面问题,具体引用实现如下:

     #region 带有尾指针链表队列
        /// <summary>
        /// 链表队列
        /// </summary>
        /// <typeparam name="E"></typeparam>
        class MyLinkedList2Queue<E> : IQueue<E>
        {
            //当前链表
            private MyLinkedList2<E> arr;
    
            public MyLinkedList2Queue()
            {
                arr = new MyLinkedList2<E>();
            }
            public int Count
            {
                get
                {
                    return arr.Count;
                }
            }
    
            public bool IsEmpty
            {
                get
                {
                    return arr.IsEmpty;
                }
            }
            /// <summary>
            /// 移除队头的元素
            /// </summary>
            /// <returns></returns>
            public E Dequeue()
            {
                return arr.RemoveFirst();
            }
            /// <summary>
            /// 添加元素到队尾
            /// </summary>
            /// <param name="e"></param>
            public void Enqueue(E e)
            {
                arr.AddLast(e);
            }
            /// <summary>
            /// 获取队头元素
            /// </summary>
            /// <returns></returns>
            public E Peek()
            {
                return arr.GetFirst();
            }
             
    
        }
        #endregion
  • 相关阅读:
    Cocos2d-x 学习笔记(15.2) EventDispatcher 事件分发机制 dispatchEvent(event)
    ‎Cocos2d-x 学习笔记(13) ActionEase
    ‎Cocos2d-x 学习笔记(12) Speed Follow
    ‎Cocos2d-x 学习笔记(11.10) Spawn
    ‎Cocos2d-x 学习笔记(11.9) FadeTo FadeIn FadeOut
    ‎Cocos2d-x 学习笔记(11.8) DelayTime ReverseTime TargetedAction ActionFloat Blink TintTo TintBy ResizeTo ResizeBy
    Cocos2d-x 学习笔记(11.7) Repeat RepeatForever
    Cocos2d-x 学习笔记(11.6) Sequence
    指针
    TX2常用命令
  • 原文地址:https://www.cnblogs.com/anjingdian/p/15172432.html
Copyright © 2011-2022 走看看