21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序排列
解题思路:
- 定义一个初始化头结点,以及一个尾节点,尾节点初始指向头结点
- 当节点1小于节点2时,尾节点的next指向节点1,节点1往后移
- 当节点1大于等于节点2时,尾节点的next指向节点2,节点2往后移
- 更新尾节点为下一节点
- 当节点1或节点2为null时,表示其中一个链表移到结尾了,可将尾节点的next直接指向另一个节点,表示拼接剩余节点
- 返回头结点的下一个节点,即拼接后的链表头结点
代码如下:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode();
ListNode moveNode = head;
while (true) {
if (l1 == null) {
moveNode.next = l2;
break;
}
if (l2 == null) {
moveNode.next = l1;
break;
}
if (l1.val < l2.val) {
moveNode.next = l1;
l1 = l1.next;
} else {
moveNode.next = l2;
l2 = l2.next;
}
moveNode = moveNode.next;
}
return head.next;
}
相关数据结构:
public class ListNode {
public int val;
public ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
55. 跳跃游戏
给定一个非负整数数组nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 ,所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
解题思路:贪心算法。第n个元素能跳的范围是n~n+nums[n],只要这个范围内存在某个元素,跳跃的最远距离能超过这个范围,则表示第n元素可以借助那个元素跳到更远;如果不存在,并且最远跳跃还没跳到数组最后一个元素,则没必要再跳下去了,直接返回false,因为不存在有效借力点,也就跳不了更远
- 统计每个位置能跳到的最远位置,若比原来最远记录更大,则更新最远记录
- 若当前位置已经越过了最远位置,表示上一个位置上的元素为0,且之前所有元素都没能跳过元素0这个位置,即该数组无法跳到最后一个下标,直接返回false
代码如下:
public boolean canJump(int[] nums) {
int max = 0;
for (int i = 0; i < nums.length; i++) {
if (i > max) {
return false;
}
max = Math.max(max, nums[i] + i);
}
return true;
}
1487. 保证文件名唯一
给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹:在第 i 分钟,新建名为 names[i] 的文件夹。
由于两个文件 不能 共享相同的文件名,因此如果新建文件夹使用的文件名已经被占用,系统会以 (k) 的形式为新文件夹的文件名添加后缀,其中 k 是能保证文件名唯一的 最小正整数 。
返回长度为 n 的字符串数组,其中 ans[i] 是创建第 i 个文件夹时系统分配给该文件夹的实际名称。
示例 1:
输入:names = ["pes","fifa","gta","pes(2019)"]
输出:["pes","fifa","gta","pes(2019)"]
解释:文件系统将会这样创建文件名:
"pes" --> 之前未分配,仍为 "pes"
"fifa" --> 之前未分配,仍为 "fifa"
"gta" --> 之前未分配,仍为 "gta"
"pes(2019)" --> 之前未分配,仍为 "pes(2019)"
示例 2:
输入:names = ["gta","gta(1)","gta","avalon"]
输出:["gta","gta(1)","gta(2)","avalon"]
解释:文件系统将会这样创建文件名:
"gta" --> 之前未分配,仍为 "gta"
"gta(1)" --> 之前未分配,仍为 "gta(1)"
"gta" --> 文件名被占用,系统为该名称添加后缀 (k),由于 "gta(1)" 也被占用,所以 k = 2 。实际创建的文件名为 "gta(2)" 。
"avalon" --> 之前未分配,仍为 "avalon"
示例 3:
输入:names = ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece"]
输出:["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece(4)"]
解释:当创建最后一个文件夹时,最小的正有效 k 为 4 ,文件名变为 "onepiece(4)"。
示例 4:
输入:names = ["wano","wano","wano","wano"]
输出:["wano","wano(1)","wano(2)","wano(3)"]
解释:每次创建文件夹 "wano" 时,只需增加后缀中 k 的值即可。
示例 5:
输入:names = ["kaido","kaido(1)","kaido","kaido(1)"]
输出:["kaido","kaido(1)","kaido(2)","kaido(1)(1)"]
解释:注意,如果含后缀文件名被占用,那么系统也会按规则在名称后添加新的后缀 (k) 。
提示:
1 <= names.length <= 5 * 10^4
1 <= names[i].length <= 20
names[i] 由小写英文字母、数字和/或圆括号组成。
解题思路:
- 定义一个map集合,用来存储文件名及其出现次数
- 遍历所有文件名,每次通过map拿到文件名出现的次数
- 如果出现次数为0,则将文件名以及出现次数1存入map中
- 如果出现次数为大于0,则表示文件名出现重复,将文件名拼接上次数之后,再判断这个新文件名是否还存在,若还存在,则次数+1之后再次拼接并再次判断是否存在,这里用while循环来处理
- 直到新文件名不再重复后,将新文件名及其出现次数1存入map中,并且更新原文件名出现的次数(原次数+重复的次数)
- 每次重命名完就更新当前位置为新文件名
代码如下:
public String[] getFolderNames(String[] names) {
int length = names.length;
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < length; i++) {
String name = names[i];
int count = map.getOrDefault(name, 0);
String filename = name;
if (count > 0) {
while (map.containsKey(filename)) {
filename = name + "(" + (count++) + ")";
}
map.put(name, count);
map.put(filename, 1);
} else {
map.put(name, 1);
}
names[i] = filename;
}
return names;
}