C# 显示队列时出错

C# 显示队列时出错,c#,queue,C#,Queue,我试图实现一个队列类,该类支持Min方法,该方法返回队列中存储的最小值,如果队列为空,则抛出异常 然而,当我试图运行该程序时,显示的队列与我导出的队列不同。我排队4个项目:6,3000,10000,-304,但显示为6->3000->10000->304->0->0。这两个0来自哪里?当我试着偷看两次时,我期望输出是10000,但还是打印了两个0 以下是我所做的: public class Queue<T> { #region Properties /// <

我试图实现一个队列类,该类支持Min方法,该方法返回队列中存储的最小值,如果队列为空,则抛出异常

然而,当我试图运行该程序时,显示的队列与我导出的队列不同。我排队4个项目:6,3000,10000,-304,但显示为6->3000->10000->304->0->0。这两个0来自哪里?当我试着偷看两次时,我期望输出是10000,但还是打印了两个0

以下是我所做的:

public class Queue<T>
{
    #region Properties

    /// <summary>
    /// The capacity of the Elements Collection
    /// </summary>
    private int _capacity;
    public int Capacity
    {
        get { return _capacity; }
        set { _capacity = value; }
    }

    /// <summary>
    /// The number of elements currently in the queue.
    /// </summary>
    private int _length;
    public int Length
    {
        get { return _length; }
        set { _length = value; }
    }

    /// <summary>
    /// The actual data elements stored in the queue.
    /// </summary>
    private T[] _elements;
    protected T[] Elements
    {
        get { return _elements; }
        set { _elements = value; }
    }

    /// <summary>
    /// This is the index where we will dequeue.
    /// </summary>
    private int _frontIndex;
    public int FrontIndex
    {
        get { return _frontIndex; }
        set { _frontIndex = value; }
    }

    /// <summary>
    /// This is the index where we will next enqueue a value. 
    /// It is calculated based on the Front Index, Length, and Capacity
    /// </summary>
    public int BackIndex
    {
        get { return (FrontIndex + Length) % Capacity; }
    }

    #endregion

    #region Constructors

    public Queue()
    {
        Elements = new T[Capacity];
    }

    public Queue(int capacity)
    {
        Capacity = capacity;
        Elements = new T[Capacity];
    }

    #endregion

    #region public methods

    public void Enqueue(T element)
    {
        if (this.Length == Capacity)
        {
            IncreaseCapacity();
        }
        Elements[BackIndex] = element;
        Length++;
    }

    public T Dequeue()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        T element = Elements[FrontIndex];
        Elements[FrontIndex] = default(T);
        Length--;
        FrontIndex = (FrontIndex + 1) % Capacity;
        return element;
    }

    public T Peek()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        return Elements.First();
    }

    public T Min()
    {
        bool notSupport = false;
        try
        {
            T minItem = Elements.First();
            foreach (T temp in Elements)
            {
                try
                {
                    if (Convert.ToDouble(minItem) > Convert.ToDouble(temp))
                        minItem = temp;
                }
                catch
                {
                    notSupport = true;

                }
            }
            return minItem;
        }
        catch
        {
            if (notSupport)
                throw (new InvalidOperationException("Error: Method not support this type."));
            else
                throw (new InvalidOperationException("Error: Queue is empty"));
        }
    }

    public void Display()
    {
        foreach (T item in Elements)
        {
            Console.Write(item);
            Console.Write("-->");
        }
        Console.WriteLine();
    }

    #endregion

    #region protected methods

    /// <summary>
    /// Private function for increasing the size of the queue 
    /// if we run out of space and need to add another element
    /// </summary>
    protected void IncreaseCapacity()
    {
        this.Capacity++;
        this.Capacity *= 2;
        Queue<T> tempQueue = new Queue<T>(this.Capacity);
        while (this.Length > 0)
        {
            tempQueue.Enqueue(this.Dequeue());
        }
        this.Elements = tempQueue.Elements;
        this.Length = tempQueue.Length;
        this.FrontIndex = tempQueue.FrontIndex;
    }

    #endregion
}

