演算法題常見模板

二声發表於2024-07-01

資料結構類

  1. 陣列 (Array)

    • 雙指標 (Two Pointers)
    • 滑動視窗 (Sliding Window)
    • 字首和 (Prefix Sum)
  2. 連結串列 (Linked List)

    • 單連結串列反轉 (Reverse Linked List)
    • 連結串列合併 (Merge Linked Lists)
    • 連結串列環檢測 (Cycle Detection)
  3. 棧和佇列 (Stack and Queue)

    • 棧的基本操作 (Basic Stack Operations)
    • 佇列的基本操作 (Basic Queue Operations)
    • 單調棧 (Monotonic Stack)
    • 優先佇列 (Priority Queue)
  4. 樹 (Tree)

    • 二叉樹遍歷 (Binary Tree Traversal)
    • 二叉搜尋樹 (Binary Search Tree)
    • 平衡二叉樹 (Balanced Binary Tree)
    • 樹的最大/最小深度 (Max/Min Depth of Tree)
    • 樹的路徑問題 (Tree Path Problems)
  5. 圖 (Graph)

    • 圖的遍歷 (Graph Traversal: DFS and BFS)
    • 最短路徑 (Shortest Path)
    • 拓撲排序 (Topological Sorting)
    • 連通分量 (Connected Components)

題目型別類

  1. 排序與搜尋 (Sorting and Searching)

    • 經典排序演算法 (Classic Sorting Algorithms: Quick Sort, Merge Sort, etc.)
    • 二分查詢 (Binary Search)
    • 快速選擇 (Quick Select)
  2. 動態規劃 (Dynamic Programming)

    • 斐波那契數列 (Fibonacci Sequence)
    • 揹包問題 (Knapsack Problem)
    • 最長公共子序列 (Longest Common Subsequence)
    • 最長遞增子序列 (Longest Increasing Subsequence)
  3. 回溯法 (Backtracking)

    • 排列與組合 (Permutations and Combinations)
    • N皇后問題 (N-Queens Problem)
    • 子集生成 (Subset Generation)
  4. 貪心演算法 (Greedy Algorithm)

    • 區間排程 (Interval Scheduling)
    • 跳躍遊戲 (Jump Game)
    • 分糖果問題 (Candy Distribution)
  5. 字串 (String)

    • 字串匹配 (String Matching: KMP, Rabin-Karp, etc.)
    • 字串變換 (String Transformation)
    • 最長公共子串 (Longest Common Substring)
  6. 數學與位運算 (Math and Bit Manipulation)

    • 素數問題 (Prime Numbers)
    • 數學組合 (Mathematical Combinations)
    • 位運算技巧 (Bit Manipulation Tricks)

資料結構類

1. 陣列 (Array)

雙指標 (Two Pointers)

模板:刪除排序陣列中的重複項

public int RemoveDuplicates(int[] nums) {
    if (nums.Length == 0) return 0;
    int i = 0;
    for (int j = 1; j < nums.Length; j++) {
        if (nums[j] != nums[i]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;
}
滑動視窗 (Sliding Window)

模板:最大連續子陣列和

public int MaxSubArray(int[] nums) {
    int maxSum = nums[0];
    int currentSum = nums[0];
    for (int i = 1; i < nums.Length; i++) {
        currentSum = Math.Max(nums[i], currentSum + nums[i]);
        maxSum = Math.Max(maxSum, currentSum);
    }
    return maxSum;
}
字首和 (Prefix Sum)

模板:陣列的字首和

public int[] PrefixSum(int[] nums) {
    int[] prefixSum = new int[nums.Length];
    prefixSum[0] = nums[0];
    for (int i = 1; i < nums.Length; i++) {
        prefixSum[i] = prefixSum[i - 1] + nums[i];
    }
    return prefixSum;
}

2. 連結串列 (Linked List)

單連結串列反轉 (Reverse Linked List)

模板:反轉連結串列

public ListNode ReverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}
連結串列合併 (Merge Linked Lists)

模板:合併兩個有序連結串列

public ListNode MergeTwoLists(ListNode l1, ListNode l2) {
    if (l1 == null) return l2;
    if (l2 == null) return l1;
    if (l1.val < l2.val) {
        l1.next = MergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = MergeTwoLists(l1, l2.next);
        return l2;
    }
}
連結串列環檢測 (Cycle Detection)

模板:檢測連結串列環

public bool HasCycle(ListNode head) {
    if (head == null || head.next == null) return false;
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) return false;
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
}

