C#数据结构之队列(Quene)实例详解

 更新时间:2015年11月27日 14:55:06   作者:Jimmy.Yang  
这篇文章主要介绍了C#数据结构之队列(Quene),结合实例形式较为详细的讲述了队列的功能、原理与C#实现队列的相关技巧,需要的朋友可以参考下

本文实例讲述了C#数据结构之队列(Quene)。分享给大家供大家参考,具体如下:

队列(Quene)的特征就是“先进先出”,队列把所有操作限制在"只能在线性结构的两端"进行,更具体一点:添加元素必须在线性表尾部进行,而删除元素只能在线性表头部进行

先抽象接口IQuene<T>

namespace 栈与队列
{
  public interface IQuene<T>
  {
    /// <summary>
    /// 取得队列实际元素的个数
    /// </summary>
    /// <returns></returns>
    public int Count();
    /// <summary>
    /// 判断队列是否为空
    /// </summary>
    /// <returns></returns>
    public bool IsEmpty();
    /// <summary>
    /// 清空队列
    /// </summary>
    public void Clear();
    /// <summary>
    /// 入队(即向队列尾部添加一个元素)
    /// </summary>
    /// <param name="item"></param>
    public void Enquene(T item);
    /// <summary>
    /// 出队(即从队列头部删除一个元素)
    /// </summary>
    /// <returns></returns>
    public T Dequene();
    /// <summary>
    /// 取得队列头部第一元素
    /// </summary>
    /// <returns></returns>
    public T Peek();
  }
}

下面是基于数组实现的示意图:

实现思路:用一个数组存放所有元素,同时设置二个关键变量front与rear用于记录队列“头”与“尾”的元素下标,当有元素入列时rear加1,当有元素出队时front+1,而rear-front即为队列实际元素的总数.

但有一种“队列伪满”的特殊情况要注意,如下图:

这张图上面的部分:假设经过入队、出队一番折腾后,rear已经指向数组的下标最大值,而front指向在中间(即front之间的元素已经出队不用考虑了,相当于front下标前面的内存区域空闲),如果这时再有一个元素入列,rear+1就超出数组下标的最大值了,但是从图上一眼就能看出,实际上front前面还空着一堆位置可以重复利用,队列并非真正的“满”--这种情况称为伪满,为了解决这个问题,我们可以把数组想象为首尾相接的循环结构,即图中下面部分,这时候可以让rear重新指向到0,以便重复利用空闲的位置。

所以:入列时rear++的操作,应该稍做修正,当rear到数组下标最大值时,让它置0,以便能循环利用 (见后面的代码)

另外还有一个问题:最开始时front与rear都为-1,即front==rear时表示队列为空,改成循环以后,有可能会出现rear在循环过程中碰到front的情况,即真正意义的上"满"状态,这时rear也同样等于front,这样就无法单纯的用rear==front来判断是满,还是空?这时可以浪费一个元素的位置,认为当rear+1==front时,队列就已经满了,虽然牺牲了一个元素的空间,但却换来了逻辑的正确性,还是值得的。

完整实现如下:

