11.6双指针

11/6

每日一题:3254.长度为k的子数组的能量值I

给你一个长度为 n 的整数数组 nums 和一个正整数 k

一个数组的 能量值 定义为:

  • 如果 所有 元素都是依次 连续上升 的,那么能量值为 最大 的元素。
  • 否则为 -1 。

你需要求出 nums 中所有长度为 k 的 子数组 的能量值。

请你返回一个长度为 n - k + 1 的整数数组 results ,其中 results[i] 是子数组 nums[i..(i + k - 1)] 的能量值。

示例 1:

输入:nums = [1,2,3,4,3,2,5], k = 3

输出:[3,4,-1,-1,-1]

解释:

nums 中总共有 5 个长度为 3 的子数组:

  • [1, 2, 3] 中最大元素为 3 。
  • [2, 3, 4] 中最大元素为 4 。
  • [3, 4, 3] 中元素 不是 连续的。
  • [4, 3, 2] 中元素 不是 上升的。
  • [3, 2, 5] 中元素 不是 连续的。

示例 2:

输入:nums = [2,2,2,2,2], k = 4

输出:[-1,-1]

示例 3:

输入:nums = [3,2,3,2,3,2], k = 2

输出:[-1,3,-1,3,-1]

提示:

  • 1 <= n == nums.length <= 500
  • 1 <= nums[i] <= 105
  • 1 <= k <= n

思路:

用一个计数器cnt统计连续递增元素的个数,正常icnt是从1开始计数。

如果nums[i] = nums[i - 1] + 1满足连续递增,则cnt++。否则置cnt = 1(不用存入ans)

如果cnt >= k,下标i - k + 1到i的子数组的能量值为最后一个数nums[i]

最后存到ans里要偏移k - 1个位置

考虑边界特殊样例:

nums.size() = k =1 ,则cnt = 1 ,ans[0] = nums[0]

image-20241106161859021

class Solution {
public:
    vector<int> resultsArray(vector<int>& nums, int k) {
      //初始化为-1
        vector<int> ans(nums.size() - k + 1, -1);        
        int cnt = 0;
        for (int i = 0; i < nums.size(); i++) {
            if(i == 0 || nums[i] == nums[i - 1] + 1)  cnt++;
            else cnt = 1;
            
            if (cnt >= k) {
                ans[i - k + 1] = nums[i];
            }
        }
        return ans;
    }
};

27. 移除元素

思路:

NOT AC,方法忘了

请注意 ,必须在不复制数组的情况下原地对数组进行操作。这句话就是在变相地提醒用快慢指针法

双指针法:定义慢指针slow保存最终的数组下标,快指针fast跑完整个数组,如果nums[fast] != val,则把nums[fast]的值保存到下标slow处。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
      int slow = 0;
        for (int fast = 0 ; fast < nums.size() ; fast ++)
        {
          if(nums[fast] != val) nums[slow ++] = nums[fast];
        }
        return slow;
    }
};

相关题:

思路:

快慢指针,跟官方的题解一样。

如果数组 nums 的长度为 0,则数组不包含任何元素,因此返回 0。

当数组 nums 的长度大于 0 时,数组中至少包含一个元素,在删除重复元素之后也至少剩下一个元素,因此 nums[0] 保持原状即可,从下标 1 开始删除重复元素。因此初值slow = fast = 1

定义两个指针 fast 和 slow 分别为快指针和慢指针,fast表示遍历数组到达的下标位置,slow表示下一个不同元素要填入的下标位置。

如果 nums[fast] !=nums[fast−1],说明 nums[fast] 和之前的元素都不同,因此将 nums[fast] 的值复制到 nums[slow],然后将 slow 的值加 1,即指向下一个位置。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slow = 1;
        for (int fast = 1 ; fast < nums.size() ; fast ++){
          if(nums[fast] != nums[fast - 1])  nums[slow ++] = nums[fast];
        }
        return slow;
    }
};

思路:

法一:快慢指针最后slow为新nums的长度,从这里开始把nums后面都赋为0

法二:使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

  • 左指针左边均为非零数
  • 右指针左边直到左指针处均为零

因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow = 0;
        for (int fast = 0 ; fast < nums.size() ; fast ++){
          if(nums[fast] != 0)  nums[slow ++] = nums[fast];
        }
        for(int i = slow ; i < nums.size() ; i ++){
          nums[i] = 0;
        }
    }
};
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
       int l = 0;
       for (int r = 0; r < nums.size(); r++) {
        //nums[r]非0 的话,换到 l左边去
          if(nums[r]) swap(nums[l ++] , nums[r]);
       }        
    }
};

思路:

  1. 用栈处理遍历过程,每次我们遍历到一个字符:是退格符,弹出;普通字符,压入栈中。空间复杂度略高为\(o(m+n)\)

  2. 双指针法。慢指针的左边维护最后生成的数组,快指针跑完整个字符串。最后定义ret数组的时候可以用cpp的技巧直接截取定义:string ret(a , 0 , slow);

    C++string 初始化的几种方式:

    • 方式一 :直接赋值

    string str1 = "test01" ;

    • 方式2 :以length为长度的ch的拷贝(即length个ch)string( size_type length, char ch );

      string str2( 5, 'c' );  //  str2 'ccccc'
      
    • 方式3string( string &str, size_type index, size_type length );

      index为索引开始的子串,长度为length, 或者 以从startend的元素为初值.

      string str4( str3, 0, 4 );  //将str3 的从第0个位置元素起复制4个长度元素到str4
      

      本题用到方式三,不用for循环。

//用栈模拟
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        return tuige(s) == tuige(t);
    }

    string tuige(string a){
      string ret;
      for(char ch : a){
        if(ch != '#')  ret.push_back(ch);
        else if(!ret.empty()) ret.pop_back();
      }
      return ret;
    }
};
class Solution {
public:
    bool backspaceCompare(string s, string t) {    
        return tuige(s) == tuige(t);
    }

    string tuige(string a){
      int slow = 0;//slow 的左边是新的最后的数组
      for (int fast = 0 ; fast < a.size() ; fast ++){
        if(a[fast] != '#')  a[slow ++] = a[fast];
        else if(slow > 0) slow --;
      }
      string ret(a , 0 , slow);
     return ret;
    }
};

NOT AC

思路:

  1. 暴力,时间复杂度主要是遍历数组,所有元素平方 O(n) + 排序O(logn)

  2. 双指针法。

    数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

    此时可以考虑双指针法了,l指向起始位置,r指向终止位置

    定义一个与原数组等长的数组res,因为从大到小,所以指针p从后往前遍历。

    比较nums[l] * nums[l]nums[r] * nums[r]大小,大的存入res。这里注意是res[p] = xxx而不是res.push_back(xxx)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n);
        for(int l = 0 , r = n - 1 ,p = n - 1; l <= r ;  p --){
            if(nums[l] * nums[l] <= nums[r] * nums[r]){
                res[p] = nums[r] * nums[r];
                r --;
            }           
            else 
            {
             res[p] = nums[l] * nums[l];
              l ++;
            }
    }
      return res;
    }
};
posted @ 2024-11-06 22:17  七龙猪  阅读(1)  评论(0)    收藏  举报
-->