#leetcode# 1

#leetcode#

1431.拥有最多糖果的孩子

class Solution {

/**

* 题目:拥有最多糖果的孩子

* 核心思路:

* 1. 先找到数组中糖果的最大值(即当前拥有最多糖果的数量)

* 2. 遍历每个孩子的糖果数,判断「当前糖果数 + 额外糖果数」是否 ≥ 最大值

* 3. 用布尔列表记录每个孩子的判断结果

* 时间复杂度:O(n),n为孩子数量,仅需两次线性遍历(找最大值+判断结果)

* 空间复杂度:O(n),用于存储结果列表(不计入输出的话为O(1))

*

* @param candies 每个孩子当前拥有的糖果数数组

* @param extraCandies 额外可分配的糖果数(所有孩子共享这部分额外糖果)

* @return 布尔列表:第i个元素为true表示第i个孩子获得额外糖果后拥有最多糖果

*/

public List<Boolean> kidsWithCandies(int[] candies, int extraCandies) {

// 记录孩子的总数(数组长度)

int n = candies.length;

// 初始化当前最大糖果数为0,用于后续遍历求解最大值

int maxCandies = 0;

// 第一遍遍历:找到数组中最大的糖果数(当前拥有最多糖果的数量)

for (int i = 0; i < n; ++i) {

// 每次比较当前孩子的糖果数与已记录的最大值,更新最大值

maxCandies = Math.max(maxCandies, candies[i]);

}

// 初始化结果列表:ArrayList是List接口的高效实现,适合动态添加元素

List<Boolean> ret = new ArrayList<Boolean>();

// 第二遍遍历:判断每个孩子获得额外糖果后是否能拥有最多糖果

for (int i = 0; i < n; ++i) {

// 核心判断:当前糖果数 + 额外糖果数 ≥ 最大值 → 该孩子能拥有最多糖果

// 直接将布尔判断结果添加到列表中

ret.add(candies[i] + extraCandies >= maxCandies);

}

// 返回最终的判断结果列表

return ret;

}

}

605.种花问题

class Solution {

/**

* 题目:种花问题

* 核心规则:花不能种植在相邻地块(1和1不能相邻),花坛初始状态已满足该规则

* 核心思路(贪心):

* 1. 遍历花坛,记录每个已种花地块(1)的位置,计算相邻花之间的空地块(0)能种多少花

* 2. 处理花坛两端的空地块(开头无花、结尾无花的情况)

* 3. 总可种花数量 ≥ n 则返回true,否则返回false

* 时间复杂度:O(m),m为花坛长度,仅需一次线性遍历

* 空间复杂度:O(1),仅用常数额外空间

*

* @param flowerbed 花坛数组(0=空地块,1=已种花)

* @param n 想要种植的花的数量

* @return 是否能在不打破规则的情况下种n朵花

*/

public boolean canPlaceFlowers(int[] flowerbed, int n) {

int count = 0; // 记录最多能种植的花的数量

int m = flowerbed.length; // 花坛的总长度

int prev = -1; // 记录上一个已种花地块的索引(初始为-1,表示未遇到任何花)

// 遍历整个花坛,计算相邻花之间的可种花数量

for (int i = 0; i < m; i++) {

// 遇到已种花的地块(1),计算当前花与上一个花之间的空地块能种花的数量

if (flowerbed[i] == 1) {

// 情况1:prev=-1(当前是第一个遇到的花)

// 此时空地块是从花坛开头(索引0)到当前花的前一个位置(i-1)

// 可种花数量 = 空地块长度 / 2(例如:0、0、0 可种1朵;0、0 可种1朵;0 可种0朵)

if (prev < 0) {

count += i / 2;

} else {

// 情况2:prev≠-1(之前遇到过花)

// 空地块长度 = 当前花索引 - 上一个花索引 - 1(中间的空地块)

// 可种花数量 = (空地块长度 - 1) / 2(需减去1避免与两边花相邻,例如:0、0、0 可种1朵;0、0、0、0 可种1朵)

count += (i - prev - 2) / 2;

}

// 更新上一个花的索引为当前花的位置

prev = i;

}

}

// 遍历结束后,处理花坛末尾的空地块(最后一个花之后到花坛结束的区域)

if (prev < 0) {

// 情况1:花坛中没有任何花(prev始终为-1)

// 可种花数量 = (花坛长度 + 1) / 2(例如:长度3可种2朵;长度2可种1朵;长度1可种1朵)

count += (m + 1) / 2;

} else {

// 情况2:花坛中有花,处理最后一个花之后的空地块

// 空地块长度 = 花坛长度 - 最后一个花的索引 - 1

// 可种花数量 = 空地块长度 / 2(例如:0、0 可种1朵;0、0、0 可种1朵;0 可种0朵)

count += (m - prev - 1) / 2;

}

// 若最多可种花数量 ≥ 想要种的n朵,返回true;否则返回false

return count >= n;

}

}

