Leetcode:27,26,283,844,977
Leetcode 27
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
即符合条件了慢指针才会移动,否则只有快指针在移动。关键点是用一个if来限制,慢指针放在if里面。
class Solution {
public int removeElement(int[] nums, int val) {
int n = nums.length;
int newIndex =0;
for (int i = 0; i < n; i++) {
if(nums[i]!=val){
nums[newIndex] = nums[i];
newIndex++;
}
}
return newIndex;
}
}
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while (right >= 0 && nums[right] == val) {
right--;
}
while (left <= right) {
if (nums[left] == val) {
nums[left] = nums[right];
right--;
}
left++;
while (right >= 0 && nums[right] == val) {
right--;
}
}
return left;
}
}
为什么这道题使用while循环更为方便
ChatGPT
对于这个特定的问题(从数组中移除指定元素),使用 while
循环比 for
循环更直观和简洁,主要有以下几点原因:
-
变量更新不是均匀的:在该问题中,
left
和right
指针的更新并不总是每次循环迭代时均匀增加或减少。right
可能需要连续减少多次以跳过连续的目标值,而left
可能在某些情况下不移动(当它指向的值不是目标值时)。while
循环可以更灵活地处理这种非均匀的更新。 -
多重条件判断:在处理数组时,我们经常需要根据数组中的实际值来决定下一步的操作。在这个问题中,我们需要根据
left
和right
指向的值来决定它们是否应该移动。这导致我们有多个内嵌的条件判断,这在while
循环中更容易处理。 -
直观性:从概念上讲,我们正在“搜索”数组,以找到需要交换或移动的值。搜索和找到通常与
while
循环相关,因为我们不知道需要多少步才能找到我们正在寻找的东西
但是第二个while里有while(right >= 0 && nums[right] == val) right--;
ChatGPT
对,我明白你的担忧。但让我们进一步分析这个嵌套的 while
循环。
考虑这个情况:当 left
指向一个需要移除的值(即值为 val
)时,我们用 right
指向的值来覆盖它。在此过程中,我们递减 right
。然后,在外部 while
循环的下一个迭代中,我们递增 left
。
此时,我们使用内部 while
循环来跳过所有从右侧开始的值为 val
的元素。然后,重复此过程,直到 left
超过 right
。
关键是注意到,无论是通过外部 while
循环的直接操作还是内部 while
循环的影响,left
和 right
都至多遍历数组一次。
left
从数组的开始到结束遍历一次。right
从数组的结束到开始遍历一次。
因此,每个元素最多只被检查两次(一次由 left
,一次由 right
)。这导致了总的 �(2�)O(2n) 复杂度,这仍然是线性的 �(�)O(n)。
尽管在循环中存在嵌套结构,但不是所有的嵌套循环都导致 �(�2)O(n2) 的复杂度。在这个特定的情况下,由于外部和内部循环都与 left
和 right
的单向移动有关,总体效率仍然是线性的。
给你一个 升序排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
这个可以用双指针的思路是只有不一样了b慢指针才会移动,否则只有快指针在移动。
class Solution {
public int removeDuplicates(int[] nums) {
int slowIndex = 1;
for (int fastIndex = 1; fastIndex < nums.length; fastIndex++) {
if(nums[fastIndex]!=nums[fastIndex-1]){
nums[slowIndex]=nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}
是slowIndex还是slowIndex+1,有什么好的方法来判断吗
ChatGPT
在这种双指针方法中,slowIndex
通常是指向下一个要填充的位置。所以,当整个过程结束时,slowIndex
指向的是新数组的下一个空位置,而不是最后一个元素的位置。
因此,新数组的长度就是 slowIndex
的值,你不需要加1。
判断方法:
-
理解指针的含义:在这种双指针策略中,
slowIndex
通常用于指示下一个应该插入的位置。如果你始终保持这种含义,那么你可以很容易地确定在循环结束时slowIndex
的值应该是多少。
如果数组长度为1这个代码还可以使用吗
ChatGPT
对于数组长度为1的情况,你给出的代码确实可以正常运行。但是,由于 slowIndex
初始值为1,所以返回值为1,表示新的数组长度为1,这是正确的。
考虑以下情况:
-
当数组为
[1]
(任何单一元素都行),fastIndex
从1开始并且直接超过了数组的长度,因此for
循环不会执行。slowIndex
保持为1,这是新数组的正确长度。 -
当数组为
[1,1]
或[1,2]
,fastIndex
从1开始,进入循环。但是,不论如何,循环结束后,slowIndex
的值为1或2,分别对应数组的新长度。
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution {
public void moveZeroes(int[] nums) {
int left = 0;
int n= nums.length;
for (int right = 0; right < nums.length; right++) {
if (nums[right] != 0) {
nums[left] = nums[right];
left++;
}
}
for (int i = left; i < n; i++) {
nums[i]=0;
}
}
}
依然和上一题一样,left指向了下一个要填充的位置。而且left的值是新数组的长度。所以添加零的时候因为是从下一个要填充的位置开始添加,所以是 int i = left.小于n就是和前面一样。添加了多少个:n-left.(老数组长度减去新数组长度)。
在 Java 中,String
类提供了许多常用的方法来处理字符串。以下是一些常用的 String
方法
-
length()
: 返回字符串的长度。charAt(int index)
: 返回指定索引处的字符。isEmpty()
: 检查字符串是否为空。equals(Object obj)
: 检查字符串是否等于给定的对象。toCharArray()
: 转换此字符串为一个新的字符数组。
StringBuffer
是 Java 中用于处理可变字符串的类。与String
不同,StringBuffer
的内容是可以修改的。以下是StringBuffer
的一些常用方法:
append(dataType data)
: 追加指定数据类型的字符串表示形式到此序列。这个方法有多个重载版本,可以接受不同类型的输入,如 String
, int
, char[]
, boolean
等。
deleteCharAt(int index)
: 删除此序列指定位置的 char
。
StringBuffer定义出来的字符串不能用toCharArray是吗
ChatGPT
ChatGPT
对的,StringBuffer
定义出来的字符串不能直接使用 toCharArray
方法。这是因为 StringBuffer
和 String
是不同的类型。String
是一个不可变的字符序列,而 StringBuffer
是一个可变的字符序列。String
类有一个 toCharArray
方法,可以将字符串转换为字符数组,但 StringBuffer
没有这个方法。
如果你想将 StringBuffer
对象转换为字符数组,你首先需要将 StringBuffer
转换为 String
,然后使用 String
的 toCharArray
方法。这可以通过调用 StringBuffer
的 toString
方法来实现。
示例代码
javaCopy code
StringBuffer stringBuffer = new StringBuffer("Example"); String str = stringBuffer.toString(); // 将 StringBuffer 转换为 String char[] chars = str.toCharArray(); // 然后将 String 转换为字符数组
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
这道题是在新数组里就好。
需要注意的是这个数组并不是完全乱序,所以排序和这道题还不一样
思路是最大的数只会在两边
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int left = 0;
int right = n-1;
int index = n-1;
int[] ans = new int[n];
while(left<=right){
if(nums[left]*nums[left]>nums[right]*nums[right]){
ans[index]=nums[left]*nums[left];
left++;
}else {
ans[index]=nums[right]*nums[right];
right--;
}
index--;
}
return ans;
}
}
中间关于left,right符号的判断,要想需不需要等于的时候的值,显然是需要的。