using System;
using System.Text;
namespace 栈与队列
{
  /// <summary>
  /// 循环顺序队列
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public class CSeqQueue<T>:IQueue<T>
  {
    private int maxsize;
    private T[] data;
    private int front;
    private int rear;    
    public CSeqQueue(int size) 
    {
      data = new T[size];
      maxsize = size;
      front = rear = -1;
    }
    public int Count()     
    {
      if (rear > front)
      {
        return rear - front;
      }
      else
      {
        return (rear - front + maxsize) % maxsize;
      }
    }
    public void Clear() 
    {
      front = rear = -1;
    }
    public bool IsEmpty() 
    {
      return front == rear;      
    }
    public bool IsFull() 
    {      
      if (front != -1) //如果已经有元素出队过
      {
        return (rear + 1) % maxsize == front;//为了区分与IsEmpty的区别,有元素出队过以后,就只有浪费一个位置来保持逻辑正确性.
      }
      else
      {
        return rear == maxsize - 1;
      }      
    }
    public void Enqueue(T item) 
    {
      if (IsFull()) 
      {
        Console.WriteLine("Queue is full");
        return;
      }
      if (rear == maxsize - 1) //如果rear到头了,则循环重来(即解决伪满问题)
      {
        rear = 0;
      }
      else
      {
        rear++;
      }
      data[rear] = item;
    }
    public T Dequeue() 
    {      
      if (IsEmpty()) 
      {
        Console.WriteLine("Queue is empty");
        return default(T);
      }
      if (front == maxsize - 1) //如果front到头了,则重新置0
      {
        front = 0;
      }
      else
      {
        front++;
      }      
      return data[front];
    }
    public T Peek() 
    {
      if (IsEmpty()) 
      {
        Console.WriteLine("Queue is empty!");
        return default(T);
      }
      return data[(front + 1) % maxsize];      
    }
    public override string ToString()
    {
      if (IsEmpty()) { return "queue is empty."; }
      StringBuilder sb = new StringBuilder();
      if (rear > front)
      {
        for (int i = front + 1; i <= rear; i++)
        {
          sb.Append(this.data[i].ToString() + ",");
        }
      }
      else
      {
        for (int i = front + 1; i < maxsize; i++)
        {
          sb.Append(this.data[i].ToString() + ",");
        }
        for (int i = 0; i <= rear; i++)
        {
          sb.Append(this.data[i].ToString() + ",");
        }
      }
      return "front = " + this.front + " \t rear = " + this.rear + "\t count = " + this.Count() + "\t data = " + sb.ToString().Trim(',');
    }
  }
}

测试代码片段:

CSeqQueue<int> queue = new CSeqQueue<int>(5);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Enqueue(4);
Console.WriteLine(queue);//front = -1    rear = 3    count = 4    data = 1,2,3,4
queue.Dequeue();
Console.WriteLine(queue);//front = 0    rear = 3    count = 3    data = 2,3,4
queue.Dequeue();
Console.WriteLine(queue);//front = 1    rear = 3    count = 2    data = 3,4
queue.Enqueue(5);
Console.WriteLine(queue);//front = 1    rear = 4    count = 3    data = 3,4,5
queue.Enqueue(6);
Console.WriteLine(queue);//front = 1    rear = 0    count = 4    data = 3,4,5,6
queue.Enqueue(7);    //Queue is full
Console.WriteLine(queue);//front = 1    rear = 0    count = 4    data = 3,4,5,6
queue.Dequeue();
queue.Enqueue(7);
Console.WriteLine(queue);//front = 2    rear = 1    count = 4    data = 4,5,6,7
queue.Clear();
Console.WriteLine(queue);//queue is empty.
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Enqueue(4);
Console.WriteLine(queue);//front = -1    rear = 3    count = 4    data = 1,2,3,4
queue.Enqueue(5);
Console.WriteLine(queue);//front = -1    rear = 4    count = 5    data = 1,2,3,4,5
queue.Enqueue(6);    //Queue is full
Console.WriteLine(queue);//front = -1    rear = 4    count = 5    data = 1,2,3,4,5
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
Console.WriteLine(queue);//front = 3    rear = 4    count = 1    data = 5
queue.Dequeue();
Console.WriteLine(queue);//queue is empty.
queue.Enqueue(0);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Enqueue(4);    //Queue is full
Console.WriteLine(queue);//front = 4    rear = 3    count = 4    data = 0,1,2,3
Console.WriteLine(queue.Peek());//0
queue.Dequeue();
Console.WriteLine(queue);//front = 0    rear = 3    count = 3    data = 1,2,3
queue.Dequeue();
Console.WriteLine(queue);//front = 1    rear = 3    count = 2    data = 2,3
queue.Dequeue();
Console.WriteLine(queue);//front = 2    rear = 3    count = 1    data = 3
queue.Dequeue();
Console.WriteLine(queue);//queue is empty.
queue.Enqueue(9);
Console.WriteLine(queue);//front = 3    rear = 4    count = 1    data = 9
Console.ReadLine();

当然,队列也可以用链表来实现,相对要容易很多。

先定义链表中的节点Node.cs

namespace 栈与队列
{
  public class Node<T>
  {
    private T data;
    private Node<T> next;
    public Node(T data, Node<T> next) 
    {
      this.data = data;
      this.next = next;
    }
    public Node(Node<T> next) 
    {
      this.next = next;
      this.data = default(T);
    }
    public Node(T data) 
    {
      this.data = data;
      this.next = null;
    }
    public Node() 
    {
      this.data = default(T);
      this.next = null;
    }
    public T Data {
      get { return this.data; }
      set { this.data = value; }
    }
    public Node<T> Next 
    {
      get { return next; }
      set { next = value; }
    }
  }
}