345.反转字符串中的元音字母

aeiou

class Solution {

/**

* 题目:反转字符串中的元音字母

* 核心思路:用双指针从字符串两端向中间遍历,

* 左指针找左侧元音,右指针找右侧元音,找到后交换两者,

* 直到指针相遇(完成所有元音反转)

* 时间复杂度:O(n),n为字符串长度,每个字符仅遍历一次(双指针总移动次数为n)

* 空间复杂度:O(n),将字符串转为字符数组(String不可变,需数组辅助修改)

*

* @param s 输入字符串(包含大小写字母及其他可打印ASCII字符)

* @return 反转元音后的字符串

*/

public String reverseVowels(String s) {

// 字符串长度(避免重复调用length()方法)

int n = s.length();

// 将字符串转为字符数组:String是不可变对象,数组可直接修改字符(核心操作载体)

char[] arr = s.toCharArray();

// 双指针初始化:i从左(0索引)开始,j从右(n-1索引)开始

int i = 0, j = n - 1;

// 双指针遍历终止条件:左指针 >= 右指针(所有元音已处理)

while (i < j) {

// 左指针向右移动:跳过非元音字符,直到找到元音或指针越界

// 条件1:i < n(防止指针超出数组左边界)

// 条件2:!isVowel(arr[i])(当前字符不是元音)

while (i < n && !isVowel(arr[i])) {

++i; // 左指针右移

}

// 右指针向左移动:跳过非元音字符,直到找到元音或指针越界

// 条件1:j > 0(防止指针超出数组右边界)

// 条件2:!isVowel(arr[j])(当前字符不是元音)

while (j > 0 && !isVowel(arr[j])) {

--j; // 右指针左移

}

// 若此时左指针仍在右指针左侧:说明找到一对元音,执行交换

if (i < j) {

swap(arr, i, j); // 交换左右指针指向的元音字符

++i; // 左指针右移,继续找下一个元音

--j; // 右指针左移,继续找下一个元音

}

}

// 将修改后的字符数组转回字符串,返回结果

return new String(arr);

}

/**

* 辅助函数:判断字符是否为元音字母(含大小写)

* 元音字母定义:a、e、i、o、u、A、E、I、O、U

*

* @param ch 待判断的字符

* @return true表示是元音,false表示非元音

*/

public boolean isVowel(char ch) {

// 利用字符串indexOf()方法:存在则返回索引(>=0),不存在返回-1

// "aeiouAEIOU"是所有元音的集合,直接判断字符是否在该字符串中

return "aeiouAEIOU".indexOf(ch) >= 0;

}

/**

* 辅助函数:交换字符数组中两个索引位置的字符

*

* @param arr 字符数组

* @param i 第一个字符的索引

* @param j 第二个字符的索引

*/

public void swap(char[] arr, int i, int j) {

char temp = arr[i]; // 临时变量存储arr[i]的值

arr[i] = arr[j]; // 将arr[j]的值赋给arr[i]

arr[j] = temp; // 将临时存储的arr[i]值赋给arr[j]

}

}

151.反转字符串中的单词

import java.util.Arrays;

import java.util.List;

import java.util.Collections;

class Solution {

/**

* 题目:反转字符串中的单词

* 核心要求:

* 1. 反转单词顺序(而非反转单词内部字符)

* 2. 去除前导、尾随空格

* 3. 单词间仅保留单个空格(合并多余空格)

* 核心思路:利用Java字符串API简化操作,三步完成:

* 1. 去除首尾空格 → 2. 按任意长度空格分割单词 → 3. 反转单词列表 → 4. 用单个空格拼接单词

* 时间复杂度:O(n),n为字符串长度(trim、split、reverse、join均为线性时间操作)

* 空间复杂度:O(n),用于存储分割后的单词列表(进阶可优化为O(1)原地解法,但该解法简洁高效)

*

* @param s 输入字符串(可能含前导/尾随空格、单词间多空格)

* @return 单词顺序反转、格式规范的结果字符串

*/

public String reverseWords(String s) {

// 第一步:去除字符串开头和末尾的所有空白字符(解决前导/尾随空格问题)

// trim() 作用:删除字符串前后的Unicode空白字符(包括空格、制表符等,本题仅需处理空格)

s = s.trim();

// 第二步:按「连续空白字符」分割字符串,得到单词列表(解决单词间多空格问题)

// 正则表达式 \\s+:匹配一个或多个空白字符(空格、制表符等,本题对应多个空格)

// Arrays.asList():将分割后的字符串数组转为List,方便后续反转

List<String> wordList = Arrays.asList(s.split("\\s+"));

// 第三步:反转单词列表的顺序(核心操作,实现单词顺序颠倒)

// Collections.reverse():原地反转List(修改原List顺序,无需额外空间)

Collections.reverse(wordList);

// 第四步:将反转后的单词列表用「单个空格」拼接为字符串(保证单词间仅一个空格)

// String.join(separator, list):将集合元素按指定分隔符拼接为字符串

return String.join(" ", wordList);

}

}

