LeetCode 笔记九

LeetCode 笔记九

    • 一.解码方法
    • 二.有效的括号字符串
    • 三.数组中重复的数据
    • 四.旋转数组的最小数字
    • 五.旋转图像

前言:主要是记录一些算法题的解题思路与技巧,纯当笔记用。

图片与部分代码来源:leetcode
图片与部分代码来源:leetcode
图片与部分代码来源:leetcode

一.解码方法

力扣链接:力扣链接

LeetCode 笔记九_第1张图片

这题可以用动态规划做,定义 dp 数组,dp[i] 的含义表示为字符串从 0 到 i 的解码方法有多少种。遍历字符串 s 同时更新 dp 数组的值。如果 s[i] 的值不为 0,则 s[i] 可以单独解码为一个字母,dp[i]+=dp[i-1] ; 如果他和 s[i-1] 合并解码是一个符合条件的值, dp[i]+=dp[i-2] 。仔细思考会发现这题本质上与青蛙跳台阶那题是一样的。

class Solution {
public:
    int numDecodings(string s) {
        
        int n = s.size();
        vector<int> f(n + 1);
        f[0] = 1;
        for (int i = 1; i <= n; ++i) {
            
            if (s[i - 1] != '0')    
                f[i] += f[i - 1];   //单独解码
            
            if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) 
                f[i] += f[i - 2];   //合并解码
        }

        return f[n];
    }
};

二.有效的括号字符串

力扣链接:有效的括号字符串

LeetCode 笔记九_第2张图片

关于字符串匹配问题,使用栈来解决是一种很好的思路,这题也是一样。可以创建两个栈,栈一用来存放 ( ,栈二用来存放 * 。遍历字符串 s 。当遇到 ( 时,存入栈一,当遇到 * 时,存入栈二。当遇到 ) 时,可以查看栈一中是否有元素,如果有,则代表存在 ( 可以与 ) 匹配,如果栈一不存在元素而栈二存在元素则 ) 也能被匹配,因为 * 可以当成 ( 使用。

如果遍历完字符串 s 后栈一中还存在元素,则说明还有未被匹配的 ( ,可以查看栈二中是否存在元素,使用 * 来匹配还未被匹配的 ( 。但要注意的一点是如果你的栈中存放的是字符的话,那么你没办法判断 * 与 ( 的相对位置。也就是说会出现 * 在 ( 后面的情况,显然这种情况是不符合条件的。所以我们的栈中不存字符,而是存放字符在字符串中的相对位置。这样就能判断出来了。

class Solution {
public:
    bool checkValidString(string s) {

         stack<int> sck1;
         stack<int> sck2;
         for(int i=0;i<s.size();++i){

             if(s[i]=='(')   //遇到 (
               sck1.push(i);
             else if(s[i]=='*')  //遇到 *
               sck2.push(i);
             else if(s[i]==')'){   //遇到 )

                 if(sck1.size())
                    sck1.pop();
                 else if(sck2.size())
                    sck2.pop();
                 else
                    return false;
             }
         }

         while(sck1.size() && sck2.size()){    //判断未必匹配的 ( 是否能被匹配

             if(sck2.top()<sck1.top())
                return false;
             sck1.pop();
             sck2.pop();
         }

         return sck1.empty();
    }
};

三.数组中重复的数据

力扣链接:数组中重复的数据

LeetCode 笔记九_第3张图片

如果没有时间复杂度的要求,那么使用哈希表应该是很自然也很容易的方法。但不能使用额外的空间,而且这题显然是要使用哈希表的。对于这样的问题,应该想到使用原数组作为哈希表来进行标记,这算是这类题型常用的套路,一个小技巧。

我们将每个数放在它数值对应的下标处,如 5 应该放在下标为 4 的地方,3 应该放在下标为 2 的地方,如果数字出现两次,那么就还有一个相等的数字不在数值对应的下标处,对于这种数,我们把它加入结果集。

class Solution {
public:
    vector<int> findDuplicates(vector<int>& nums) {
        
          int n=nums.size();
          vector<int> ret;
          for(int i=0;i<n;++i){

              while(nums[i]!=nums[nums[i]-1])     //把数字放到它数值对应的下标处,循环处理是为了把交换过来的数字也放到它数值对应的下标处,否则会漏掉一些数
                   swap(nums[i],nums[nums[i]-1]);
          }

          for(int i=0;i<n;++i){

              if(nums[i]!=i+1)    //找到出现了两次的数字,加入结果集
                ret.push_back(nums[i]);
          }

          return ret;
    }
};

四.旋转数组的最小数字

力扣链接:旋转数组的最小数字

LeetCode 笔记九_第4张图片

还是惯常的思路,遇到有序数组,找某个数,首先应该想到的就是二分法。这题虽然把有序数组进行了旋转,但还是可以用二分法。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        
        int left = 0,right=numbers.size()-1;
        while (left < right) {
            
            int mid = left + (right - left) / 2;
            if (numbers[mid] < numbers[right])    //未被翻转的部分,最小的数在 mid 前面
                right = mid;
            else if (numbers[mid] > numbers[right])   //最小的数在 mid 与 right 之间
                left = mid + 1;
            else 
                right -= 1;
        }

        return numbers[left];
    }
};

五.旋转图像

力扣链接:旋转图像

LeetCode 笔记九_第5张图片

要旋转 90 度,可以先水平翻转,在沿主对角线翻转就行了。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        
         //水平翻转
         int n=matrix.size();
         for(int i=0;i<n/2;++i){
             for(int j=0;j<n;++j)
                 swap(matrix[i][j],matrix[n-i-1][j]);
         }

         //对角线翻转
         for(int i=0;i<n;++i){
             for(int j=0;j<i;++j)
                 swap(matrix[i][j],matrix[j][i]);
         }
    }
};

你可能感兴趣的:(LeetCode,笔记,leetcode,算法)