3. 棧和佇列 (Stack and Queue)

棧的基本操作 (Basic Stack Operations)

模板:棧的基本操作

Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int top = stack.Peek();
stack.Pop();
bool isEmpty = stack.Count == 0;
佇列的基本操作 (Basic Queue Operations)

模板:佇列的基本操作

Queue<int> queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
int front = queue.Peek();
queue.Dequeue();
bool isEmpty = queue.Count == 0;
單調棧 (Monotonic Stack)

模板:單調棧用於下一個更大元素問題

public int[] NextGreaterElement(int[] nums) {
    int[] result = new int[nums.Length];
    Stack<int> stack = new Stack<int>();
    for (int i = nums.Length - 1; i >= 0; i--) {
        while (stack.Count > 0 && stack.Peek() <= nums[i]) {
            stack.Pop();
        }
        result[i] = stack.Count == 0 ? -1 : stack.Peek();
        stack.Push(nums[i]);
    }
    return result;
}
優先佇列 (Priority Queue)

模板:優先佇列的基本操作

PriorityQueue<int, int> pq = new PriorityQueue<int, int>();
pq.Enqueue(1, 1);
pq.Enqueue(2, 2);
int top = pq.Dequeue();

4. 樹 (Tree)

二叉樹遍歷 (Binary Tree Traversal)

模板:二叉樹的前序遍歷

public IList<int> PreorderTraversal(TreeNode root) {
    List<int> result = new List<int>();
    PreorderHelper(root, result);
    return result;
}

private void PreorderHelper(TreeNode node, List<int> result) {
    if (node == null) return;
    result.Add(node.val);
    PreorderHelper(node.left, result);
    PreorderHelper(node.right, result);
}
二叉搜尋樹 (Binary Search Tree)

模板:驗證二叉搜尋樹

public bool IsValidBST(TreeNode root) {
    return Validate(root, null, null);
}

private bool Validate(TreeNode node, int? lower, int? upper) {
    if (node == null) return true;
    int val = node.val;
    if (lower != null && val <= lower) return false;
    if (upper != null && val >= upper) return false;
    if (!Validate(node.right, val, upper)) return false;
    if (!Validate(node.left, lower, val)) return false;
    return true;
}
平衡二叉樹 (Balanced Binary Tree)

模板:判斷平衡二叉樹

public bool IsBalanced(TreeNode root) {
    return CheckHeight(root) != -1;
}

private int CheckHeight(TreeNode node) {
    if (node == null) return 0;
    int left = CheckHeight(node.left);
    if (left == -1) return -1;
    int right = CheckHeight(node.right);
    if (right == -1) return -1;
    if (Math.Abs(left - right) > 1) return -1;
    return Math.Max(left, right) + 1;
}
樹的最大/最小深度 (Max/Min Depth of Tree)

模板:二叉樹的最大深度

public int MaxDepth(TreeNode root) {
    if (root == null) return 0;
    int leftDepth = MaxDepth(root.left);
    int rightDepth = MaxDepth(root.right);
    return Math.Max(leftDepth, rightDepth) + 1;
}
樹的路徑問題 (Tree Path Problems)

模板:二叉樹的所有路徑