334.递增的三元子序列

class Solution {

/**

* 题目:递增的三元子序列

* 核心要求:判断数组中是否存在 i<j<k 且 nums[i]<nums[j]<nums[k] 的三元组

* 核心思路(预处理+中心判断):

* 1. 预处理左最小数组:leftMin[i] 表示 nums[0..i] 中的最小值(即 j 左侧的最小元素)

* 2. 预处理右最大数组:rightMax[i] 表示 nums[i..n-1] 中的最大值(即 j 右侧的最大元素)

* 3. 遍历每个中间位置 j(1<=j<=n-2),判断是否满足 leftMin[j-1] < nums[j] < rightMax[j+1]

* 满足则存在递增三元子序列(i=leftMin[j-1]的对应索引,k=rightMax[j+1]的对应索引)

* 时间复杂度:O(n),三次线性遍历(左最小+右最大+中心判断)

* 空间复杂度:O(n),存储两个预处理数组(进阶可优化为 O(1) 贪心解法)

*

* @param nums 输入整数数组(可能含正负值,长度最大 5e5)

* @return 是否存在递增三元子序列

*/

public boolean increasingTriplet(int[] nums) {

int n = nums.length;

// 边界条件:数组长度小于3,不可能存在三元子序列,直接返回false

if (n < 3) {

return false;

}

// 左最小数组:leftMin[i] 存储 nums[0] 到 nums[i] 中的最小值

// 作用:快速查询任意位置 j 左侧(0..j-1)的最小元素,判断是否存在 nums[i] < nums[j]

int[] leftMin = new int[n];

leftMin[0] = nums[0]; // 第一个元素的左侧最小就是自身(无左侧元素)

// 从左到右遍历,递推计算左最小数组

for (int i = 1; i < n; i++) {

// 第i位的左最小 = 前一位的左最小 和 当前元素的较小值

leftMin[i] = Math.min(leftMin[i - 1], nums[i]);

}

// 右最大数组:rightMax[i] 存储 nums[i] 到 nums[n-1] 中的最大值

// 作用:快速查询任意位置 j 右侧(j+1..n-1)的最大元素,判断是否存在 nums[k] > nums[j]

int[] rightMax = new int[n];

rightMax[n - 1] = nums[n - 1]; // 最后一个元素的右侧最大就是自身(无右侧元素)

// 从右到左遍历,递推计算右最大数组

for (int i = n - 2; i >= 0; i--) {

// 第i位的右最大 = 后一位的右最大 和 当前元素的较大值

rightMax[i] = Math.max(rightMax[i + 1], nums[i]);

}

// 遍历每个可能的中间位置 j(j必须有左侧和右侧元素,即 1<=j<=n-2)

for (int i = 1; i < n - 1; i++) {

// 核心判断:j左侧的最小元素 < nums[j] < j右侧的最大元素

// 若满足,则存在 i<j<k 使得 nums[i]=leftMin[j-1] < nums[j] < rightMax[j+1]=nums[k]

if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {

return true; // 找到符合条件的三元组,直接返回true

}

}

// 遍历完所有中间位置都不满足,返回false

return false;

}

}

相关推荐
sheeta19981 小时前
LeetCode 每日一题笔记 日期:2025.11.30 题目:1590.使数组和能被 P 整除
笔记·算法·leetcode
le serein —f2 小时前
用go实现-回文链表
算法·leetcode·golang
胖咕噜的稞达鸭2 小时前
算法入门:专题二分查找算法 模板总结 题目练手 :排序数组中查找元素的第一个和最后一个位置 第一个错误的版本 查找x的平方根 搜索插入位置 山脉数组的封顶索引
c语言·c++·算法·leetcode
im_AMBER4 小时前
Leetcode 65 固定长度窗口 | 中心辐射型固定窗口
笔记·学习·算法·leetcode
资深web全栈开发5 小时前
[特殊字符] LeetCode 2141:如何让 N 台电脑续航最久?——“二分答案“套路一文讲透
算法·leetcode
刃神太酷啦5 小时前
C++的IO流和C++的类型转换----《Hello C++ Wrold!》(29)--(C/C++)
java·c语言·开发语言·c++·qt·算法·leetcode
Teroin5 小时前
LeetCode55 跳跃游戏
数据结构·算法·leetcode
CoderYanger5 小时前
递归、搜索与回溯-记忆化搜索:40.矩阵中的最长递增路径
java·线性代数·算法·leetcode·矩阵·1024程序员节