---
# System prepended metadata

title: Stack and Queue
tags: [提升程式設計師的面試力]

---

---
tags: 提升程式設計師的面試力
---
# Stack and Queue

### Queue

概念：先進先出，先加入的元素會先被移除

方法：

add(item) : 加入元素

remove() : 移除對列的第一個元素

peek() : 取得對列的第一個元素(並不移除)

isEmpty() : 只有對列為空時為真

結構：

實作與Stack類似，不過移除的方向與Stack相反。

```csharp
public class MyQueue<T>
{
    // 建立 LinkedList 結構
    private class QueueNode<T>
    {
        public T data;
        public QueueNode<T> next;

        public QueueNode(T data)
        {
            this.data = data;
        }
    }

    // 對列第一個項目
    private QueueNode<T> first;
    // 對列最後項目
    private QueueNode<T> last;

    // Add
    public void Add(T data)
    {
        var t = new QueueNode<T>(data);
        if (last != null)
        {
            last.next = t;
        }
        last = t;
        if (first == null)
        {
            first = last;
        }
    }

    public T Remove()
    {
        if (first == null) throw new NoSuchElementException();

        T data = first.data;
        first = first.next;
        if (first == null)
        {
            last = null;
        }
        return data;
    }

    public T Peek()
    {
        if (first == null) throw new NoSuchElementException();
        return first.data;
    }

    public bool IsEmpty()
    {
        return first == null;
    }

}
```

### 題目

**用Stack做出Queue**：利用兩個Stack做出一個Queue

提示1：如何移除Stack內最舊的元素？

提示2：如果要呼叫Queue.pop()，可以如何優化？

解法：

```csharp
public class StackQueue<T>
{
    Stack<T> stackNewest, stackOldest;

    public StackQueue()
    {
        stackNewest = new Stack<T>();
        stackOldest = new Stack<T>();
    }

    public bool IsEmpty()
    {
        return Size() == 0;
    }

    public int Size()
    {
        return stackNewest.Count + stackOldest.Count;
    }

    public void Add(T value)
    {
        stackNewest.Push(value);
    }

    private void ShiftStacks()
    {
        if (stackOldest.Count == 0)
        {
            while (stackNewest.Count != 0)
            {
                stackOldest.Push(stackNewest.Pop());
            }
        }
    }

    public T Peek()
    {
        ShiftStacks();
        return stackOldest.Peek();
    }

    public T Remove()
    {
        ShiftStacks();
        return stackOldest.Pop();
    }
}
```

---

**排列Stack內元素**：寫一段程式，讓Stack內最上方的元素是最小的。
限制： 只能使用一個暫存Stack來完成

提示1：有一種排序方法是，遍歷所有元素，取出最大並存入新Stack

提示2：假設第二個Stack已經排序好了，要如何把新元素存入第二個Stack？如何設計一個暫存的空間？

提示3：讓第二個Stack保持排序狀態(最上面最大)，把第一個Stack作為暫存Stack。

解法：

```csharp
public class SortStack : Stack<int>
{
    public void Sort()
    {
        // 亂序Stack
        var s = this;
        // 排序Stack
        Stack<int> r = new();

        while (s.Count != 0)
        {
            int temp = s.Pop();
            while (r.Count != 0 && r.Peek() > temp)
            {
                s.Push(r.Pop());
            }
            r.Push(temp);
        }

        while (r.Count != 0)
        {
            s.Push(r.Pop());
        }
    }
}
```

---

**動物收容所：** 動物收容所內有貓與狗，先進來的動物會優先被領養，提供實作以下方法：enqueue, dequeueAny, dequeueDog, dequeueCat。

提示1：可以用一個串列紀錄所有貓狗，由遍歷的方式取出符合條件的動物，此做法有何利弊？

提示2：如果用兩個串列分別記錄貓狗，要怎麼取得最早的動物？

提示3：在現實生活中，要怎麼依據時序紀錄收入的動物？要怎麼找到最早的動物？要怎麼維護這些資料？

解法

```csharp
public class AnimalQueue
{
    Queue<Dog> dogs = new();
    Queue<Cat> cats = new();
    private int order = 0; // 紀錄收容順序

    public void Enqueue(Animal a)
    {
        a.SetOrder(order);
        order++;

        if (a is Dog) dogs.Enqueue((Dog)a);
        else if (a is Cat) cats.Enqueue((Cat)a);
    }

    public Animal DequeueAny()
    {
        if (dogs.Count == 0) return DequeueCat();
        else if (cats.Count == 0) return DequeueDog();

        var dog = dogs.Peek();
        var cat = cats.Peek();

        if (dog.IsOlderThan(cat))
        {
            return DequeueDog();
        }
        else
        {
            return DequeueCat();
        }
    }

    public Dog DequeueDog() => dogs.Dequeue();

    public Cat DequeueCat() => cats.Dequeue();

}
```