《圖說演算法-使用C#》要點摘錄 – 「棧與隊列」

書名:《圖說演算法-使用C#》

作者:吳燦銘、胡昭民

所讀版本:博碩文化

數組實作Stack

  • public class ArrayStack
    {
       int[] stack;
       int top; //頂端索引

       public ArrayStack(int size)
      {
           stack = new int[size];
           top = -1; //實例化後默認頂端索引為-1(無元素)
      }

       public bool Push(int data)
      {
           if (top >= stack.Length) { return false; } //判斷Stack是否已經滿了
           stack[++top] = data;//索引+1,把數據入堆
           return true;
      }

       public bool IsEmpty()
      {
           return top == -1;
      }

       public int Pop()
      {
           if (IsEmpty()) { return -1; } //判斷Stack是否為空
           return stack[top--]; //把數據出堆,索引-1
      }
    }

鏈表實作Stack

  • public class LinkedListStack
    {
       public Node front; //指向堆的底端
       public Node rear; //指向堆的頂端

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

       public void OutputStack()
      {
           Node curr = front;
           while(curr != null)
          {
               Console.Write(curr.val + " ");
               curr = curr.next;
          }
           Console.WriteLine();
      }

       public void Push(int data)
      {
           Node newNode = new Node(data);
           if (IsEmpty())
          {
               front = newNode;
               rear = newNode;
          }
           else
          {
               rear.next = newNode;
               rear = newNode;
          }
      }

       public void Pop()
      {
           Node target;
           if (IsEmpty()) { return; }
           target = front;
           if(target == rear) //只有一個元素
          {
               front = null;
               rear = null;
          }
           else
          {
               while(target.next != rear)
              {
                   target = target.next;
              }
               target.next = rear.next;
               rear = target;
          }
      }
    }

河內塔算法

  • 假設有A, B, C三個木樁和n個大小不一的套環,由小到大的編號為1~n,編號越大,直徑越大。開始的時候,n個套環套在A木樁上。

    • 直徑較小的套環永遠置於直徑較大的套環上。

    • 套環可任意地由任何一個木樁移到其他的木樁上。

    • 每一次僅能移動一個套環,而且只能從最上面的套環開始移動。

  • 當有n個盤子時,河內塔的移動步驟

    • 將n – 1個盤子從木樁1移動到木樁2

    • 將n個最大盤子,從木樁1移動到木樁3

    • 將n-1 個盤子,從木樁2移動到木樁3

  • public static void Hanoi(int n, int p1, int p2, int p3)
    {
       if(n == 1)
      {
           Console.WriteLine($"Plate Moved From {p1} To {p3}");
      }
       else
      {
           Hanoi(n - 1, p1, p3, p2);
           Console.WriteLine($"Plate Moved From {p1} To {p3}");
           Hanoi(n - 1, p2, p1, p3);
      }
    }

