当前位置: 首页 > news >正文

2025高频面试算法总结篇【链表堆栈队列】

文章目录


直接刷题链接直达

  1. 反转链表
  • 206. 反转链表
  1. 环形链表
  • 141. 环形链表
  • 142. 环形链表 II
  1. 判断一个序列是否为合理的出栈顺序
  • 946. 验证栈序列
  1. 最长有效括号
  • 32. 最长有效括号
  1. 旋转链表
  • 61. 旋转链表
  1. 删除链表 M 个节点之后的 N 个节点
  • 删除链表 M 个节点之后的 N 个节点
  1. 复杂链表的复制
  • 138. 复制带随机指针的链表
  1. 约瑟夫环问题
  • 面试题62. 圆圈中最后剩下的数字
  1. 滑动窗口最大值
  • 239. 滑动窗口最大值

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode pre = null;
        ListNode curr = head;
        ListNode next = null;
        while (curr != null) {
            next = curr.next;
            curr.next = pre;
            pre = curr;
            curr = next;
        }

        return pre;
    }
}

环形链表

解法:
(1)采用快慢指针去判断链表是否有环,有环快慢指针一定会相遇。
(2)有环,找到这个环入点,先快慢指针,判断环,然后快指针赋值head,快慢指针,保持相同的速度1,遍历,直到相等,这个节点就是入点。

题目: 给你一个链表的头节点 head ,判断链表中是否有环。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }
}

题目:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        // 判断有没有环
        if (head == null || head.next == null) return null;
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                break;
            }
        }
        // 有环
        if (fast != null && fast.next != null) {
            fast = head;
            while (fast != slow) {
                fast = fast.next;
                slow = slow.next;
            }

            return fast;
        }

        // 没环
        return null;

    }
}

判断一个序列是否为合理的出栈顺序

题目:给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack = new Stack<>();
        int k = 0;
        for (int num: pushed) {
            stack.push(num);
            while (!stack.isEmpty() && stack.peek() == popped[k]) {
                stack.pop();
                k++;
            }
        }

        return stack.isEmpty();
    }
}

最长有效括号

题目: 给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

思路:

  • 用栈存储括号索引,帮助找到匹配的子串长度。
  • 遍历字符串:
    • 遇到 ( 入栈。
    • 遇到 ) 出栈,计算最长有效长度。
  • 额外维护 -1 作为基准索引,避免匹配失败时无法计算长度。
class Solution {
    public int longestValidParentheses(String s) {
        // 栈初始化,存储索引
        Stack<Integer> stack = new Stack<>();
        stack.push(-1); // 这个 -1 是为了处理从第一个字符开始的有效括号情况

        int maxLen = 0; // 用于记录最长有效括号的长度
        
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(i);
            }else {
                stack.pop();
                if (stack.isEmpty()) {
                    stack.push(i);
                }else {
                    maxLen = Math.max(maxLen, i - stack.peek());
                }
            }
        }
        return maxLen;
    }
}

旋转链表

题目: 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || head.next == null || k == 0) return head;

        // 计算链表长度
        int n = 0;
        ListNode curr = head;
        while (curr != null) {
            curr = curr.next;
            n++;
        }

        k = n - k % n;
        if (k == n) {
            return head;
        }

        ListNode pre = head;
        for (int i = 1; i < k; i++) {
            pre = pre.next;
        }
        ListNode newHead = pre.next;
        pre.next = null;
        curr = newHead;
        while (curr.next != null) {
            curr = curr.next;
        }
        curr.next = head;
        return newHead;
    }
}


