LeetCode 796. Rotate String
题目描述给定两个字符串 s 和 goal,如果在若干次旋转操作之后,s 能变成 goal,那么返回 true。 s 的旋转操作就是将 s 最左边的字符移动到最右边。 例如: s = "abcde", goal = "cdeab" → 输出 true s = "abcde", goal = "abced" → 输出 false 解题思路本题有一个非常巧妙的技巧。 核心观察如果我们把 s 和自己拼接起来得到 s + s,那么 s 的所有旋转结果都一定是 s + s 的一个子串。 例如 s = "abcde": s + s = "abcdeabcde" 旋转 1 次:"bcdea" → 是 s + s 从下标 1 开始的子串 旋转 2 次:"cdeab" → 是 s + s 从下标 2 开始的子串 旋转 3 次:"deabc" → 是 s + s 从下标 3 开始的子串 旋转 4 次:"eab...
LeetCode 83. Remove Duplicates from Sorted List
题目描述给定一个已排序的链表的头 head,删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。 解题思路本题要求删除排序链表中的重复元素。由于链表已经排序,重复元素必然相邻,因此只需要遍历链表,比较当前节点和下一个节点的值是否相同。 核心思路: 首先处理特殊情况:如果链表为空(head == null),直接返回 null。 保存头节点指针 res,用于最终返回结果。 遍历链表,当 head.next 不为空时: 如果当前节点值等于下一个节点值(head.val == head.next.val),说明找到了重复元素,将当前节点的 next 指针跳过下一个节点,直接指向下下个节点(head.next = head.next.next)。注意这里使用 continue 而不是移动 head,因为可能需要连续删除多个重复元素(例如 1 → 1 → 1 → 2)。 如果值不相同,正常移动指针到下一个节点(head = head.next)。 这种做法的关键是:当删除一个重复节点后不立即移动指针,这是因为被跳过的那个节点后面的新节点可能仍然与当前节点值相同,需要继续判断...
LCR 061. 查找和最小的 K 对数字
题目描述给定两个以升序排列的整数数组 nums1 和 nums2,以及一个整数 k。定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk)。 例如:nums1 = [1,7,11],nums2 = [2,4,6],k = 3,输出 [[1,2],[1,4],[1,6]]。 解释:和最小的三对依次是 (1,2) 和=3,(1,4) 和=5,(1,6) 和=7。 解题思路暴力法枚举所有 m * n 个数对,排序后取前 k 个。时间复杂度 O(mn log(mn))。当数组较大时效率很低。 最小堆优化(O(k log k))核心思想:利用两个数组升序排列的性质,使用最小堆来”多路归并”。 关键观察对于固定 nums1[i],它与 nums2 中元素配对的和是递增的: 1234(i,0): nums1[i] + nums2[0] (最小)(i,1): nums1[i] + nums2[1](i,2): nums1[i] + nums2[2].....
LeetCode 1354. Construct Target Array With Multiple Sums
题目描述给你一个整数数组 target。一开始,你有一个数组 A,它的所有元素均为 1,你可以执行以下操作: 令 x 为你数组里所有元素的和。 选择满足 0 <= i < target.length 的任意下标 i,并让 A 数组中对应下标处的值为 x。 你可以重复该过程任意次。如果能从 A 开始构造出目标数组 target,返回 true,否则返回 false。 例如:target = [9,3,5] → true 初始 A = [1,1,1],sum = 3,选择下标 0 → A = [3,1,1],sum = 5 选择下标 2 → A = [3,1,5],sum = 9 选择下标 0 → A = [9,1,5],sum = 15 选择下标 1 → A = [9,3,5],等于 target 例如:target = [1,1,1,2] → false 解题思路该题的关键是逆向思维:与其从全 1 数组构造 target,不如从 target 反向推导回全 1 数组。 正...
LeetCode 1046. Last Stone Weight
题目描述有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设两块石头的重量分别为 x 和 y,且 x <= y: 如果 x == y,那么两块石头都会被完全粉碎; 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y - x。 最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。 例如:stones = [2,7,4,1,8,1] 第一轮:选出 8 和 7,剩下 8-7=1,数组变为 [2,4,1,1,1] 第二轮:选出 4 和 2,剩下 4-2=2,数组变为 [2,1,1,1] 第三轮:选出 2 和 1,剩下 2-1=1,数组变为 [1,1,1] 第四轮:选出 1 和 1,完全粉碎,数组变为 [1] 返回 1 解题思路这道题完美适合使用最大堆(优先队列)来求解。因为每一轮都需要快速获取最大的两个元素,并在可能的情况下将差值插回。 大顶堆解法 将所有石头重量插入最大堆中。 当堆中元素数量 > 1 时,循环执行: 取出最大的元素...
LeetCode 1700. Number of Students Unable to Eat Lunch
题目描述学校的自助午餐提供圆形和方形的三明治,分别用 0 和 1 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个栈里,每一轮: 如果队列最前面的学生喜欢栈顶的三明治,会拿走它并离开队列; 否则学生会放弃这个三明治并回到队列的尾部。这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。返回无法吃上午餐的学生数量。 例如:students = [1,1,0,0],sandwiches = [0,1,0,1],输出 0。 解释:所有学生最终都能拿到喜欢的三明治。 解题思路核心思想这是一个模拟问题,使用队列来模拟学生的排队行为。 步骤 将所有学生放入队列 queue 中。 遍历三明治栈(sandwiches),对于每个三明治: 记录已检查的学生数 r = 0。 不断检查队首学生是否喜欢当前三明治: 如果喜欢(queue.peek() == sandwich),该学生拿走三明治并出队,跳出内层循环。 如果不喜欢,该学生回到队尾(queue.offer(queue.poll())),r++。 关键:如果 ...
LeetCode 2073. Time Needed to Buy Tickets
题目描述有 n 个人前来排队买票,其中第 0 人站在队伍最前方,第 n-1 人站在队伍最后方。给定一个整数数组 tickets,其中 tickets[i] 表示第 i 个人想要购买的票数。每个人买票需要 1 秒。一个人一次只能买一张票,买完后如果还需要买更多票,就必须走到队尾重新排队。返回位于位置 k 的人完成买票所需的时间。 例如:tickets = [2,3,2],k = 2,输出 6。 解释:排队的模拟过程为: 第 1 秒:第 0 人买一张,剩余 [1,3,2],到队尾。 第 2 秒:第 1 人买一张,剩余 [1,2,2],到队尾。 第 3 秒:第 2 人(k=2)买一张,剩余 [1,2,1],到队尾。 第 4 秒:第 0 人买一张,剩余 [0,2,1],离队。 第 5 秒:第 1 人买一张,剩余 [0,1,1],到队尾。 第 6 秒:第 2 人买最后一张,剩余 [0,1,0],完成。所需时间为 6 秒。 解题思路模拟法(代码所用方法)直接按照题目描述的规则进行模拟。 步骤 初始化计数器 ans = 0,使用无限循环和取模运算模拟环形队列。 在循环中 inde...
LeetCode 232. Implement Queue using Stacks
问题描述请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)。使用自定义的 MyQueue 类(位于 datastructure 包中),内部用两个栈实现队列。 解题思路栈是后进先出(LIFO)的数据结构,队列是先进先出(FIFO)的数据结构。要用栈模拟队列,关键在于两次入栈等于一次顺序反转。 双栈设计使用两个栈: 输入栈(stackIn):专门负责接收 push 操作 输出栈(stackOut):专门负责 pop 和 peek 操作 核心操作原理 push(x):直接将元素压入 stackIn。时间复杂度 O(1)。 pop() / peek(): 如果 stackOut 不为空,直接从 stackOut 弹出/查看栈顶元素 如果 stackOut 为空,则将 stackIn 中的所有元素依次弹出并压入 stackOut。经过这次转移,stackIn 中的元素顺序被反转,stackOut 的栈顶恰好就是整个队列的队首元素 empty():当两个栈都为空时,队列为空。 为什么这样做是正确...
LeetCode 482. License Key Formatting
问题描述给定一个许可密钥字符串 s,仅由字母、数字字符和破折号组成。字符串由 n 个破折号分成 n+1 组。给定一个整数 k,重新格式化字符串,使每一组恰好包含 k 个字符,第一组可以少于 k 个字符但至少包含一个字符。用破折号分隔组,所有小写字母转换为大写。 解题思路本题的要点在于理解分组规则:除了第一组外,其余每组恰好 k 个字符。这意味着我们需要先计算出总的有效字符数,然后确定第一组的大小。 算法步骤 去除破折号: 使用 s.split("-") 将原字符串按破折号分割 将所有非破折号部分拼接起来,得到所有有效字符的连续字符串 计算第一组长度: 设总有效字符数为 sumLength 第一组的长度 firstLen = sumLength % k 如果 firstLen == 0(恰好整除),则第一组也应取 k 个字符 按组格式化: 取前 firstLen 个字符作为第一组,转为大写 从 firstLen 位置开始,每次取 k 个字符作为一组,组间用 "-" 分隔,每组转为大写 举例说明示例 1:s = "...
LeetCode 520. Detect Capital
问题描述我们定义,在以下情况时,单词的大写用法是正确的: 全部字母都是大写,比如 "USA" 单词中所有字母都不是大写,比如 "leetcode" 如果单词不只含有一个字母,只有首字母大写,比如 "Google" 给定一个字符串 word,判断其大写使用是否正确。 解题思路题目的三种合法情况可以归纳为三个独立的判断条件,满足其一即可: 辅助方法设计 isAllUppercase(str):检查字符串中所有字符是否都是大写 遍历每个字符,如果发现小写字母则返回 false isAllLowercase(str):检查字符串中所有字符是否都是小写 遍历每个字符,如果发现大写字母则返回 false isFirstUppercase(str):检查是否首字母大写且其余字母小写 先检查首字母是否大写 如果是,从第二个字符开始遍历,发现大写字母则返回 false 如果首字母不是大写,直接返回 false 主逻辑1return isAllLowercase(word) || isFirstUppercase(w...