public IList<string> BinaryTreePaths(TreeNode root) {
    List<string> paths = new List<string>();
    if (root == null) return paths;
    if (root.left == null && root.right == null) paths.Add(root.val.ToString());
    if (root.left != null) foreach (var path in BinaryTreePaths(root.left)) paths.Add(root.val + "->" + path);
    if (root.right != null) foreach (var path in BinaryTreePaths(root.right)) paths.Add(root.val + "->" + path);
    return paths;
}

5. 圖 (Graph)

圖的遍歷 (Graph Traversal: DFS and BFS)

模板:深度優先搜尋

public void DFS(int[,] graph, int start) {
    bool[] visited = new bool[graph.GetLength(0)];
    Stack<int> stack = new Stack<int>();
    stack.Push(start);
    while (stack.Count > 0) {
        int node = stack.Pop();
        if (!visited[node]) {
            visited[node] = true;
            Console.WriteLine($"Visited {node}");
            for (int i = 0; i < graph.GetLength(1); i++) {
                if (graph[node, i] == 1 && !visited[i]) {
                    stack.Push(i);
                }
            }
        }
    }
}

模板:廣度優先搜尋

public void BFS(int[,] graph, int start) {
    bool[] visited = new bool[graph.GetLength(0)];
    Queue<int> queue = new Queue<int>();
    queue.Enqueue(start);
    visited[start] = true;
    while (queue.Count > 0) {
        int node = queue.Dequeue();
        Console.WriteLine($"Visited {node}");
        for (int i = 0; i < graph.GetLength(1); i++) {
            if (graph[node, i] == 1 && !visited[i]) {
                queue.Enqueue(i);
                visited[i] = true;
            }
        }
    }
}
最短路徑 (Shortest Path)

模板:Dijkstra演算法

public int[] Dijkstra(int[,] graph, int start) {
    int n = graph.GetLength(0);
    int[] dist = new int[n];
    bool[] visited = new bool[n];


    for (int i = 0; i < n; i++) dist[i] = int.MaxValue;
    dist[start] = 0;
    for (int i = 0; i < n; i++) {
        int u = -1;
        for (int j = 0; j < n; j++) {
            if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
                u = j;
            }
        }
        if (dist[u] == int.MaxValue) break;
        visited[u] = true;
        for (int v = 0; v < n; v++) {
            if (!visited[v] && graph[u, v] != 0 && dist[u] + graph[u, v] < dist[v]) {
                dist[v] = dist[u] + graph[u, v];
            }
        }
    }
    return dist;
}
拓撲排序 (Topological Sorting)

模板:Kahn's演算法

public List<int> TopologicalSort(int[,] graph) {
    int n = graph.GetLength(0);
    int[] inDegree = new int[n];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (graph[i, j] != 0) inDegree[j]++;
        }
    }
    Queue<int> queue = new Queue<int>();
    for (int i = 0; i < n; i++) {
        if (inDegree[i] == 0) queue.Enqueue(i);
    }
    List<int> topOrder = new List<int>();
    while (queue.Count > 0) {
        int node = queue.Dequeue();
        topOrder.Add(node);
        for (int i = 0; i < n; i++) {
            if (graph[node, i] != 0) {
                inDegree[i]--;
                if (inDegree[i] == 0) queue.Enqueue(i);
            }
        }
    }
    return topOrder;
}
連通分量 (Connected Components)

模板:使用DFS尋找連通分量

public int CountConnectedComponents(int[,] graph) {
    int n = graph.GetLength(0);
    bool[] visited = new bool[n];
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (!visited[i]) {
            DFS(graph, i, visited);
            count++;
        }
    }
    return count;
}

private void DFS(int[,] graph, int node, bool[] visited) {
    Stack<int> stack = new Stack<int>();
    stack.Push(node);
    while (stack.Count > 0) {
        int u = stack.Pop();
        if (!visited[u]) {
            visited[u] = true;
            for (int v = 0; v < graph.GetLength(1); v++) {
                if (graph[u, v] == 1 && !visited[v]) {
                    stack.Push(v);
                }
            }
        }
    }
}

