双指针强化练习

双指针强化练习

以下是一些经典的双指针题目,这些题目不仅可以帮助你强化双指针技巧,还能加深你对常见算法的理解。

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

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

**解法:**使用双指针法,left指针跟踪新的唯一元素位置,right指针遍历数组,找到与left指针指向的元素不同的元素时,将right指针指向的元素放到left + 1的位置。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int left=0, right=0;
        while(right<nums.size()){
            if(nums[right]!=nums[left]){
                left++;
                nums[left]=nums[right];
            }else{
                right++;
            }
        }
        return left+1;
    }
};

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

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前七个元素被修改为 0, 0, 1, 1, 2, 3, 3。不需要考虑数组中超出新长度后面的元素。

**解法:**使用双指针left指针维护当前可用的位置,right指针遍历数组。通过count变量来记录当前元素的出现次数,若次数小于等于2,则保留该元素。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()<=2) return nums.size();
        int left=0, right=1;
        int count=1;
        while(right<nums.size()){
            if(nums[right]==nums[right-1]){
                count=count+1;
            }else{
                count=1;
            }
            if(count<=2){
                left++;
                nums[left]=nums[right];
            }
            right++;
        }
        return left+1;
    }
};

125. 验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

示例 2:

输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。

示例 3:

输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

解法:使用两个指针leftright,分别从字符串的两端开始,跳过非字母数字字符并比较字符是否相同。

class Solution {
public:
    bool isPalindrome(string s) {
        string news;
        for(int i=0; i<s.size();i++){
            char temp=s[i];
            if(temp>='A'&&temp<='Z') temp=temp-'A'+'a';
            if((temp>='a' && temp <= 'z' ) || (temp>='0' && temp<='9')){
                news+=temp;
            }
        }
        int left=0, right=news.size()-1;
        while(left<=right){
            if(news[left]==news[right]){
                left++;
                right--;
            }else{
                return false;
            }
        }
        return true;
    }
};

75. 颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数 012 分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

解法:使用三指针法(p0p2p)。p0指向数组的开始,负责放置0p2指向数组的末尾,负责放置2p用于遍历数组。

class Solution {
public:
    void sortColors(vector<int>& nums) {
        //整3个指针,p0指针负责存放开头0,p2指针负责存放开头2,p指针负责便利寻找,p0和p2分完了,自然都是1
        int p0=0, p2=nums.size()-1, p=0;
        while(p<=p2){
            if(nums[p]==0){
                swap(nums,p0,p);
                p0++;p++;//因为p是从前往后遍历的,所以p前面的都遍历过(0或1),所以可跳过
            }else if(nums[p]==2){
                swap(nums,p,p2);
                p2--;
            }else{p++;}
        }
    }
    void swap(vector<int>& nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
};

88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。

**注意:**最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

解法:使用三个指针p1p2p,分别从nums1nums2的末尾开始,比较两个数组的元素,将较大的元素放到nums1的末尾。

class Solution {
public:
    //直接从nums1后面往前面放最大的元素(nums1前段和nums2对比)即可
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p=nums1.size()-1;
        int p1=m-1;
        int p2=n-1;
        while(p1>=0&&p2>=0){
            if(nums1[p1]>=nums2[p2]){
                nums1[p]=nums1[p1];
                p1--;
            }else{
                nums1[p]=nums2[p2];
                p2--;
            }
            p--;
        }
        while(p2>=0){
            nums1[p]=nums2[p2];
            p2--;
            p--;
        }
    }
};

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]

解法:使用双指针法,left指针从数组的左侧开始,right指针从数组的右侧开始。比较两边的绝对值,平方后较大的数放到结果数组的末尾。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> res(nums.size(),0);
        int left=0, right=nums.size()-1;
        int p=res.size()-1;
        while(left<=right){
            if(abs(nums[left])>=abs(nums[right])){
                res[p]=nums[left]*nums[left];
                left++;
            }else{
                res[p]=nums[right]*nums[right];
                right--;
            }
            p--;
        }
        return res;
    }
};

1329. 将矩阵按对角线排序