//test client
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("*** Create queue type interger and enqueue some item: 6, 3000, 10000, -304 *** ");
        Queue<int> queue = new Queue<int>();
        queue.Enqueue(6);
        queue.Enqueue(3000);
        queue.Enqueue(10000);
        queue.Enqueue(-304);
        queue.Display();

        Console.WriteLine("*** Dequeue two item ***");
        Console.WriteLine(queue.Dequeue());
        Console.WriteLine(queue.Dequeue());

        Console.WriteLine("*** Peek two times ***");
        Console.WriteLine(queue.Peek());
        Console.WriteLine(queue.Peek());

        Console.WriteLine("*** Min value ***");
        Console.WriteLine(queue.Min());

        Console.WriteLine("*** Dequeue 2 item, queue is empty, and Dequeue mor item ***");
        try
        {
            queue.Dequeue();
            queue.Dequeue();
            queue.Dequeue();
        }
        catch (InvalidOperationException error)
        {
            Console.WriteLine(error.Message);
        }

        Console.WriteLine("*** Peek empty queue ***");
        try
        {
            queue.Peek();
        }
        catch (InvalidOperationException error)
        {
            Console.WriteLine(error.Message);
        }

        Console.WriteLine("*** Find min item in empty queue ***");
        try
        {
            queue.Min();
        }
        catch (InvalidOperationException error)
        {
            Console.WriteLine(error.Message);
        }

        Console.WriteLine("*** Insert 5 item: 10, -3, 39, 2, 40000 ***");
        queue.Enqueue(10);
        queue.Enqueue(-3);
        queue.Enqueue(39);
        queue.Enqueue(2);
        queue.Enqueue(40000);
        queue.Display();

        Console.WriteLine("*** Peek two times ***");
        Console.WriteLine(queue.Peek());
        Console.WriteLine(queue.Peek());

        Console.WriteLine("*** Min value ***");
        Console.WriteLine(queue.Min());

        Console.WriteLine("*** Dequeue two item ***");
        Console.WriteLine(queue.Dequeue());
        Console.WriteLine(queue.Dequeue());


        Console.WriteLine("*** Create queue type double and enqueue some item: 6.4, 3000.322, 10000.333, -304.221 *** ");
        Queue<double> doubleQueue = new Queue<double>();
        doubleQueue.Enqueue(6.4);
        doubleQueue.Enqueue(3000.322);
        doubleQueue.Enqueue(10000.333);
        doubleQueue.Enqueue(-304.221);
        doubleQueue.Display();

        Console.ReadLine();
    }
}

您需要调试IncreaseCapacity方法。每当重新计算队列容量时,它都会添加不必要的条目,这些条目显示为伪零

这两个0来自哪里

这是因为在构造函数中初始化元素

int的默认值是0

当我试着偷看两次时,我期望输出是10000,但是 再次打印两个0

这是因为在队列的实现中,元素不会移动,而是停留在它们的位置上

我建议您在使用队列存储元素的实现中实现您的功能。大概是这样的:

public class CustomQueue<T>
{
    private readonly Queue<T> queue;

    private int _capacity;
    public int Capacity
    {
        get { return _capacity; }
        set { _capacity = value; }
    }


    public CustomQueue()
    {
        this.queue = new Queue<T>();
    }

    public CustomQueue(int capacity)
    {
        this.Capacity = capacity;
        this.queue = new Queue<T>(capacity);
    }

    public void Enqueue(T element)
    {
        if (this.queue.Count == this.Capacity)
        {
            IncreaseCapacity();
        }

        this.queue.Enqueue(element);
    }

    public T Dequeue()
    {
        if (this.queue.Count < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }

        return this.queue.Dequeue();
    }

    public T Peek()
    {
        if (this.queue.Count < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }

        return this.queue.Peek();
    }

