数据结构入门模板

一、栈(Stack)

定义

栈是一种**后进先出(LIFO,Last In First Out)**的数据结构。插入和删除操作只能在栈顶进行。

特点

  1. 只能从栈顶操作数据。
  2. 操作简单,时间复杂度为 O ( 1 ) O(1) O(1)

应用场景

  • 表达式求值(如括号匹配)。
  • 深度优先搜索(DFS)。

时间复杂度

操作 时间复杂度
入栈(push) O ( 1 ) O(1) O(1)
出栈(pop) O ( 1 ) O(1) O(1)
访问栈顶元素 O ( 1 ) O(1) O(1)

案例:括号匹配

问题描述:给定一个只包含 () 的字符串,判断括号是否匹配。

C++实现

#include <iostream>
#include <stack>
using namespace std;

bool isValid(string s) {
    
    
    stack<char> stk;
    for (char c : s) {
    
    
        if (c == '(') {
    
    
            stk.push(c);
        } else {
    
    
            if (stk.empty() || stk.top() != '(') {
    
    
                return false;
            }
            stk.pop();
        }
    }
    return stk.empty();
}

int main() {
    
    
    string s = "(())";
    cout << (isValid(s) ? "Valid" : "Invalid") << endl;
    return 0;
}

Python实现

def is_valid(s):
    stack = []
    for c in s:
        if c == '(':
            stack.append(c)
        else:
            if not stack or stack[-1] != '(':
                return False
            stack.pop()
    return len(stack) == 0

s = "(())"
print("Valid" if is_valid(s) else "Invalid")

Java实现

import java.util.Stack;

public class Main {
    
    
    public static boolean isValid(String s) {
    
    
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
    
    
            if (c == '(') {
    
    
                stack.push(c);
            } else {
    
    
                if (stack.isEmpty() || stack.peek() != '(') {
    
    
                    return false;
                }
                stack.pop();
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args) {
    
    
        String s = "(())";
        System.out.println(isValid(s) ? "Valid" : "Invalid");
    }
}

二、队列(Queue)

定义

队列是一种**先进先出(FIFO,First In First Out)**的数据结构,插入操作在队尾,删除操作在队首。

特点

  1. 操作受限。
  2. 时间复杂度为 O ( 1 ) O(1) O(1)

应用场景

  • 任务调度。
  • 宽度优先搜索(BFS)。

时间复杂度

操作 时间复杂度
入队 O ( 1 ) O(1) O(1)
出队 O ( 1 ) O(1) O(1)

案例:二叉树的层序遍历

问题描述:按层序输出二叉树的节点值。

C++实现

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

struct TreeNode {
    
    
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {
    
    }
};

vector<vector<int>> levelOrder(TreeNode* root) {
    
    
    vector<vector<int>> result;
    if (!root) return result;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
    
    
        int size = q.size();
        vector<int> level;
        for (int i = 0; i < size; ++i) {
    
    
            TreeNode* node = q.front(); q.pop();
            level.push_back(node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        result.push_back(level);
    }
    return result;
}

int main() {
    
    
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    vector<vector<int>> result = levelOrder(root);
    for (const auto& level : result) {
    
    
        for (int val : level) {
    
    
            cout << val << " ";
        }
        cout << endl;
    }
    return 0;
}

Python实现

from collections import deque

def level_order(root):
    if not root:
        return []
    result, queue = [], deque([root])
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)
    return result

# TreeNode class definition
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
print(level_order(root))

Java实现

import java.util.*;

class TreeNode {
    
    
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
    
     val = x; }
}

public class Main {
    
    
    public static List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
    
    
            int size = queue.size();
            List<Integer> level = new ArrayList<>();
            for (int i = 0; i < size; i++) {
    
    
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
            result.add(level);
        }
        return result;
    }

    public static void main(String[] args) {
    
    
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        System.out.println(levelOrder(root));
    }
}

三、堆(Heap)

定义

堆是一种基于树的数据结构,分为最大堆和最小堆:

  • 最大堆:根节点值大于给定树中所有子节点的值。
  • 最小堆:根节点值小于给定树中所有子节点的值。

通常使用堆实现一些需要自动索引的场景,如优先队列。