矩阵对角线 是一条从矩阵最上面行或者最左侧列中的某个元素开始的对角线,沿右下方向一直到矩阵末尾的元素。例如,矩阵 mat63 列,从 mat[2][0] 开始的 矩阵对角线 将会经过 mat[2][0]mat[3][1]mat[4][2]

给你一个 m * n 的整数矩阵 mat ,请你将同一条 矩阵对角线 上的元素按升序排序后,返回排好序的矩阵。

示例 1:

输入:mat = [[3,3,1,1],[2,2,1,2],[1,1,1,2]]
输出:[[1,1,1,1],[1,2,2,2],[1,2,3,3]]

示例 2:

输入:mat = [[11,25,66,1,69,7],[23,55,17,45,15,52],[75,31,36,44,58,8],[22,27,33,25,68,4],[84,28,14,11,5,50]]
输出:[[5,17,4,1,52,7],[11,11,25,45,8,69],[14,23,25,44,58,15],[22,27,31,36,50,66],[84,28,75,33,55,68]]
class Solution {
public:
    vector<vector<int>> diagonalSort(vector<vector<int>>& mat) {
        int m = mat.size();
        int n = mat[0].size();
        unordered_map<int,vector<int>> map;
        //元素放入哈希表中,(i-j)用于区分每一个对角线
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                map[i-j].push_back(mat[i][j]);
            }
        }
        //分别对每个对角线上的元素进行排序
        for(auto& [_,num]:map){
            sort(num.begin(),num.end());
        }
        //排序后的元素放进去
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                mat[i][j]=map[i-j].front();
                map[i-j].erase(map[i-j].begin());
            }
        }
        return mat;
    }
};

1260. 二维网格迁移

给你一个 mn 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。

每次「迁移」操作将会引发下述活动:

  • 位于 grid[i][j]j < n - 1)的元素将会移动到 grid[i][j + 1]
  • 位于 grid[i][n - 1] 的元素将会移动到 grid[i + 1][0]
  • 位于 grid[m - 1][n - 1] 的元素将会移动到 grid[0][0]

请你返回 k 次迁移操作后最终得到的 二维网格

示例 1:

输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1
输出:[[9,1,2],[3,4,5],[6,7,8]]

示例 2:

输入:grid = [[3,8,1,9],[19,7,2,5],[4,6,11,10],[12,0,21,13]], k = 4
输出:[[12,0,21,13],[3,8,1,9],[19,7,2,5],[4,6,11,10]]

示例 3:

输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 9
输出:[[1,2,3],[4,5,6],[7,8,9]]

二维转一维数组进行处理,把后面k个调到前面,就是先反转后面k个,再反转前部分,最后整体反转

class Solution {
public:
    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int m=grid.size();
        int n=grid[0].size();
        k=k%(m*n);
        vector<int> temp{};
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                temp.push_back(grid[i][j]);
            }
        }
        reverse(temp.end()-k,temp.end());
        reverse(temp.begin(),temp.end()-k);
        reverse(temp.begin(),temp.end());
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                grid[i][j]=temp[i*n+j];
            }
        }
        return grid;
    }
};

867. 转置矩阵

给你一个二维整数数组 matrix, 返回 matrix转置矩阵 。矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[1,4,7],[2,5,8],[3,6,9]]

示例 2:

输入:matrix = [[1,2,3],[4,5,6]]
输出:[[1,4],[2,5],[3,6]]
class Solution {
public:
    vector<vector<int>> transpose(vector<vector<int>>& matrix) {
        //因为这个数组如果是2*3的,转置后就是3*2的,所以无法在原数组上修改,只能新建一个矩阵
        int m = matrix.size();
        int n = matrix[0].size(); 
        vector<vector<int>> result(n,vector<int>(m));
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
               result[j][i]=matrix[i][j];
            } 
        }
        return result;
    }
};

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        int m = strs.size(); //行数
        int n = strs[0].size(); //列数
        string res;
        for(int j=0; j<n; j++){
            bool issame=true;
            for(int i=1; i<m; i++){
                char now = strs[i][j];
                char prev = strs[i-1][j];
                if(now != prev){
                    issame=false;
                }
            }
            if(issame==false){
                return res;
            }else{
                res.push_back(strs[0][j]);
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值