LeetCode 41 ~ 50

41. 缺失的第一个正数

桶排序:

  1. 若数字不为INT_MIN,则将该数字自减一次
  2. 使用桶排序,将每个数字放到对应索引的位置上
  3. 最后遍历一遍数组,若nums[i] != i,则返回i + 1

时间复杂度为 O ( n ) O(n) O(n),不使用额外空间,空间复杂度为 O ( 1 ) O(1) O(1)

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        if (!n) return 1;

        for (auto& x : nums)
            if (x != INT_MIN) x -- ;

        for (int i = 0; i < n; i ++ ) {
          	//while循环每次会将一个数放在自己对应的位置上,从i由0 递增到 n 的过程中,最多执行n次
            while (nums[i] >= 0 && nums[i] < n && nums[i] != i && nums[i] != nums[nums[i]])
                swap(nums[i], nums[nums[i]]);
        }
				
      	//再遍历一次返回正确答案
        for (int i = 0; i < n; i ++ )
            if (nums[i] != i)
                return i + 1;

        return n + 1;
    }
};
42. 接雨水
class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> stk;
        int res = 0;
        for(int i = 0; i < height.size(); i ++ ) {
            int last = 0;
            while(stk.size() && height[stk.top()] <= height[i]) {
                res += (height[stk.top()] - last) * (i - stk.top() - 1);
                last = height[stk.top()];
                stk.pop();
            }

            if(stk.size()) res += (i - stk.top() - 1) * (height[i] - last);
            stk.push(i);
        }      
        return res;
    }
};
43. 字符串相乘

模拟

  1. 使用两个vector数组A,B从后往前存放两个字符串数字的每一位
  2. 模拟乘法规则,使用第三个vector数组C存放两个数组对应位的乘积,C[i + j] = A[i] * B[j],遍历两个数组每一位
  3. 将C中每个位上的数字取余再进位,去除前导零
  4. 将C数组中的数字逐个转换成字符串拼接
class Solution {
public:
    string multiply(string num1, string num2) {
        vector<int> A, B;
        int n = num1.size(), m = num2.size();
        for(int i = n - 1; i >= 0; i -- ) A.push_back(num1[i] - '0');
        for(int i = m - 1; i >= 0; i -- ) B.push_back(num2[i] - '0');
        
        vector<int> C(n + m);
        for(int i = 0; i < n; i ++ )
            for(int j = 0; j < m; j ++ )
                C[i + j] += A[i] * B[j];
        
        for(int i = 0, t = 0; i < C.size(); i ++ ) {
            t += C[i];
            C[i] = t % 10;
            t /= 10;
        }

        int k = C.size() - 1;
        while(k > 0 && !C[k]) k -- ;

        string res;
        while(k >= 0) res += C[k -- ] + '0';

        return res;
    }
};
44. 通配符匹配
class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        f[0][0] = true;

        for(int i = 0; i <= n; i ++ )
            for(int j = 1; j <= m; j ++ ) {
                if(p[j] == '*') {
                    f[i][j] = f[i][j - 1] || i && f[i - 1][j];
                } else {
                    f[i][j] = (s[i] == p[j] || p[j] == '?') && i &&  f[i - 1][j - 1];
                }
            }
        
        return f[n][m];
    }
};
45. 跳跃游戏 II
  1. 状态表示: f ( i ) f(i) f(i)表示走i步最远可以到达的距离
  2. 初始化: f ( 0 ) = 0 f(0) = 0 f(0)=0 表示走0最远距离0
  3. i的每一次移动都可以到达 [ i + 1 , i + n u m s ( i ) ] [i+1, i+nums(i)] [i+1,i+nums(i)]这个区间中的任意位置,从1遍历到n的过程中,通过i第一次可以到达j时,j的最少步数为i的最少步数加1。
  4. 状态转移: f ( i ) = f ( j ) + 1 f(i)=f(j) + 1 f(i)=f(j)+1,j j + n u m s [ j ] > = i j + nums[j] >= i j+nums[j]>=i的第一个值