    public T Min()
    {
        bool notSupport = false;
        try
        {
            T minItem = this.queue.Peek();

            foreach (T temp in this.queue)
            {
                try
                {
                    if (Convert.ToDouble(minItem) > Convert.ToDouble(temp))
                        minItem = temp;
                }
                catch
                {
                    notSupport = true;

                }
            }
            return minItem;
        }
        catch
        {
            if (notSupport)
                throw (new InvalidOperationException("Error: Method not support this type."));
            else
                throw (new InvalidOperationException("Error: Queue is empty"));
        }
    }

    public void Display()
    {
        foreach (T item in this.queue)
        {
            Console.Write(item);
            Console.Write("-->");
        }
        Console.WriteLine();
    }


    protected void IncreaseCapacity()
    {
        this.Capacity++;
    }
}

您可以创建两个扩展方法,将您想要的两个函数添加到现有队列中,而不是尝试创建一个新类来完成内置的所有功能


注意,由于使用System.Linq,我将Min重命名为MyMin;执行myQueue.Min可以开箱即用,根本不需要编写任何代码,但如果类型不支持转换为double,它不会像您的代码那样引发异常。

您的队列实现似乎存在多个不同的问题。其中大多数都与这样一个事实有关,即使用循环数组来保存元素,这是实现队列的一种非常好的方法,但在代码中的一些地方,对该数组进行索引的方式是不正确的

我还注意到一些其他问题,可能与不熟悉c语言通常如何处理某些事情有关,例如

尝试将T类型的项目转换为double以进行比较,而不是使用IComparer。 为每个属性使用一个支持字段,在该字段中可以只使用自动属性。 将这些属性的setter公开为public,允许出现诸如类的客户端更改元素数Length=10之类的问题;不让任何元素排队或退队,也不让不变量失效。 正如其他人已经提到的,有一个标准的.NET实现可用,您可以简单地使用所需的额外功能进行包装或扩展

不过,我相信明确强调代码中当前存在的一些问题以及如何解决这些问题可能会很有用。因此,我创建了一个版本,我相信它可以解决这些问题,其中的主要更改以//***风格的注释突出显示

public class Queue<T> 
{
    // *** You need to be able to compare elements in 'Min()' ***
    private readonly IComparer<T> _comparer;
    // *** Allocate a small initial size if no capacity given ***
    private const int DefaultCapacity = 4;

    #region Properties

    /// <summary>
    /// The capacity of the Elements Collection
    /// </summary>
    public int Capacity
    {
        // *** The number of allocated elements == capacity ***
        get { return Elements.Length; } 
    }

    // *** For each of these properties:
    // (1) you don't need a backing field, you can use an "auto property".
    // (2) don't make the "set" operation available to clients of the class: 
    //     make it "private".

    /// <summary>
    /// The number of elements currently in the queue.
    /// </summary>
    public int Length { get; private set; }

    /// <summary>
    /// The actual data elements stored in the queue.
    /// </summary>
    protected T[] Elements { get; private set; }

    /// <summary>
    /// This is the index where we will dequeue.
    /// </summary>
    public int FrontIndex { get; private set; }

    /// <summary>
    /// This is the index where we will next enqueue a value. 
    /// It is calculated based on the Front Index, Length, and Capacity
    /// </summary>
    public int BackIndex
    {
        get { return (FrontIndex + Length) % Capacity; }
    }

    #endregion

    #region Constructors

    /*** Allows supplying a comparer to find the minimum */
    public Queue(IComparer<T> comparer = null) : this(DefaultCapacity, comparer)
    {
    }

    public Queue(int capacity, IComparer<T> comparer = null)
    {
        // *** Ensure capacity > 0 ***
        Elements = new T[capacity > 0 ? capacity : DefaultCapacity];
        _comparer = comparer ?? Comparer<T>.Default; // *** We need a comparer ****
    }

    #endregion

    #region public methods

