力扣网-复写零

发布于:2025-05-20 ⋅ 阅读:(11) ⋅ 点赞:(0)

1.题目要求

2.题目链接

1089. 复写零 - 力扣(LeetCode)

3.题目解答

class Solution {
    public void duplicateZeros(int[] arr) {
        int cur=0,dest=-1,n=arr.length;
        while(cur<n){
            //遇到0就dest走两步
            if(arr[cur]==0){
                dest+=2;
            }
            //遇到非零元素dest就走一步
            else{
                dest+=1;
            }
            //如果dest发生越界或者出现边界情况,就break跳出循环
            if(dest>=n-1){
                break;
            }
            //不跳出循环的情况下,cur++;
            cur++;
       }
       //判断边界情况,也就是dest==n,倒数第二个元素是0,dest+=2,导致越界。
       if(dest==n){
        arr[n-1]=0;
        dest-=2;cur-=1;
       }
       //找到cur和dest的初始位置后,开始从后往前遍历。
       while(cur>=0){
        if(arr[cur]!=0){
            arr[dest--]=arr[cur--];
        }
        //这里如果是if(arr[cur]==0)的话,就会报错,因为两个if语句是存在顺序关系的,会从上而下的依次判断条件并执行,有可能会重复执行导致数组越界。
        else{
            arr[dest--]=0;
            arr[dest--]=0;
            cur--;
        }   
       }
    }
}

4.解题思路

(1)遍历顺序

首先我们应该知道的是,这道题使用的是双指针算法,并且,如果是按从前往后的顺序执行的话,是无法达到题目要求的。这是因为双指针从前往后执行的过程中,会覆盖掉一些值导致错误。

所以这道题应该是使用双指针算法从后往前的遍历,那么,双指针的起始位置应该怎么找呢?

(2)双指针的起始位置:嵌套双指针

这里我们在双指针从后往前遍历前,嵌套一个双指针来先找到起始位置。我们可以先模拟题目的过程:即遇到0复写2次0,我们先不考虑数组值的变化,单纯模拟题目流程,也就是指针dest+=2,cur+=1遇到非零元素,单纯抄写一遍即可,也就是dest++,cur++。

while(cur<n){
            //遇到0就dest走两步
            if(arr[cur]==0){
                dest+=2;
            }
            //遇到非零元素dest就走一步
            else{
                dest+=1;
            }
            //如果dest发生越界或者出现边界情况,就break跳出循环
            if(dest>=n-1){
                break;
            }
            //不跳出循环的情况下,cur++;
            cur++;
       }

遇到dest=arr.length-1时,也就是dest遍历到在最后一个元素时,停止遍历。得到双指针的初始位置。

(3)特殊情况

如果数组的倒数第二个元素时0,也就是arr【n-2】=0时,这时dest+=2,dest=n,发生数组越界,导致报错。所以这里我们和上面一样,在break跳出循环后,要判断一下。

//判断边界情况,也就是dest==n,倒数第二个元素是0,dest+=2,导致越界。
       if(dest==n){
        arr[n-1]=0;
        dest-=2;cur-=1;
       }

这时,我们把数组最后一个元素置为0,并且dest指针后退两步,cur指针后退一步,即可正确得到初始位置。

(4)双指针从后往前遍历

 //找到cur和dest的初始位置后,开始从后往前遍历。
       while(cur>=0){
        if(arr[cur]!=0){
            arr[dest--]=arr[cur--];
        }
        //这里如果是if(arr[cur]==0)的话,就会报错,因为两个if语句是存在顺序关系的,会从上而下的依次判断条件并执行,有可能会重复执行导致数组越界。
        else{
            arr[dest--]=0;
            arr[dest--]=0;
            cur--;
        }   
       }

这里比较巧妙地运用了arr【dest--】=arr【cur--】和arr【dest--】=0,使得赋值的同时也完成了指针的移动。 

5.代码细节

(1)代码简洁

   while(cur>=0){
        if(arr[cur]!=0){
            arr[dest--]=arr[cur--];
        }
        //这里如果是if(arr[cur]==0)的话,就会报错,因为两个if语句是存在顺序关系的,会从上而下的依次判断条件并执行,有可能会重复执行导致数组越界。
        else{
            arr[dest--]=0;
            arr[dest--]=0;
            cur--;
        }   
       }

 这里的两种情况要采用if-else,而不可以使用两个if语句进行判断。因为如果使用两个if语句判断,他们会形成先后顺序,在第一个if语句进入后,有可能触发第二个if语句,从而导致数组越界。

(2)越界处理

//判断边界情况,也就是dest==n,倒数第二个元素是0,dest+=2,导致越界。
       if(dest==n){
        arr[n-1]=0;
        dest-=2;cur-=1;
       }

 在dest==n这种情况下,是因为cur检测到数组倒数第二个元素是0,导致dest走两步到n,发生数组下标越界。为了处理这种情况,我们可以手动覆写数组最后一个元素为0,这样就等于复写了倒数第二个元素0

完成复写倒数第二个元素后,我们把双指针对应前移,也就是dest-=2,cur-=1。这样我们就可以忽略倒数第二个元素,从而从后往前进行复写操作(因为我们已经通过arr【n-1】=0操作手动复写了n-2下标的0元素)。


网站公告

今日签到

点亮在社区的每一天
去签到