class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n);
        for(int i = 1, j = 0; i < n; i ++ ) {
            while(j + nums[j] < i) j ++ ;
            f[i] = f[j] + 1;
        }
        return f[n - 1];
    }
};

双指针解法

  1. 定义指针 cur代表当前位置和dis代表最远可以到达的位置
    1. 移动curdis,记录最远可达位置nxt
    2. 答案+1
    3. 更新disnxt
  2. dis大于n-1视为可达,返回答案
class Solution {
public:
    int jump(vector<int>& nums) {
        const int n = nums.size();
        int ans = 0, cur = 0, dis = 0;

        while (dis < n - 1) {
            int next = 0;
          	
          	//dis标记目前的cur最远可以到达的位置
          	//超过dis的位置,当前的cur都无法到达
            while (cur <= dis) {
              	//next记录最远当前cur最远可达的位置
                next = max(next, cur + nums[cur]);
                cur++;
            }
				
            ans++;
          	//更新dis
            dis = next;
        }

        return ans;
    }
};
46. 全排列

从前往后一位一位枚举,每次选择一个还没有被使用的数字,计算出所有的可能

class Solution {
    vector<vector<int>> res;
    vector<int> cur;
    vector<bool> st;
public:
    void dfs(vector<int>& nums, int u)
    {
        if(u == nums.size())	//枚举完所有数字
        {
            res.push_back(cur);
            return;
        }
      
				//从前往后枚举
        for(int i = 0; i < nums.size(); i ++ ) 
            if(!st[i])	//选择还没有在路径数组中的数字
            {
                cur[u] = nums[i];
                st[i] = true;	//标记该数字
                dfs(nums, u + 1);
                st[i] = false;//回溯
            }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        st.assign(nums.size(), false);
        cur.resize(nums.size());
        dfs(nums, 0);
        return res;
    }
};
47. 全排列 II

思路同46.,初始化的时候先排序一遍,枚举时再增加一个判断是否和上一个数字相同的条件即可

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<bool> st;
    
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        path = vector<int>(nums.size());
        st = vector<bool>(nums.size());
        dfs(nums, 0);
        return ans;
    }

    void dfs(vector<int>& nums, int u) {
        if(u == nums.size()) {
            ans.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); i ++ ) {
            if(!st[i]) {
                if(i && nums[i - 1] == nums[i] && !st[i - 1]) continue;
                st[i] = true;
                path[u] = nums[i];
                dfs(nums, u + 1);
                st[i] = false; 
            }
        }
    }
};
48. 旋转图像

先沿着中轴上下交换,再沿着对角线一一交换,即可顺时针旋转90度

123       789        741
456 ----> 456 -----> 852
789       123        963
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = (int)matrix.size() - 1;
      	//上下交换
        for(int i = 0, j = n; i < j; i ++ , j -- )
            for(int k  = 0; k <= n; k ++ )
                swap(matrix[i][k], matrix[j][k]);
        //沿着对角线交换
        for(int k = 0; k <= n; k ++ )
            for(int i = k, j = k; i >= 0; i -- , j -- )
                swap(matrix[i][k], matrix[k][j]);
    }
};
49. 字母异位词分组
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> hash;
        for(auto& str : strs) {
            string nstr = str;
            sort(nstr.begin(), nstr.end());
            hash[nstr].push_back(str);
        }

        vector<vector<string>> res;
        for(auto &item : hash) res.push_back(item.second);

        return res;
    }
};
50. Pow(x, n)

比较典型的一道快速幂题目,可以使用位运算实现,也可以使用递归的做法实现,这里采用位运算的实现方式

class Solution {
public:
    double myPow(double x, int n) {
    	//long long类型方便计算
        typedef long long LL; 
        
		//记录指数幂是否为负数							
        bool is_minus = n < 0;
        //res 记录答案
        double res = 1;

		//位运算,时间复杂度为O(log n)
        for (LL k = abs(LL(n)); k; k >>= 1) {
        	
        	//如果k的二进制串表示最末尾的一位为1,则res乘以x
            if (k & 1) res *= x;
			
			//x倍增
            x *= x;
        }
        if (is_minus) res = 1 / res;
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值