跳转至

数据结构

需求 适合的数据结构 Java 实现方式 特点
后进先出 栈(Stack) Deque<T> stack = new ArrayDeque<>(); 操作快,比Stack类更推荐
先进先出 队列(Queue) Queue<T> queue = new LinkedList<>(); 或 ArrayDeque<>(); LinkedList双端、ArrayDeque快
双端操作 双端队列(Deque) Deque<T> deque = new ArrayDeque<>(); 可做单调队列、滑动窗口
快速查找 哈希表 HashMap<K,V>,HashSet<E> 常用于计数、查重
有序集合 TreeSet / TreeMap TreeSet<>,TreeMap<> 基于红黑树,自动排序
区间操作 树状数组 / 线段树 自己实现类 用于区间求和、区间最值等
优先处理 堆(优先队列) PriorityQueue<T> 默认小顶堆,可自定义比较器
图 邻接表 / 邻接矩阵 用 List<List<Integer>> 或 int[][] 视题目需求选择稀疏或稠密
树结构 树节点类 + DFS/BFS 自定义 TreeNode 结构 常结合递归和栈使用

优先队列

**原理:**基于堆结构实现(大根堆或小根堆),自动对元素进行排序,每次取出堆顶元素(最大或最小元素)。插入和删除操作的时间复杂度均为 O (log n)。

**应用:**处理需要频繁获取最大或最小元素的问题,如 Dijkstra 算法中用于选取当前距离最小的节点、任务调度问题(每次选取优先级最高的任务)等。

哈希表

**原理:**过哈希函数将键映射到数组中的某个位置,实现快速的查找、插入和删除操作。哈希函数的设计至关重要,要尽量减少冲突,常用的解决冲突的方法有开放寻址法和链地址法。

**应用:**快速查找数据(如判断某个元素是否存在于集合中)、统计元素的出现次数(如在 “字母异位词分组” 问题中,用哈希表记录每个字母排序后的字符串对应的分组)等。

并查集

**原理:**每个集合用一个根节点表示,每个节点存储其父节点,通过路径压缩和按秩合并优化,使得查找和合并操作的时间复杂度接近 O (1)。

**应用:**解决不相交集的合并

int fa[MAXN];
inline void init(int n)
{
    for (int i = 1; i <= n; ++i)
        fa[i] = i;
}
int find(int x)
{
    if(x == fa[x])
        return x;
    else{
        fa[x] = find(fa[x]);  //父节点设为根节点
        return fa[x];         //返回父节点
    }
}
inline void merge(int i, int j)
{
    fa[find(i)] = find(j);
}
inline void init(int n)
{
    for (int i = 1; i <= n; ++i)
    {
        fa[i] = i;
        rank[i] = 1;
    }
}
inline void merge(int i, int j)
{
    int x = find(i), y = find(j);    //先找到两个根节点
    if (rank[x] <= rank[y])
        fa[x] = y;
    else
        fa[y] = x;
    if (rank[x] == rank[y] && x != y)
        rank[y]++;                   //如果深度相同且根节点不同,则新的根节点的深度+1
}

单调栈

**原理:**栈中的元素保持单调递增或递减的顺序。当新元素入栈时,弹出栈中不满足单调性的元素,直到栈顶元素满足单调性条件,然后将新元素入栈。

**应用:**求解序列中每个数最左边或最右边第一个大于(或小于)其的数

​ 求解序列中所有子序列的最值和

        int[] left = new int[n];
        int[] right = new int[n];
        Deque<Integer> q = new LinkedList<>();
        //找下一个小于的,满足条件就出栈,出栈的代表已经找到了,他记录找到的坐标
        for (int i = 0; i < n; i++) {
            while (!q.isEmpty() && arr[q.peekLast()] >= arr[i]) {
                int index = q.pollLast();
                left[index]=i;
            }
            q.offerLast(i);
        }
        //最后没有找到记录为-1
        while (!q.isEmpty()) {
            int index = q.pollLast();
            left[index]=-1;
        }
        q.clear();
        //反向找也是最小
        for (int i = n-1; i >=0; i--) {
            while (!q.isEmpty() && arr[q.peekLast()] >= arr[i]) {
                int index = q.pollLast();
                right[index]=i;
            }
            q.offerLast(i);
        }
        while (!q.isEmpty()) {
            int index = q.pollLast();
            right[index]=-1;
        }
        System.out.println(Arrays.toString(left));
        System.out.println(Arrays.toString(right));

单调队列

**原理:**队列中的元素保持单调递增或递减的顺序。当新元素进入队列时,从队列尾部删除比新元素大(或小)的元素,然后将新元素加入队尾;同时,从队列头部删除超出窗口范围的元素。

**应用:**求解序列中每个长度为 k 的窗口的最值(最大值或最小值)。

        int[] V = {1, 3, -1, -3, 5, 3, 6, 7};
        int n = V.length;
        int m = 3; // 窗口大小
        Deque<Integer> Q = new LinkedList<>(); // 存储的是索引

        for (int i = 0; i < n; ++i) {
            // 如果队首元素的索引已滑出窗口,则弹出
            if (!Q.isEmpty() && i - Q.peekFirst() >= m) {
                Q.pollFirst();
            }
            // 保持队列单调递减:将所有比 V[i] 小的元素“退役”
            while (!Q.isEmpty() && V[Q.peekLast()] < V[i]) {
                Q.pollLast();
            }
            Q.offerLast(i); // 新元素入队

            // 如果已经形成一个完整窗口,就输出当前最大值
            if (i >= m - 1) {
                System.out.print(V[Q.peekFirst()] + " ");
            }
        }