C sharp:DequeOfT

From GPWiki

Files:GUITutorial_warn.gif The Game Programming Wiki has moved! Files:GUITutorial_warn.gif

The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server.

However, the GPWiki forums are still active! Come say hello.

Note: The correct name of this page is C#:Deque<T>; however, due to technical restrictions it is represented as C_sharp:DequeOfT instead.

The folllowing code is a C# implementation of a generic double-ended queue (or deque). Implements ICollection<T> and IEnumerable<T> so you can use the foreach keyword to iterate through the items. Has operations AddToHead, AddToTail, RemoveFromHead, RemoveFromTail, PeekHead, PeakTail.

Code

using System;
using System.Collections.Generic;
 
namespace GPWiki
{
    /// <summary>
    /// A double-ended queue. Provides fast insertion and removal from the head or tail end, 
    /// and fast indexed access.
    /// </summary>
    /// <typeparam name="T">The type of item to store in the deque.</typeparam>
    public class Deque<T> : IList<T>
    {
        //Constants
        private const int Min_Capacity = 4;
        //Fields
        private int _head = 0;
        private int _capacity = Min_Capacity;
        private int _count;
        private T[] _data;
        /// <summary>
        /// Gets the number of items in the deque.
        /// </summary>
        public int Count
        {
            get { return _count; }
        }
        /// <summary>
        /// Gets or sets the capacity of the deque. If the count exceeds the 
        /// capacity, the capacity will be automatically increased.
        /// </summary>
        public int Capacity
        {
            get { return _capacity; }
            set
            {
                int newCapacity = Math.Max(value, Math.Max(_count, Min_Capacity));
                T[] temp = new T[newCapacity];
                int length = Math.Min(_capacity - _head, _count);
                Array.Copy(_data, _head, temp, 0, length);
                Array.Copy(_data, 0, temp, length, _count - length);
                _data = temp;
                _head = 0;
                _capacity = newCapacity;
            }
        }
        /// <summary>
        /// Creates a new deque.
        /// </summary>
        public Deque()
        {
            _data = new T[_capacity];
        }
        /// <summary>
        /// Creates a new deque.
        /// </summary>
        /// <param name="capacity">The initial capacity to give the deque.</param>
        public Deque(int capacity)
        {
            _capacity = capacity;
            _data = new T[_capacity];
        }
        /// <summary>
        /// Creates a new deque from a collection.
        /// </summary>
        /// <param name="items">A collection of items of type T.</param>
        public Deque(ICollection<T> items)
        {
            Capacity = items.Count;
            foreach (T item in items)
            {
                this.Add(item);
            }
        }
        private int Tail
        {
            get { return (_head + _count) % _capacity; }
        }
        //Increments (and wraps if necessary) an index
        private int Increment(int index)
        {
            return (index + 1) % _capacity;
        }
        //Decrements (and wraps if necessary) an index
        private int Decrement(int index)
        {
            return (index + _capacity - 1) % _capacity;
        }
        /// <summary>
        /// Adds an item to the tail end of the deque.
        /// </summary>
        /// <param name="item">The item to be added.</param>
        public void AddToTail(T item)
        {
            if (_count + 1> _capacity) Capacity *= 2;
            _data[Tail] = item;
            _count++;
        }
        /// <summary>
        /// Removes an item from the tail of the deque.
        /// </summary>
        /// <returns>An item of type T.</returns>
        public T RemoveFromTail()
        {
            if (_count < 0)
            {
                throw new InvalidOperationException("Deque is empty.");
            }
            T item = _data[Tail];
            _data[Tail] = default(T);
            _count--;
            return item;
        }
        /// <summary>
        /// Adds an item to the head of the deque.
        /// </summary>
        /// <param name="item">The item to be added.</param>
        public void AddToHead(T item)
        {
            _count++;
            if (_count > _capacity) Capacity *= 2;
            if (_count > 1) _head = Decrement(_head);
            _data[_head] = item;
        }
        /// <summary>
        /// Removes an item from the head of the deque.
        /// </summary>
        /// <returns>An item of type T.</returns>
        public T RemoveFromHead()
        {
            _count--;
            if (_count < 0)
            {
                throw new InvalidOperationException("Deque is empty.");
            }
            T item = _data[_head];
            _data[_head] = default(T);
            _head = Increment(_head);
            return item;
        }
        /// <summary>
        /// Gets the item at the head of the deque.
        /// </summary>
        /// <returns>An item of type T.</returns>
        public T PeekHead()
        {
            return _data[_head];
        }
        /// <summary>
        /// Gets the item at the tail of the deque.
        /// </summary>
        /// <returns>An item of type T.</returns>
        public T PeekTail()
        {
            return _data[Tail];
        }
        /// <summary>
        /// Gets the item at the specified position.
        /// </summary>
        /// <param name="position">The position of the item to return.</param>
        /// <returns>An item of type T.</returns>
        public T this[int position]
        {
            get
            {
                if (position >= _count || position < 0) throw new ArgumentOutOfRangeException("position");
                return _data[(_head + position) % _capacity];
            }
            set
            {
                if (position >= _count || position < 0) throw new ArgumentOutOfRangeException("position");
                _data[(_head + position) % _capacity] = value;
            }
        }
        /// <summary>
        /// Creates an array of the items in the deque.
        /// </summary>
        /// <returns>An array of type T.</returns>
        public T[] ToArray()
        {
            T[] array = new T[_count];
            CopyTo(array, 0);
            return array;
        }
        /// <summary>
        /// Copies the deque to an array at the specified index.
        /// </summary>
        /// <param name="array">One dimensional array that is the destination of the copied elements.</param>
        /// <param name="arrayIndex">The zero-based index at which copying begins.</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            if (_count == 0) return;
            int length = Math.Min(_capacity - _head, _count);
            Array.Copy(_data, _head, array, 0, length);
            Array.Copy(_data, 0, array, length, _count - length);
        }
        /// <summary>
        /// Gets and removes an item at the specified index. 
        /// </summary>
        /// <param name="index">The index at which to remove the item.</param>
        /// <returns>An item of type T.</returns>
        public T RemoveAt(int index)
        {
            if (index >= _count) throw new ArgumentOutOfRangeException("index");
            int i = (_head + index) % _capacity;
            T item = _data[i];
            if (i < _head)
            {
                Array.Copy(_data, i + 1, _data, i, Tail - i);
                _data[Tail] = default(T);
            }
            else
            {
                Array.Copy(_data, _head, _data, _head + 1, i - _head);
                _data[_head] = default(T);
                _head = Increment(_head);
            }
            _count--;
            return item;
        }
        /// <summary>
        /// Clears all items from the deque.
        /// </summary>
        public void Clear()
        {
            Array.Clear(_data, 0, _capacity);
            _head = 0;
            _count = 0;
        }
        /// <summary>
        /// Gets an enumerator for the deque.
        /// </summary>
        /// <returns>An IEnumerator of type T.</returns>
        public System.Collections.Generic.IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < _count; i++)
            {
                yield return this[i];
            }
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        /// <summary>
        /// Adds an item to the tail of the deque. 
        /// </summary>
        /// <param name="item"></param>
        public void Add(T item)
        {
            AddToTail(item);
        }
        /// <summary>
        /// Checks to see if the deque contains the specified item.
        /// </summary>
        /// <param name="item">The item to search the deque for.</param>
        /// <returns>A boolean, true if deque contains item.</returns>
        public bool Contains(T item)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Equals(item))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Gets whether or not the deque is readonly.
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }
 
        /// <summary>
        /// Removes an item from the deque.
        /// </summary>
        /// <param name="item">The item to be removed.</param>
        /// <returns>Boolean true if the item was removed.</returns>
        public bool Remove(T item)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Equals(item))
                {
                    RemoveAt(i);
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Gets the first index of an item in the list.
        /// </summary>
        /// <param name="item">The item to locate.</param>
        /// <returns>The integer index of the item.</returns>
        public int IndexOf(T item)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Equals(item))
                {
                    return i;
                }
            }
            throw new InvalidOperationException("Item not found.");
        }
        /// <summary>
        /// Inserts an item at the specified index.
        /// </summary>
        /// <param name="index">The index at which to insert the item.</param>
        /// <param name="item">The item to insert.</param>
        public void Insert(int index, T item)
        {
            if (index > _count || index < 0) throw new ArgumentOutOfRangeException("index");
            _count++;
            if (_count > _capacity) Capacity *= 2;
            int split = _capacity - _head;
            int i = (_head + index) % _capacity;
            if (index < split)
            {
                Array.Copy(_data, i, _data, i + 1, _count - index);
            }
            else
            {
                Array.Copy(_data, _head, _data, _head - 1, index);
            }
            _data[i] = item;
        }
 
        void IList<T>.RemoveAt(int index)
        {
            RemoveAt(index);
        }
    }
}

Usage

Using the deque is simple. Just add and removes items from either end. Or you can access items by index.

Deque<int> deque = new Deque<int>();
deque.AddToTail(1);
deque.AddToTail(2);
deque.AddToTail(3);
deque.AddToTail(4);
deque.AddToTail(5);
deque.AddToTail(6);
deque.AddToTail(7);
int index3 = deque[3]; // return the item at index 3 in the deque. (i.e "4")
int last = deque.RemoveFromTail(); //remove and return the item at the tail of list. (i.e "7")
int first = deque.RemoveFromHead(); //remove and return the item at the head of the list (i.e. "1")
//deque will now contain 2,3,4,5,6