为了方便,定义了很多构造函数的重载版本,当然这些只是浮云,重点是理解结构:data用来保存数据,next指出下一个节点是谁
链式队列的完整实现LinkQueue.cs

using System;
using System.Text;
namespace 栈与队列
{
  public class LinkQueue:IQueue
  {
    private Node front;//队列头
    private Node rear;//队列尾
    private int num;//队列元素个数
    /// 
    /// 构造器
    /// 
    public LinkQueue() 
    {
      //初始时front,rear置为null,num置0
      front = rear = null;
      num = 0;
    }
    public int Count() 
    {
      return this.num;
    }
    public void Clear() 
    {
      front = rear = null;
      num = 0;
    }
    public bool IsEmpty() 
    {
      return (front == rear && num == 0);
    }
    //入队
    public void Enqueue(T item) 
    {
      Node q = new Node(item);
      if (rear == null)//第一个元素入列时
      {
        front = rear = q;
      }
      else
      {
        //把新元素挂到链尾
        rear.Next = q;
        //修正rear指向为最后一个元素
        rear = q;
      }
      //元素总数+1
      num++;
    }
    //出队
    public T Dequeue() 
    {
      if (IsEmpty()) 
      {
        Console.WriteLine("Queue is empty!");
        return default(T);
      }
      //取链首元素
      Node p = front;
      //链头指向后移一位
      front = front.Next;
      //如果此时链表为空,则同步修正rear
      if (front == null) 
      {
        rear = null;
      }
      num--;//个数-1
      return p.Data;
    }
    public T Peek() 
    {
      if (IsEmpty()) 
      {
        Console.WriteLine("Queue is empty!");
        return default(T);
      }
      return front.Data;
    }
    public override string ToString()
    {
      if (IsEmpty()) {
        Console.WriteLine("Queue is empty!");
      }
      StringBuilder sb = new StringBuilder();
      Node node = front;
      sb.Append(node.Data.ToString());
      while (node.Next!=null)
      {
        sb.Append("," + node.Next.Data.ToString());
        node = node.Next;
      }
      return sb.ToString().Trim(',');
    }
  }
}

希望本文所述对大家C#程序设计有所帮助。

相关文章

  • C#开启线程的四种示例

    C#开启线程的四种示例

    这篇文章主要介绍了C#开启线程的四种方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2020-10-10
  • C#设置页面单位和缩放的方法

    C#设置页面单位和缩放的方法

    这篇文章主要介绍了C#设置页面单位和缩放的方法,涉及C#设置页面属性的相关技巧,需要的朋友可以参考下
    2015-06-06
  • C# newtonsoft.json中文乱码问号的解决方案

    C# newtonsoft.json中文乱码问号的解决方案

    这篇文章主要介绍了C# newtonsoft.json中文乱码问号的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • c#中token的使用方法实例

    c#中token的使用方法实例

    本文主要介绍了c#中token的使用方法实例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • C#实现简易的计算器

    C#实现简易的计算器

    这篇文章主要为大家详细介绍了C#实现简易的计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • Unity的IPostprocessBuild实用案例深入解析

    Unity的IPostprocessBuild实用案例深入解析

    这篇文章主要为大家介绍了Unity的IPostprocessBuild实用案例深入解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • WPF实现文字粒子闪烁动画效果

    WPF实现文字粒子闪烁动画效果

    这篇文章主要为大家详细介绍了WPF实现文字粒子闪烁动画效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • c#中winform根据邮箱地址和密码一键发送email的实现

    c#中winform根据邮箱地址和密码一键发送email的实现

    本文主要介绍了c#winform根据邮箱地址和密码一键发送email的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • C#文件加密方法汇总

    C#文件加密方法汇总

    这篇文章主要介绍了C#文件加密方法,实例汇总了常见的加密方法如AES加密类、文件加密类、文件夹加密类等,最后给出完整的实例源码下载供大家参考借鉴,需要的朋友可以参考下
    2014-11-11
  • C# 面向对象的基本原则

    C# 面向对象的基本原则

    什么是面向对象的基本原则?设计原则是基本的工具,应用这些规则可以使你的代码更加灵活、更容易维护,更容易扩展。
    2009-11-11

最新评论