八皇后算法

  • 在一個8*8棋盤內,置入一個皇后,確保這個位置不會被先前放置的皇后吃掉(直、橫、斜沒有其他皇后),就將這個皇后存入Stack。

  • 當放置新皇后的該行的8個位置都沒有辦法放置新皇后,就從Stack取出上一個皇后的位置,並在那行找一個新的位置放置皇后,然後再將該位置存入Stack中(BackTracking)。

  • static int solutionCnt = 0;

    public static int TotalQueens()
    {
       int[,] chessBoard = new int[8, 8];
       BackTrack(chessBoard, 0);
       return solutionCnt;
    }

    static void BackTrack(int[,] chessBoard, int currRow)
    {
       //Loop Column of chessBoard
       for (int col = 0; col < 8; col++)
      {
           if (chessBoard[currRow, col] == 0) //Placable
          {
               chessBoard[currRow, col] = 1; //Place Queen
               SetAttackRange(chessBoard, currRow, col, 1); //Set Attack Range

               if(currRow + 1 == 8)
              {
                   //Current Combination Confirmed
                   solutionCnt++;
              }
               else
              {
                   //Move To Next Row
                   BackTrack(chessBoard, currRow + 1);
              }

               //Remove Queen
               chessBoard[currRow, col] = 0;
               SetAttackRange(chessBoard, currRow, col, -1);

          }
      }
    }

    //標記攻撃範圍
    static void SetAttackRange(int[,] chessBoard, int row, int col, int change)
    {
       //縱橫
       for (int i = 0; i < 8; i++)
      {
           if (i != col) { chessBoard[row, i] += change; }
           if (i != row) { chessBoard[i, col] += change; }
      }

       //斜
       //Top Left
       int y = row - 1;
       int x = col - 1;
       while (y >= 0 && x >= 0) { chessBoard[y--, x--] += change; }
       //Top Right
       y = row - 1;
       x = col + 1;
       while (y >= 0 && x < 8) { chessBoard[y--, x++] += change; }
       //Bottom Left
       y = row + 1;
       x = col - 1;
       while(y < 8 && x >= 0) { chessBoard[y++, x--] += change;}
       //Bottom Right
       y = row + 1;
       x = col + 1;
       while(y < 8 && x < 8) { chessBoard[y++, x++] += change; }

    }

數組實作Queue

  • public class ArrayQueue
    {
       int[] queue;
       int front;
       int rear;

       public ArrayQueue(int size)
      {
           queue = new int[size];
           front = -1;
           rear = -1;
      }

       public bool Enqueue(int data)
      {
           if(rear + 1 == queue.Length) { return false; }

           queue[++rear] = data;
           return true;
      }

       public int Dequeue()
      {            
           if(rear > front)
          {
               front++;
               int output = queue[front];
               queue[front] = 0;
               return output;
          }
           return -1;
      }        
    }

鏈表實作Queue

  • public class LinkedListQueue
    {
       public Node front;
       public Node rear;

       public LinkedListQueue()
      {
           front = null;
           rear = null;
      }

       public bool Enqueue(int data)
      {
           Node newNode = new Node(data);

           if(front == null)
          {
               front = newNode;
          }
           else
          {
               rear.next = newNode;
          }
           rear = newNode;
           return true;
      }

       public int Dequeue()
      {
           if (front == null) { return -1; }
           int value = front.val;
           if (front == rear) { rear = null; }
           front = front.next;
           return value;
      }
    }

雙向Queue

  • 前後兩端都可以輸入/取出

  • 加入及取出時,指標往Queue的中央移動

  • public class DoublyLinkedListQueue
    {
       public Node front;
       public Node rear;

       public DoublyLinkedListQueue()
      {
           front = null;
           rear = null;
      }

       //Only Insert Front Head
       public bool Enqueue(int data)
      {
           Node newNode = new Node(data);

           if(rear == null)
          {
               front = newNode;
          }
           else
          {
               rear.next = newNode;
          }
           rear = newNode;
           return true;
      }

       //Dequeue From Both Sides
       public int Dequeue(bool isFront)
      {
           int value;
           Node tmp;
           Node start;

           if(front != null && isFront)
          {
               value = front.val;
               front = front.next;
               return value;
          }
           else if(rear != null && !isFront)
          {
               start = front; //save ref of front;
               value = rear.val;

               tmp = front;
               //track to the node before rear node
               while(front.next != rear && front.next != null)
              {
                   front = front.next;
                   tmp = front;
              }                
               front = start;
               //set new rear
               rear = tmp;

               //Dequeue Last Node
               if(front.next == null || rear.next == null)
              {
                   front = null;
                   rear = null;                    
              }
               return value;
          }
           else
          {
               return -1;
          }
      }
    }

優先Queue

  • HPOF(Highest Priority Out First)rather than FIFO

  • 為每一個元素賦予優先權,加入元素時任意加入,最高優先權者最先輸出