    public void Enqueue(T element)
    {
        if (this.Length == Capacity)
        {
            IncreaseCapacity();
        }
        Elements[BackIndex] = element;
        Length++;
    }

    public T Dequeue()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        T element = Elements[FrontIndex];
        Elements[FrontIndex] = default(T);
        Length--;
        FrontIndex = (FrontIndex + 1) % Capacity;
        return element;
    }

    public T Peek()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        return Elements[FrontIndex]; // *** You need to index by FrontIndex ***
    }

    // *** Added to allow easy enumeration. ***
    public IEnumerable<T> Items
    {
        get
        {
            for (var i = 0; i < Length; i++)
                yield return Elements[(FrontIndex + i) % Capacity];
        }
    }

    public T Min() 
    {
        // *** Cannot get min if there are no items.
        if (Length < 1)
            throw new InvalidOperationException("Error: Queue is empty");

        // *** Need to start searching from FrontIndex
        var min = Elements[FrontIndex];
        for (var i = 1; i < Length; i++)
        {
            var item = Elements[(FrontIndex + i) % Capacity];
            // *** Need  the comparer for comparison of 'T'
            if (_comparer.Compare(min, item) > 0) // 'min' > 'item'
                min = item;
        }
        return min;
    }

    public void Display()
    {
        foreach (T item in Items) // *** We can use the "Items" enumerable here.
        {
            Console.Write(item);
            Console.Write("-->");
        }
        Console.WriteLine();
    }

    #endregion

    #region private methods

    /// <summary>
    /// Private function for increasing the size of the queue 
    /// if we run out of space and need to add another element
    /// </summary>
    private void IncreaseCapacity()
    {
        // *** Need to allocate a new larger array for the items
        var newElementsArray = new T[this.Capacity * 2];
        // *** And fill it with our current elements sequentially
        var ndx = 0;
        foreach (var item in Items)
            newElementsArray[ndx++] = item;

        // *** Now we can assign it and reset the front index.
        this.Elements = newElementsArray;
        this.FrontIndex = 0;
    }

    #endregion
}

与其重新发明轮子,为什么不从System.Collections.Generic.Queue派生一个类来添加Min和Display方法呢?我不明白你想说什么。@HarperSue不幸的是,修复你的代码并不容易。我实现了一个自定义队列,它使用原始队列存储数据。希望这有帮助。最好使用框架的功能,而不是重新发明轮子。我尝试再次运行,但现在测试客户端方法出现错误System.Collections.Generic.Queue不包含“Display”的定义@HarperSue您必须创建CustomQueue的实例,而不是队列。您必须注意,我的类中的队列是来自.NET Framework的队列,而不是您的队列实现。
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{

    class Program
    {
        static void Main(string[] args)
        {
            var myQueue = new Queue<int>();
            myQueue.Enqueue(6);
            myQueue.Enqueue(3000);
            myQueue.Enqueue(1000);
            myQueue.Enqueue(-304);

            Console.WriteLine("Min: {0}", myQueue.MyMin());
            myQueue.Display();
            Console.ReadLine();
        }
    }

    static class QueueExtensionMethods
    {
        //Renamed to MyMin because Min already exists from System.Linq
        public static T MyMin<T>(this Queue<T> queue)
        {
            bool notSupport = false;
            try
            {
                T minItem = queue.First();
                foreach (T temp in queue)
                {
                    try
                    {
                        if (Convert.ToDouble(minItem) > Convert.ToDouble(temp))
                            minItem = temp;
                    }
                    catch
                    {
                        notSupport = true;

                    }
                }
                return minItem;
            }
            catch
            {
                if (notSupport)
                    throw (new InvalidOperationException("Error: Method not support this type."));
                else
                    throw (new InvalidOperationException("Error: Queue is empty"));
            }
        }

        public static void Display<T>(this Queue<T> queue)
        {
            foreach (T item in queue)
            {
                Console.Write(item);
                Console.Write("-->");
            }
            Console.WriteLine();
        }
    }
}
public class Queue<T> 
{
    // *** You need to be able to compare elements in 'Min()' ***
    private readonly IComparer<T> _comparer;
    // *** Allocate a small initial size if no capacity given ***
    private const int DefaultCapacity = 4;

