Day1-2数组

文章讲述了在LeetCode题目中处理数组操作的技巧,包括移除指定元素、删除重复项、移动零和比较含退格的字符串,以及在有序数组中计算平方并保持非递减顺序。讨论了使用双指针法、while循环和StringBuilder类的适用场景。
摘要由CSDN通过智能技术生成

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 循环更直观和简洁,主要有以下几点原因:

  1. 变量更新不是均匀的:在该问题中,leftright 指针的更新并不总是每次循环迭代时均匀增加或减少。right 可能需要连续减少多次以跳过连续的目标值,而 left 可能在某些情况下不移动(当它指向的值不是目标值时)。while 循环可以更灵活地处理这种非均匀的更新。

  2. 多重条件判断:在处理数组时,我们经常需要根据数组中的实际值来决定下一步的操作。在这个问题中,我们需要根据 leftright 指向的值来决定它们是否应该移动。这导致我们有多个内嵌的条件判断,这在 while 循环中更容易处理。

  3. 直观性:从概念上讲,我们正在“搜索”数组,以找到需要交换或移动的值。搜索和找到通常与 while 循环相关,因为我们不知道需要多少步才能找到我们正在寻找的东西

但是第二个while里有while(right >= 0 && nums[right] == val) right--;

ChatGPT

对,我明白你的担忧。但让我们进一步分析这个嵌套的 while 循环。

考虑这个情况:当 left 指向一个需要移除的值(即值为 val)时,我们用 right 指向的值来覆盖它。在此过程中,我们递减 right。然后,在外部 while 循环的下一个迭代中,我们递增 left

此时,我们使用内部 while 循环来跳过所有从右侧开始的值为 val 的元素。然后,重复此过程,直到 left 超过 right

关键是注意到,无论是通过外部 while 循环的直接操作还是内部 while 循环的影响,leftright 都至多遍历数组一次。

  • left 从数组的开始到结束遍历一次。
  • right 从数组的结束到开始遍历一次。

因此,每个元素最多只被检查两次(一次由 left,一次由 right)。这导致了总的 �(2�)O(2n) 复杂度,这仍然是线性的 �(�)O(n)。

尽管在循环中存在嵌套结构,但不是所有的嵌套循环都导致 �(�2)O(n2) 的复杂度。在这个特定的情况下,由于外部和内部循环都与 leftright 的单向移动有关,总体效率仍然是线性的。

26. 删除有序数组中的重复项

给你一个 升序排列 的数组 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。

判断方法:

  1. 理解指针的含义:在这种双指针策略中,slowIndex 通常用于指示下一个应该插入的位置。如果你始终保持这种含义,那么你可以很容易地确定在循环结束时 slowIndex 的值应该是多少。

如果数组长度为1这个代码还可以使用吗

ChatGPT

对于数组长度为1的情况,你给出的代码确实可以正常运行。但是,由于 slowIndex 初始值为1,所以返回值为1,表示新的数组长度为1,这是正确的。

考虑以下情况:

  1. 当数组为 [1](任何单一元素都行),fastIndex 从1开始并且直接超过了数组的长度,因此 for 循环不会执行。slowIndex 保持为1,这是新数组的正确长度。

  2. 当数组为 [1,1][1,2]fastIndex 从1开始,进入循环。但是,不论如何,循环结束后,slowIndex 的值为1或2,分别对应数组的新长度。

283. 移动零

给定一个数组 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.(老数组长度减去新数组长度)。

844. 比较含退格的字符串

在 Java 中,String 类提供了许多常用的方法来处理字符串。以下是一些常用的 String 方法

    • length(): 返回字符串的长度。
    • charAt(int index): 返回指定索引处的字符。
    • isEmpty(): 检查字符串是否为空。
    • equals(Object obj): 检查字符串是否等于给定的对象。
    • toCharArray(): 转换此字符串为一个新的字符数组。

  1. StringBuffer 是 Java 中用于处理可变字符串的类。与 String 不同,StringBuffer 的内容是可以修改的。以下是 StringBuffer 的一些常用方法:

append(dataType data): 追加指定数据类型的字符串表示形式到此序列。这个方法有多个重载版本,可以接受不同类型的输入,如 String, int, char[], boolean 等。

deleteCharAt(int index): 删除此序列指定位置的 char

StringBuffer定义出来的字符串不能用toCharArray是吗

ChatGPT

ChatGPT

对的,StringBuffer 定义出来的字符串不能直接使用 toCharArray 方法。这是因为 StringBufferString 是不同的类型。String 是一个不可变的字符序列,而 StringBuffer 是一个可变的字符序列。String 类有一个 toCharArray 方法,可以将字符串转换为字符数组,但 StringBuffer 没有这个方法。

如果你想将 StringBuffer 对象转换为字符数组,你首先需要将 StringBuffer 转换为 String,然后使用 StringtoCharArray 方法。这可以通过调用 StringBuffertoString 方法来实现。

示例代码

 

javaCopy code

StringBuffer stringBuffer = new StringBuffer("Example"); String str = stringBuffer.toString(); // 将 StringBuffer 转换为 String char[] chars = str.toCharArray(); // 然后将 String 转换为字符数组

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 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符号的判断,要想需不需要等于的时候的值,显然是需要的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值