特点

  1. 深度为 ⌊ log ⁡ n ⌋ \lfloor \log n \rfloor logn,操作时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)
  2. 通常用数组表示,父节点、左子节点和右子节点与索引关系为:
    • 父节点: parent ( i ) = ⌊ ( i − 1 ) / 2 ⌋ \text{parent}(i) = \lfloor (i-1)/2 \rfloor parent(i)=⌊(i1)/2
    • 左子节点: left ( i ) = 2 i + 1 \text{left}(i) = 2i + 1 left(i)=2i+1
    • 右子节点: right ( i ) = 2 i + 2 \text{right}(i) = 2i + 2 right(i)=2i+2

应用场景

  • 优先队列(Priority Queue)
  • 最大/最小 K 问题

时间复杂度

操作 时间复杂度
插入(insert) O ( log ⁡ n ) O(\log n) O(logn)
删除(delete) O ( log ⁡ n ) O(\log n) O(logn)
取最大/最小值 O ( 1 ) O(1) O(1)

案例:前 K 个最大元素

问题描述:给定一个数组,找到其中前 K 个最大元素。

C++实现

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

vector<int> topKElements(vector<int>& nums, int k) {
    
    
    priority_queue<int, vector<int>, greater<int>> minHeap;
    for (int num : nums) {
    
    
        minHeap.push(num);
        if (minHeap.size() > k) {
    
    
            minHeap.pop();
        }
    }
    vector<int> result;
    while (!minHeap.empty()) {
    
    
        result.push_back(minHeap.top());
        minHeap.pop();
    }
    return result;
}

int main() {
    
    
    vector<int> nums = {
    
    3, 2, 1, 5, 6, 4};
    int k = 2;
    vector<int> result = topKElements(nums, k);
    for (int val : result) {
    
    
        cout << val << " ";
    }
    cout << endl;
    return 0;
}

Python实现

import heapq

def top_k_elements(nums, k):
    return heapq.nlargest(k, nums)

nums = [3, 2, 1, 5, 6, 4]
k = 2
print(top_k_elements(nums, k))

Java实现

import java.util.*;

public class Main {
    
    
    public static List<Integer> topKElements(int[] nums, int k) {
    
    
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int num : nums) {
    
    
            minHeap.offer(num);
            if (minHeap.size() > k) {
    
    
                minHeap.poll();
            }
        }
        List<Integer> result = new ArrayList<>(minHeap);
        Collections.sort(result, Collections.reverseOrder());
        return result;
    }

    public static void main(String[] args) {
    
    
        int[] nums = {
    
    3, 2, 1, 5, 6, 4};
        int k = 2;
        System.out.println(topKElements(nums, k));
    }
}

下面是并查集和树状数组的内容,整合到现有文档中:


四、并查集(Union-Find)

定义

并查集是一种用于处理动态连通性问题的数据结构,主要支持以下两种操作:

  1. **合并:将两个元素所在的集合合并。
  2. 查找:查询某个元素属于哪个集合。

特点

  1. 常用于解决连通性问题(如网络连通、图的连通分量)。
  2. 通过路径压缩和按秩合并优化,时间复杂度接近 O ( 1 ) O(1) O(1)

应用场景

  • 判断无向图是否有环。
  • 网络连通性问题。
  • 最小生成树(Kruskal算法)。

时间复杂度

操作 时间复杂度
查找 O ( l o g ( n ) ) O(log(n)) O(log(n))
合并 O ( l o g ( n ) ) O(log(n)) O(log(n))

案例:判断图是否连通

问题描述:给定包含 n 个节点的无向图,以及一些边,判断图是否是连通的。

C++实现

#include <iostream>
#include <vector>
using namespace std;

class UnionFind {
    
    
public:
    UnionFind(int n) : parent(n), rank(n, 1) {
    
    
        for (int i = 0; i < n; ++i) parent[i] = i;
    }