題目型別類

1. 排序與搜尋 (Sorting and Searching)

經典排序演算法 (Classic Sorting Algorithms: Quick Sort, Merge Sort, etc.)

模板:快速排序

public void QuickSort(int[] nums, int left, int right) {
    if (left < right) {
        int pivotIndex = Partition(nums, left, right);
        QuickSort(nums, left, pivotIndex - 1);
        QuickSort(nums, pivotIndex + 1, right);
    }
}

private int Partition(int[] nums, int left, int right) {
    int pivot = nums[right];
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (nums[j] < pivot) {
            i++;
            Swap(nums, i, j);
        }
    }
    Swap(nums, i + 1, right);
    return i + 1;
}

private void Swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

模板:二分查詢

public int BinarySearch(int[] nums, int target) {
    int left = 0, right = nums.Length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) return mid;
        if (nums[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
快速選擇 (Quick Select)

模板:快速選擇

public int QuickSelect(int[] nums, int k) {
    return QuickSelect(nums, 0, nums.Length - 1, k - 1);
}

private int QuickSelect(int[] nums, int left, int right, int k) {
    if (left == right) return nums[left];
    int pivotIndex = Partition(nums, left, right);
    if (k == pivotIndex) return nums[k];
    else if (k < pivotIndex) return QuickSelect(nums, left, pivotIndex - 1, k);
    else return QuickSelect(nums, pivotIndex + 1, right, k);
}

private int Partition(int[] nums, int left, int right) {
    int pivot = nums[right];
    int i = left;
    for (int j = left; j < right; j++) {
        if (nums[j] < pivot) {
            Swap(nums, i, j);
            i++;
        }
    }
    Swap(nums, i, right);
    return i;
}

private void Swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

2. 動態規劃 (Dynamic Programming)

斐波那契數列 (Fibonacci Sequence)

模板:斐波那契數列

public int Fib(int n) {
    if (n <= 1) return n;
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}
揹包問題 (Knapsack Problem)

模板:0-1揹包問題

public int Knapsack(int[] weights, int[] values, int capacity) {
    int n = weights.Length;
    int[,] dp = new int[n + 1, capacity + 1];
    for (int i = 1; i <= n; i++) {
        for (int w = 1; w <= capacity; w++) {
            if (weights[i - 1] <= w) {
                dp[i, w] = Math.Max(dp[i - 1, w], dp[i - 1, w - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i, w] = dp[i - 1, w];
            }
        }
    }
    return dp[n, capacity];
}
最長公共子序列 (Longest Common Subsequence)

模板:最長公共子序列

public int LongestCommonSubsequence(string text1, string text2) {
    int m = text1.Length;
    int k = text2.Length;
    int[,] dp = new int[m + 1, k + 1];
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= k; j++) {
            if (text1[i - 1] == text2[j - 1]) {
                dp[i, j] = dp[i - 1, j - 1] + 1;
            } else {
                dp[i, j] = Math.Max(dp[i - 1, j], dp[i, j - 1]);
            }
        }
    }
    return dp[m, k];
}
最長遞增子序列 (Longest Increasing Subsequence)

模板:最長遞增子序列

public int LengthOfLIS(int[] nums) {
    if (nums.Length == 0) return 0;
    int[] dp = new int[nums.Length];
    int maxLen = 1;
    for (int i = 0; i < nums.Length; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.Max(dp[i], dp[j] + 1);
            }
        }
        maxLen = Math.Max(maxLen, dp[i]);
    }
    return maxLen;
}

3. 回溯法 (Backtracking)

排列與組合 (Permutations and Combinations)

模板:全排列

public IList<IList<int>> Permute(int[] nums) {
    List<IList<int>> res = new List<IList<int>>();
    bool[] used = new bool[nums.Length];
    Backtrack(nums, new List<int>(), used, res);
    return res;
}

private void Backtrack