题目描述: 给定一个单链表 head 和两个整数 M 和 N,你需要按照如下规则修改链表:

  • 保留 链表的前 M 个节点。
  • 删除 接下来的 N 个节点。
  • 重复 上述过程,直到遍历完整个链表。
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public class Solution {
    public ListNode deleteNodes(ListNode head, int M, int N) {
        ListNode curr = head;  // 当前遍历的节点
        
        while (curr != null) {
            // 1. 保留 M 个节点
            for (int i = 1; i < M && curr != null; i++) {
                curr = curr.next;
            }
            
            if (curr == null) break;  // 已到链表末尾
            
            // 2. 删除 N 个节点
            ListNode temp = curr.next;  // temp 用于删除后续的 N 个节点
            for (int i = 0; i < N && temp != null; i++) {
                temp = temp.next;
            }
            
            // 3. 连接 M 个节点之后的剩余部分
            curr.next = temp;
            curr = temp; // 继续遍历
        }
        
        return head;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();

        // 构造链表 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
        ListNode head = new ListNode(1);
        ListNode curr = head;
        for (int i = 2; i <= 10; i++) {
            curr.next = new ListNode(i);
            curr = curr.next;
        }

        int M = 2, N = 3;  // 示例:保留2个,删除3个
        ListNode newHead = solution.deleteNodes(head, M, N);

        // 打印结果
        while (newHead != null) {
            System.out.print(newHead.val + " -> ");
            newHead = newHead.next;
        }
        System.out.println("NULL");
    }
}


复杂链表的复制

题目: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        Map<Node, Node> map = new HashMap<>();
        Node cur = head;
        while (cur != null) {
            map.put(cur, new Node(cur.val));
            cur = cur.next;
        }
        
        cur = head;
        while (cur != null) {
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }

        return map.get(head);
    }

}

约瑟夫环问题

题目: 社团共有 num 位成员参与破冰游戏,编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target,从 0 号成员起开始计数,排在第 target 位的成员离开圆桌,且成员离开后从下一个成员开始计数。请返回游戏结束时最后一位成员的编号。

class Solution {
    public int iceBreakingGame(int num, int target) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(i);
        }
        int idx = 0;
        while (num > 1) {
            idx = (idx + target - 1) % num;
            list.remove(idx);
            num--;
        }
        return list.get(0);

    }
}

滑动窗口最大值

题目: 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

import java.util.Deque;
import java.util.LinkedList;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // 单调队列(存储索引,保持递减)
        Deque<Integer> dq = new LinkedList<>();
        int[] ans = new int[nums.length - k + 1];

        for (int i = 0; i < nums.length; i++) {
            // 1. 维护单调递减队列
            while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) {
                dq.pollLast();
            }

            // 2. 移除超出窗口的元素(队列头部)
            while (!dq.isEmpty() && dq.peekFirst() <= i - k) {
                dq.pollFirst();
            }

            // 3. 添加当前元素索引
            dq.addLast(i);

            // 4. 只有当窗口形成(i >= k - 1)时,才开始记录答案
            if (i >= k - 1) {
                ans[i - k + 1] = nums[dq.peekFirst()];
            }
        }

        return ans;
    }

}


相关文章:

  • priority_queue的模拟实现
  • 如何使用logminer
  • 剑指小米特斯拉:秦L EV上市11.98万起
  • BP神经网络+NSGAII算法(保真)
  • 【F#入门】第四讲 函数,管道与组合
  • dnf命令详解
  • hackmyvm-lookup
  • Linux中查找标准库函数的定义
  • MongoDB未授权访问漏洞
  • 个人博客系统 --- 测试报告
  • 【js逆向】某酒店模拟登录
  • 【蓝桥杯】真题 2386染色时间 (优先队列BFS)
  • 智慧教育云平台支持下的学生自主学习能力培养策略研究
  • dfs刷题矩阵搜索问题
  • 深入理解Java虚拟机(学习笔记)
  • 开源视频剪辑工具,无损编辑更高效
  • 车道保持中车道线识别
  • XSS 攻击向量与绕过技巧
  • OpenCV的基本用法全解析
  • 【深度学习入门_机器学习理论】梯度提升决策树(GBDT)
  • 首开股份:一季度净利润亏损约10.79亿元,签约金额63.9亿元
  • 浪尖计划再出发:万亿之城2030课题组赴九城调研万亿产业
  • 解密62个“千亿县”:强者恒强,新兴产业助新晋县崛起
  • 民生银行一季度净利127.42亿降逾5%,营收增7.41%
  • 习近平访问金砖国家新开发银行
  • 中方发布《不跪!》视频传递何种信息?外交部回应