    int find(int x) {
    
    
        if (x != parent[x]) {
    
    
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }

    void unionSet(int x, int y) {
    
    
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
    
    
            if (rank[rootX] > rank[rootY]) {
    
    
                parent[rootY] = rootX;
            } else if (rank[rootX] < rank[rootY]) {
    
    
                parent[rootX] = rootY;
            } else {
    
    
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }

private:
    vector<int> parent, rank;
};

int main() {
    
    
    UnionFind uf(5);
    uf.unionSet(0, 1);
    uf.unionSet(1, 2);
    cout << (uf.find(0) == uf.find(2) ? "Connected" : "Not Connected") << endl;
    return 0;
}

Python实现

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [1] * n

    def find(self, x):
        if x != self.parent[x]:
            self.parent[x] = self.find(self.parent[x])  # 路径压缩
        return self.parent[x]

    def union(self, x, y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            if self.rank[rootX] > self.rank[rootY]:
                self.parent[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.parent[rootX] = rootY
            else:
                self.parent[rootY] = rootX
                self.rank[rootX] += 1

uf = UnionFind(5)
uf.union(0, 1)
uf.union(1, 2)
print("Connected" if uf.find(0) == uf.find(2) else "Not Connected")

Java实现

class UnionFind {
    
    
    private int[] parent;
    private int[] rank;

    public UnionFind(int n) {
    
    
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
    
    
            parent[i] = i;
            rank[i] = 1;
        }
    }

    public int find(int x) {
    
    
        if (x != parent[x]) {
    
    
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }

    public void union(int x, int y) {
    
    
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
    
    
            if (rank[rootX] > rank[rootY]) {
    
    
                parent[rootY] = rootX;
            } else if (rank[rootX] < rank[rootY]) {
    
    
                parent[rootX] = rootY;
            } else {
    
    
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        UnionFind uf = new UnionFind(5);
        uf.union(0, 1);
        uf.union(1, 2);
        System.out.println(uf.find(0) == uf.find(2) ? "Connected" : "Not Connected");
    }
}

五、树状数组(Fenwick Tree)

定义

树状数组是一种用于高效维护数组前缀和的数据结构,支持快速更新和查询操作。

特点

  1. 时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)
  2. 适用于动态数组

应用场景

  • 区间查询和更新。
  • 逆序对计数。

时间复杂度

操作 时间复杂度
单点更新 O ( log ⁡ n ) O(\log n) O(logn)
前缀查询 O ( log ⁡ n ) O(\log n) O(logn)

案例:计算数组前缀和

问题描述:支持动态更新和快速前缀和查询。

C++实现

#include <iostream>
#include <vector>
using namespace std;

class FenwickTree {
    
    
public:
    FenwickTree(int n) : tree(n + 1, 0) {
    
    }

    void update(int i, int delta) {
    
    
        while (i < tree.size()) {
    
    
            tree[i] += delta;
            i += i & -i;
        }
    }

    int query(int i) {
    
    
        int sum = 0;
        while (i > 0) {
    
    
            sum += tree[i];
            i -= i & -i;
        }
        return sum;
    }

private:
    vector<int> tree;
};

int main() {
    
    
    FenwickTree ft(5);
    ft.update(1, 5);
    ft.update(2, 3);
    cout << ft.query(2) << endl; // 输出8
    return 0;
}

Python实现

class FenwickTree:
    def __init__(self, n):
        self.tree = [0] * (n + 1)

    def update(self, i, delta):
        while i < len(self.tree):
            self.tree[i] += delta
            i += i & -i

    def query(self, i):
        sum_ = 0
        while i > 0:
            sum_ += self.tree[i]
            i -= i & -i
        return sum_

ft = FenwickTree(5)
ft.update(1, 5)
ft.update(2, 3)
print(ft.query(2))  # 输出8

Java实现

class FenwickTree {
    
    
    private int[] tree;

    public FenwickTree(int n) {
    
    
        tree = new int[n + 1];
    }

    public void update(int i, int delta) {
    
    
        while (i < tree.length) {
    
    
            tree[i] += delta;
            i += i & -i;
        }
    }

    public int query(int i) {
    
    
        int sum = 0;
        while (i > 0) {
    
    
            sum += tree[i];
            i -= i & -i;
        }
        return sum;
    }

    public static void main(String[] args) {
    
    
        FenwickTree ft = new FenwickTree(5);
        ft.update(1, 5);
        ft.update(2, 3);
        System.out.println(ft.query(2)); // 输出8
    }
}