(int[] nums, List<int> tempList, bool[] used, List<IList<int>> res) {
    if (tempList.Count == nums.Length) {
        res.Add(new List<int>(tempList));
    } else {
        for (int i = 0; i < nums.Length; i++) {
            if (used[i]) continue;
            used[i] = true;
            tempList.Add(nums[i]);
            Backtrack(nums, tempList, used, res);
            used[i] = false;
            tempList.RemoveAt(tempList.Count - 1);
        }
    }
}
子集 (Subsets)

模板:子集

public IList<IList<int>> Subsets(int[] nums) {
    List<IList<int>> res = new List<IList<int>>();
    Backtrack(nums, 0, new List<int>(), res);
    return res;
}

private void Backtrack(int[] nums, int start, List<int> tempList, List<IList<int>> res) {
    res.Add(new List<int>(tempList));
    for (int i = start; i < nums.Length; i++) {
        tempList.Add(nums[i]);
        Backtrack(nums, i + 1, tempList, res);
        tempList.RemoveAt(tempList.Count - 1);
    }
}
八皇后問題 (N-Queens)

模板:八皇后問題

public IList<IList<string>> SolveNQueens(int n) {
    List<IList<string>> res = new List<IList<string>>();
    char[][] board = new char[n][];
    for (int i = 0; i < n; i++) {
        board[i] = new char[n];
        Array.Fill(board[i], '.');
    }
    Backtrack(board, 0, res);
    return res;
}

private void Backtrack(char[][] board, int row, List<IList<string>> res) {
    if (row == board.Length) {
        List<string> tempList = new List<string>();
        foreach (var r in board) {
            tempList.Add(new string(r));
        }
        res.Add(tempList);
    } else {
        for (int col = 0; col < board.Length; col++) {
            if (IsValid(board, row, col)) {
                board[row][col] = 'Q';
                Backtrack(board, row + 1, res);
                board[row][col] = '.';
            }
        }
    }
}

private bool IsValid(char[][] board, int row, int col) {
    for (int i = 0; i < row; i++) {
        if (board[i][col] == 'Q') return false;
    }
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q') return false;
    }
    for (int i = row - 1, j = col + 1; i >= 0 && j < board.Length; i--, j++) {
        if (board[i][j] == 'Q') return false;
    }
    return true;
}

4. 貪心演算法 (Greedy Algorithm)

區間排程 (Interval Scheduling)

模板:區間排程問題

public int IntervalSchedule(int[][] intervals) {
    if (intervals.Length == 0) return 0;
    Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1]));
    int end = intervals[0][1];
    int count = 1;
    for (int i = 1; i < intervals.Length; i++) {
        if (intervals[i][0] >= end) {
            end = intervals[i][1];
            count++;
        }
    }
    return count;
}
跳躍遊戲 (Jump Game)

模板:跳躍遊戲

public bool CanJump(int[] nums) {
    int maxReach = 0;
    for (int i = 0; i < nums.Length; i++) {
        if (i > maxReach) return false;
        maxReach = Math.Max(maxReach, i + nums[i]);
    }
    return true;
}
分配問題 (Assignment Problem)

模板:分配問題

public int AssignCookies(int[] g, int[] s) {
    Array.Sort(g);
    Array.Sort(s);
    int i = 0, j = 0;
    while (i < g.Length && j < s.Length) {
        if (g[i] <= s[j]) i++;
        j++;
    }
    return i;
}

5. 位運算 (Bit Manipulation)

基本位操作 (Basic Bit Operations)

模板:位操作模板

// 獲取整數的二進位制表示中1的個數
public int HammingWeight(uint n) {
    int count = 0;
    while (n != 0) {
        count++;
        n &= (n - 1);
    }
    return count;
}
異或操作 (XOR Operation)

模板:找出陣列中唯一的一個重複數字

public int SingleNumber(int[] nums) {
    int result = 0;
    foreach (var num in nums) {
        result ^= num;
    }
    return result;
}

相關文章