    #region Properties

    /// <summary>
    /// The capacity of the Elements Collection
    /// </summary>
    public int Capacity
    {
        // *** The number of allocated elements == capacity ***
        get { return Elements.Length; } 
    }

    // *** For each of these properties:
    // (1) you don't need a backing field, you can use an "auto property".
    // (2) don't make the "set" operation available to clients of the class: 
    //     make it "private".

    /// <summary>
    /// The number of elements currently in the queue.
    /// </summary>
    public int Length { get; private set; }

    /// <summary>
    /// The actual data elements stored in the queue.
    /// </summary>
    protected T[] Elements { get; private set; }

    /// <summary>
    /// This is the index where we will dequeue.
    /// </summary>
    public int FrontIndex { get; private set; }

    /// <summary>
    /// This is the index where we will next enqueue a value. 
    /// It is calculated based on the Front Index, Length, and Capacity
    /// </summary>
    public int BackIndex
    {
        get { return (FrontIndex + Length) % Capacity; }
    }

    #endregion

    #region Constructors

    /*** Allows supplying a comparer to find the minimum */
    public Queue(IComparer<T> comparer = null) : this(DefaultCapacity, comparer)
    {
    }

    public Queue(int capacity, IComparer<T> comparer = null)
    {
        // *** Ensure capacity > 0 ***
        Elements = new T[capacity > 0 ? capacity : DefaultCapacity];
        _comparer = comparer ?? Comparer<T>.Default; // *** We need a comparer ****
    }

    #endregion

    #region public methods

    public void Enqueue(T element)
    {
        if (this.Length == Capacity)
        {
            IncreaseCapacity();
        }
        Elements[BackIndex] = element;
        Length++;
    }

    public T Dequeue()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        T element = Elements[FrontIndex];
        Elements[FrontIndex] = default(T);
        Length--;
        FrontIndex = (FrontIndex + 1) % Capacity;
        return element;
    }

    public T Peek()
    {
        if (this.Length < 1)
        {
            throw new InvalidOperationException("Error: Queue is empty");
        }
        return Elements[FrontIndex]; // *** You need to index by FrontIndex ***
    }

    // *** Added to allow easy enumeration. ***
    public IEnumerable<T> Items
    {
        get
        {
            for (var i = 0; i < Length; i++)
                yield return Elements[(FrontIndex + i) % Capacity];
        }
    }

    public T Min() 
    {
        // *** Cannot get min if there are no items.
        if (Length < 1)
            throw new InvalidOperationException("Error: Queue is empty");

        // *** Need to start searching from FrontIndex
        var min = Elements[FrontIndex];
        for (var i = 1; i < Length; i++)
        {
            var item = Elements[(FrontIndex + i) % Capacity];
            // *** Need  the comparer for comparison of 'T'
            if (_comparer.Compare(min, item) > 0) // 'min' > 'item'
                min = item;
        }
        return min;
    }

    public void Display()
    {
        foreach (T item in Items) // *** We can use the "Items" enumerable here.
        {
            Console.Write(item);
            Console.Write("-->");
        }
        Console.WriteLine();
    }

    #endregion

    #region private methods

    /// <summary>
    /// Private function for increasing the size of the queue 
    /// if we run out of space and need to add another element
    /// </summary>
    private void IncreaseCapacity()
    {
        // *** Need to allocate a new larger array for the items
        var newElementsArray = new T[this.Capacity * 2];
        // *** And fill it with our current elements sequentially
        var ndx = 0;
        foreach (var item in Items)
            newElementsArray[ndx++] = item;

        // *** Now we can assign it and reset the front index.
        this.Elements = newElementsArray;
        this.FrontIndex = 0;
    }

    #endregion
}