文章目录
2024.5.22(5题)
1108. IP 地址无效化
在显示或处理IP地址时避免误解析,例如在某些HTML或JSON内容中防止被错误地识别为实际的IP地址。
在数据处理中将某些字符替换为特定格式以满足特定需求。
给你一个有效的 IPv4 地址 address
,返回这个 IP 地址的无效化版本。
所谓无效化 IP 地址,其实就是用 "[.]"
代替了每个 "."
。
示例 1:
输入:address = "1.1.1.1"
输出:"1[.]1[.]1[.]1"
示例 2:
输入:address = "255.100.50.0"
输出:"255[.]100[.]50[.]0"
提示:
- 给出的
address
是一个有效的 IPv4 地址
题解一
class Solution {
public String defangIPaddr(String address) {
return address.replace(".", "[.]");
}
}
replace()
是String
类中的一个方法,用于将字符串中的某个子字符串替换为另一个子字符串。address.replace(".", "[.]");
的作用是将字符串address
中的所有点(.
)替换为[.]
。
题解二
class Solution {
public String defangIPaddr(String address) {
StringBuilder sb = new StringBuilder();
int n = address.length();
int index = 0;
while (index < n) {
char c = address.charAt(index);
if (c == '.') {
sb.append('[');
}
sb.append(c);
if (c == '.') {
sb.append(']');
}
index++;
}
return sb.toString();
}
}
-
StringBuilder sb = new StringBuilder();
:创建一个StringBuilder
对象sb
。StringBuilder
是一个可变的字符串序列,适合用于在循环中进行字符串拼接,因为它比使用String
对象的拼接效率更高。 -
char c = address.charAt(index);
:获取字符串address
在索引index
处的字符,并将其存储在变量c
中。charAt
方法是 Java 中String
类的一部分,它用于返回指定索引处的字符。 -
sb.append('[');
:检查字符c
是否是点(.
)。如果是,则向StringBuilder
对象sb
中追加字符'['
。append
方法是 Java 中StringBuilder
类的一部分,它用于将指定的数据追加到StringBuilder
对象的末尾。- 注意这个拼接顺序,当前字符
c
是点(.
)的时候,还没把字符c
拼上,所以先拼接[
,然后拼接字符c
,然后拼接]
。
- 注意这个拼接顺序,当前字符
-
return sb.toString();
:当循环结束时,将StringBuilder
对象sb
转换为字符串并返回。
1480. 一维数组的动态和
这个题是对之后学前缀和算法至关重要,是一维前缀和的求法;
同时这里给出的题解一实际上是一个极简的动态规划算法。
给你一个数组 nums
。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i])
。
请返回 nums
的动态和。
示例 1:
输入:nums = [1,2,3,4]
输出:[1,3,6,10]
解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
示例 2:
输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。
示例 3:
输入:nums = [3,1,2,10,1]
输出:[3,4,6,16,17]
提示:
1 <= nums.length <= 1000
-10^6 <= nums[i] <= 10^6
题解一
class Solution {
public int[] runningSum(int[] nums) {
int[] arr = new int[nums.length];
arr[0] = nums[0];
for (int i = 1; i < arr.length; i++) {
arr[i] = nums[i] + arr[i-1];
}
return arr;
}
}
- 创建一个新的数组
arr
来存储动态和的结果,长度与nums
相同。 - 设置
arr[0]
为nums[0]
。因为第一个就是本身,不需要跟谁相加。 - 从索引
1
开始遍历数组nums
,每次将当前元素与前一个元素的动态和相加,得到当前元素的动态和。
题解二
class Solution {
public int[] runningSum(int[] nums) {
int n = nums.length;
for (int i = 1; i < n; i++) {
nums[i] += nums[i - 1];
}
return nums;
}
}
- 每次将当前元素与前一个元素的和赋值给当前元素,从而在原数组上直接进行累加。
1394. 找出数组中的幸运数
计数排序思想:使用一个数组来统计频次,这种方法类似于计数排序中对元素进行计数的过程。
哈希表统计频次;官方给的题解,不过我觉得这个题完全没必要有哈希的额外时空开销。
在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。
给你一个整数数组 arr
,请你从中找出并返回一个幸运数。
- 如果数组中存在多个幸运数,只需返回 最大 的那个。
- 如果数组中不含幸运数,则返回 -1 。
示例 1:
输入:arr = [2,2,3,4]
输出:2
解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。
示例 2:
输入:arr = [1,2,2,3,3,3]
输出:3
解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。
示例 3:
输入:arr = [2,2,2,3,3]
输出:-1
解释:数组中不存在幸运数。
示例 4:
输入:arr = [5]
输出:-1
示例 5:
输入:arr = [7,7,7,7,7,7,7]
输出:7
提示:
1 <= arr.length <= 500
1 <= arr[i] <= 500
题解一
class Solution {
public int findLucky(int[] arr) {
int[] counts = new int[501];
int ans = -1;
for (int i = 0; i < arr.length; i++) {
counts[arr[i]]++;
}
for (int i = 1; i < counts.length; i++) {
if (counts[i] == i) {
ans = Math.max(i, ans);
}
}
return ans;
}
}
- 创建一个长度为
501
的数组counts
,用于记录每个整数的出现频次。题目给了数组范围是 [1,500],这个数组的下标就是要统计的数字,下标对应的值就是这个数出现的次数。所以要开辟501的长度。 - 如果不含辛运数,返回 -1,ans 初始化为-1。
- 第一个循环是把arr数组中的每个元素赋值给counts数组,中括号内的就是数组的下标,前面说了,counts数组的下标就是要统计的数字
- 第二个循环就是判断幸运数,下标和对应值相等时就是幸运数,然后通过max输出最大的幸运数。
Math.max(i, ans)
的意思是取i
和ans
中的较大值。这个方法会返回两个数中较大的那个数。整个语句就是当遇到更大的幸运数时,就会更新ans
题解二
class Solution {
public int findLucky(int[] arr) {
Map<Integer, Integer> m = new HashMap<Integer, Integer>();
for (int x : arr) {
m.put(x, m.getOrDefault(x, 0) + 1);
}
int ans = -1;
for (Map.Entry<Integer, Integer> entry : m.entrySet()) {
int key = entry.getKey(), value = entry.getValue();
if (key == value) {
ans = Math.max(ans, key);
}
}
return ans;
}
}
- 代码是官方的题解,本质跟题解一的计数法一样,只不过开了一个哈希表来存储次数
HashMap
是 Java 中的一个数据结构,它实现了Map
接口,可以用来存储键值对。在HashMap
中,每个键值对都是通过键和值进行关联的。以下是HashMap
的一些重要概念:
键值对(Entry):
HashMap
中的每个元素都是一个键值对,包含一个键对象和一个值对象。键对象用于查找值对象。哈希表(Hash Table):
HashMap
的内部实现是基于哈希表的。哈希表是一种数据结构,可以提供快速的插入、删除和查找操作。哈希函数(Hash Function):哈希函数将键映射到哈希表中的一个位置。这个位置称为哈希桶(Hash Bucket)或槽(Slot)。
冲突(Collision):由于哈希函数的映射不是一一对应的,可能会出现不同的键被映射到相同的位置,这种情况称为哈希冲突。
HashMap
使用链表或红黑树来解决冲突。负载因子(Load Factor):负载因子是指哈希表中元素的数量与哈希桶数量的比率。当负载因子超过某个阈值时,
HashMap
会自动调整哈希桶的数量,以保持操作的效率。扩容(Rehashing):当
HashMap
中的元素数量达到一定阈值时,HashMap
会自动扩容,即重新创建一个更大的哈希表,并将所有元素重新插入到新表中。
HashMap
的主要优点是快速的查找、插入和删除操作(平均时间复杂度为 O(1)),缺点是它不是线程安全的,需要在多线程环境中使用时进行额外的同步处理。
-
Map<Integer, Integer> m = new HashMap<Integer, Integer>();
:这行代码创建了一个 HashMap 对象m
,用于存储整数键和整数值之间的映射关系。在这个 HashMap 中,键是数组中的元素,值是该元素在数组中出现的次数。 -
for (int x : arr) { m.put(x, m.getOrDefault(x, 0) + 1); }
:这个循环遍历数组arr
中的每个元素x
,并将其作为键放入 HashMapm
中。如果该元素在 HashMap 中已经存在,则将其值加 1;否则,将其值设为 1。getOrDefault(key, defaultValue)
是HashMap
类中的一个方法,用于获取指定键的值,如果该键不存在,则返回指定的默认值。具体来说,如果HashMap
中包含指定的键,则返回与该键关联的值;否则,返回指定的默认值。 -
for (Map.Entry<Integer, Integer> entry : m.entrySet()) { ... }
:这个循环遍历 HashMapm
中的每个条目,其中每个条目都是一个键值对。在循环的每次迭代中,entry
是一个表示 HashMap 中的一个键值对的对象。entrySet()
是HashMap
类中的一个方法,用于返回包含HashMap
中所有键值对的集合。这个方法返回的集合中的每个元素都是一个Map.Entry
对象,表示HashMap
中的一个键值对。 -
int key = entry.getKey(), value = entry.getValue();
:这两行代码从当前条目中提取键和值。在这个上下文中,键key
是数组中的一个元素,值value
是该元素在数组中出现的次数。 -
if (key == value) { ans = Math.max(ans, key); }
:如果当前键和值相等,则将键key
与ans
中的当前最大值比较,并将较大的值赋给ans
。最终,ans
将保存数组中的幸运数,即值等于其自身的键。 -
return ans;
:返回数组中的幸运数,如果没有幸运数,则返回 -1。
1470. 重新排练数组
双指针解法,最易于理解版,以便理解二分,以及一些更难的双指针算法。
优化题解
给你一个数组 nums
,数组中有 2n
个元素,按 [x1,x2,...,xn,y1,y2,...,yn]
的格式排列。
请你将数组按 [x1,y1,x2,y2,...,xn,yn]
格式重新排列,返回重排后的数组。
示例 1:
输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7]
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]
示例 2:
输入:nums = [1,2,3,4,4,3,2,1], n = 4
输出:[1,4,2,3,3,2,4,1]
示例 3:
输入:nums = [1,1,2,2], n = 2
输出:[1,2,1,2]
提示:
1 <= n <= 500
nums.length == 2n
1 <= nums[i] <= 10^3
题解一
class Solution {
public int[] shuffle(int[] nums, int n) {
int left = 0;
int mid = n;
int index = 0;
int[] ans = new int[2*n];
for (int i = 0; i < n; i++) {
ans[index++] = nums[left++];
ans[index++] = nums[mid++];
}
return ans;
}
}
- 根据题意,数组的长度是偶数,并且要求的新数组就是前半部分和后半部分交替组成的
- 自然而然想到双指针,一个指向数组第一个元素,一个指向数组中间的元素
- 交替赋值给新数组就行了
- 这里维护了一个index索引(更易理解),每次“插入”新数组元素后,index要加一,这里的加一直接写在数组
- 里面了
题解二
class Solution {
public int[] shuffle(int[] nums, int n) {
int[] ans = new int[2 * n];
for (int i = 0; i < n; i++) {
ans[2 * i] = nums[i];
ans[2 * i + 1] = nums[i + n];
}
return ans;
}
}
- 效果和逻辑跟题解一是一样的,更简洁,理解了题解一,这个就能直接理解了
- left 用 i 替代,因为 i 也是从 0 开始,每次循环++ ;mid 用 i + n 代替
- 带进去算一下,发现跟题解一效果是一样的
977. 有序数组的平方
双指针+二分思想
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序
进阶:
- 请你设计时间复杂度为
O(n)
的算法解决本问题
题解一
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
-
暴力解法,先把数组中的元素全都平方,然后排序
Arrays.sort(nums)
是 Java 中用于对数组进行排序的方法。这个方法使用的是快速排序(Quick Sort)算法或归并排序(Merge Sort)算法,具体使用哪种排序算法取决于数组的长度和类型。快速排序的时间复杂度取决于基准元素的选择和数组的划分情况,平均情况下的时间复杂度为 O(n log n),最坏情况下的时间复杂度为 O(n^2),最好情况下的时间复杂度为 O(n log n)。因此,
Arrays.sort(nums)
的时间复杂度为 O(n log n)。 -
很明显这个题解没有达到进阶的 O(n) 的要求
题解二
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
int[] arr = new int[len];
int l = 0;
int r = len - 1;
int index = len -1;
while (l <= r) {
int ls = nums[l] * nums[l];
int rs = nums[r] * nums[r];
if (ls > rs) {
arr[index] = ls;
l++;
}else{
arr[index] = rs;
r--;
}
index--;
}
return arr;
}
}
- 初始化两个指针
l
和r
,分别指向数组nums
的第一个元素和最后一个元素。 - 初始化变量
index
,表示新数组arr
的索引,从数组末尾开始向前遍历。 - 进入循环,循环条件为
l
小于等于r
。在循环内部:- 计算左指针
l
指向元素的平方并赋值给ls
。 - 计算右指针
r
指向元素的平方并赋值给rs
。 - 如果
ls
大于rs
,将ls
放入arr
的当前位置,并将l
向右移动一位;否则,将rs
放入arr
的当前位置,并将r
向左移动一位。 - 将
index
减一,指向下一个位置。
- 计算左指针
题解三
// 优化题解二代码
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] result = new int[n];
int left = 0;
int right = n - 1;
for (int i = n - 1; i >= 0; i--) {
if (Math.abs(nums[left]) > Math.abs(nums[right])) {
result[i] = nums[left] * nums[left];
left++;
} else {
result[i] = nums[right] * nums[right];
right--;
}
}
return result;
}
}
-
先判断两个数的绝对值的大小,然后在进行平方,整个代码更符合逻辑,更简洁
Math.abs()
是 Java 中的一个静态方法,用于返回一个数的绝对值。绝对值是指这个数到原点的距离,即无论这个数是正数还是负数,它的绝对值都是非负数。