C++ : 力扣_Top(62-84)
文章目录
- C++ : 力扣_Top(62-84)
- 62、不同路径(中等)
- 66、加一(简单)
- 69、x的平方根(中等)
- 70、爬楼梯(简单)
- 73、矩阵置零(中等)
- 75、颜色分类(中等)
- 76、最小覆盖子串(困难)
- 78、子集(中等)
- 79、单次搜索(中等)
- 84、柱状图中最大的矩形(困难)
62、不同路径(中等)
一个机器人位于一个 m x n 网格的左上角,机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角.问总共有多少条不同的路径?
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 向右 向下
- 向右 向下 向右
- 向下 向右 向右
输入: m = 7, n = 3
输出: 28
class Solution {
public:
int uniquePaths(int m, int n) {
if(m<=0||n<=0) return 0;
return NumPaths(0,0,m,n);
}
int NumPaths(int i, int j, int m, int n){
if(i==m-1&&j==n-1) return 1;
if(i==m-1) return NumPaths(i,j+1,m,n);
else if(j==n-1) return NumPaths(i+1,j,m,n);
else return NumPaths(i+1,j,m,n) + NumPaths(i,j+1,m,n);
}
};
class Solution {
public:
int uniquePaths(int m, int n) {
if(m==0||n==0) return 0;
int bp[n];
for(int i=0; i<n; ++i){
bp[i] = 1;
}
for(int i=1; i<m; ++i){
for(int j=1; j<n; ++j){
bp[j] = bp[j-1] + bp[j];
}
}
return bp[n-1];
}
};
思路:递归的方法好懂但复杂度特别高,故将递归转换为简单的动态规划,f(i,j)表示走到坐标(i,j)处的路径数,故f(i,j)=f(i-1,j)+f(i,j-1); 可以建立动态规划矩阵,但实际上我们只会利用矩阵左侧和上侧的信息,故只需要一行数组来保存上一行的结果即可;根据递推公式进行累计计算;
66、加一(简单)
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
if(digits.empty()) return {};
for(int i=digits.size()-1; i>=0; --i){
if(digits[i]==9){
digits[i] = 0;
if(i==0){
vector<int> re(digits.size()+1,0);
re.front() = 1;
return re;
}
}
else{
digits[i] += 1;
break;
}
}
vector<int> re(digits.begin(),digits.end());
return re;
}
};
思路:比较简单,不用化为数学形式做,直接分为两种情况进位即可,从后往前遍历,首先如果当前位不是9,直接加1返回就行了;如果当前位是9,则把它变成0就好;如果是[9,9,9]的情况,则需要每位的9都变成0,然后在前面再添加一位1;
69、x的平方根(中等)
实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。
class Solution {
public:
int mySqrt(int x) {
if(x<0) return -1;
if(x==0) return 0;
int left = 0, right = x/2 + 1;
int mid;
long int squa;
while(left<right){
mid = (left + right) / 2 + 1;
squa = mid; squa *= squa;
if(squa==x) return mid;
if(squa<x) left = mid;
if(squa>x) right = mid - 1;
}
return left;
}
};
class Solution {
public:
int mySqrt(int a) {
if(a<0) return -1;
if(a==0) return 0;
double x = 1;
double x0 = 0;
while(abs(x-x0)>1e-6){
x0 = x;
x = 0.5 * (x0 + a/x0);
}
return int(x);
}
};
思路:两种方法:其一是二分查找法,对0-x中的值进行二分查找,其中二分的时候由于取整数部分的特殊操作,需要在二分的时候进行特殊处理,具体见代码注释;其次还有一个牛顿法,也可以用迭代的方法解方程组:x2-a=0,此处a是程序输入,x是最终结果;牛顿法简单推导:定义f(x)=x2-a=0,即求解x的方程组,方程左侧泰勒展开f(x)=f(x0)+f’(x0)(x-x0)=0.令其为零,则x=x0-f(x0)/f’(x0),不停的用新的x去代替x0直至x和x0的差距很小。
70、爬楼梯(简单)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
class Solution {
public:
int climbStairs(int n) {
if(n==0) return 0;
if(n==1) return 1;
if(n==2) return 2;
int num0 = 1, num1 = 2;
int result = 0;
for(int i=3; i<=n; ++i){
result = num0 + num1;
(num0 < num1 ? num0 : num1) = result;
}
return result;
}
};
思路:与剑指offer的青蛙跳台阶相同,f(n)=f(n-1)+f(n-2),斐波那契数列,用动态规划从小往大循环递增即可;
73、矩阵置零(中等)
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
输入:
[1,1,1],
[1,0,1],
[1,1,1]
输出:
[1,0,1],
[0,0,0],
[1,0,1]
输入:
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
输出:
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
if(matrix.empty()) return;
vector<int>::size_type m = matrix.size();
vector<int>::size_type n = matrix[0].size();
set<size_t> list_row, list_col;
for(size_t i=0; i<m; ++i){
for(size_t j=0; j<n; ++j){
if(matrix[i][j]==0){
list_row.insert(i);
list_col.insert(j);
}
}
}
for(auto iter=list_row.begin(); iter!=list_row.end(); ++iter){
for(size_t i=0; i<n; ++i){
matrix[*iter][i] = 0;
}
}
for(auto iter=list_col.begin(); iter!=list_col.end(); ++iter){
for(size_t i=0; i<m; ++i){
matrix[i][*iter] = 0;
}
}
}
};
思路:常规题,没有特别好的办法,具体思路是利用两个set哈希表,编译一次数组,找到存在0的行号和列号,分别存入set,(set的好处是可以剔除重复的键值,置零的时候不用重复置零,比如坐标(1,2)和(1,3)都是0时,记录行号1和列号2,3;然后根据两个set的值进行置零即可;
75、颜色分类(中等)
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:不能使用代码库中的排序函数来解决这道题。
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
class Solution {
public:
void sortColors(vector<int>& nums) {
if(nums.empty()) return;
vector<int>::size_type size = nums.size();
vector<int>::size_type left = 0, right = size - 1;
for(size_t i=0; i<=right&&right>0; ++i){
if(nums[i]==0){
swap(nums[left],nums[i]);
++left;
}
else if(nums[i]==2){
swap(nums[right],nums[i]);
--right;
--i;
}
}
}
};
思路:典型的荷兰国旗问题,三指针法可以一次遍历解决问题,并且不使用额外的内存空间;首先定义left和right表示排序后前面的0和后面的2的边界处,然后定义一个指针从头遍历数组,如果遇到0,则将其和现在的left处的值交换,把0换到前面去,然后left指针向右移动一位,注意此处换过来的数一定是1,所以不用再重新判断当前换过来的节点;如果遇到1,则忽视,继续遍历;如果遇到2,则将其和right处的值交换,把2换到右边,然后right指针向左移动一位,由于不知道原先right处的值是多少,故需要–i,重新判断当前缓过来的这个节点;另外还有一个需要注意的是如果定义了size_type或size_t,一定注意其不能为负,在自减操作时必须留意不能为0;
76、最小覆盖子串(困难)
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
public:
string minWindow(string s, string t) {
int start = 0, minLen = INT_MAX;
if(t.empty()||s.empty()||s.size()<t.size()) return "";
unordered_map<char,int> letter_t;
unordered_map<char,int> letter_w;
for(auto c : t){
++letter_t[c];
}
int left = 0, right = 0;
int num_matched = 0;
while(right<s.size()){
char c = s[right];
if(letter_t.find(c)!=letter_t.end()){
++letter_w[c];
if(letter_t[c]==letter_w[c]){
++num_matched;
}
}
while(num_matched==letter_t.size()){
if(right-left+1<minLen){
minLen = right - left + 1;
start = left;
}
char c2 = s[left];
if(letter_t.find(c2)!=letter_t.end()){
if(--letter_w[c2]<letter_t[c2]){
--num_matched;
}
}
++left;
}
++right;
}
return minLen<INT_MAX ? s.substr(start,minLen) : "";
}
};
思路:稍微复杂的题目;可以利用滑动窗口,两个指针left,right交叉右移;两个哈希表,一个letter_t记录t中出现的字符和出现的次数,一个letter_w记录滑动窗口中出现的字符和出现次数;首先right右移,直到满足窗口中的字符满足匹配条件(哈希表letter_w中的字符的次数都大于等于letter_t中的字符的个数,该条件可用一个num_matched判断,其表示当前窗口中已经匹配上的字符的数量,如果等于了letter_t元素的数目,就是匹配上了),然后让left指针右移收缩,收缩同时更新满足条件的滑动窗口的最短长度和起始下标,直到不满足匹配条件时停止;然后继续right右移。。。。算法相当于先遍历一遍t,然后使左右指针从左到右遍历一遍s,复杂度为O(len_t+2len_s);
78、子集(中等)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
class Solution {
vector<vector<int> > result;
public:
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
if(nums.empty()) return result;
vector<int> tmp;
FindSubVec(nums, 0, nums.size(), tmp);
return result;
}
void FindSubVec(vector<int>& nums, int left, int size, vector<int>& tmp){
if(left==size){
result.push_back(tmp);
return;
}
tmp.push_back(nums[left]);
FindSubVec(nums, left+1, size, tmp);
tmp.pop_back();
FindSubVec(nums, left+1, size, tmp);
}
};
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int> > result(1);
for(int i=0; i<nums.size(); ++i){
int size = result.size();
for(int j=0; j<size; ++j){
vector<int> tmp = result[j];
tmp.push_back(nums[i]);
result.push_back(tmp);
}
}
return result;
}
};
思路:典型的求全组合问题,递归深度优先遍历求解是最常用的做法;第二种循环法的思想比较有趣,从空vector算起,每次给这些组合添加一个元素,添加后的组合数量应该为之前的两倍,具体思路见图:
79、单次搜索(中等)
给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
board =
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false
提示:
board 和 word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if(word.empty()) return true;
if(board.empty()) return false;
int row = board.size();
int col = board[0].size();
int list[row*col];
memset(list, 0, sizeof(list));
bool result = false;
for(int i=0; i<row; ++i){
for(int j=0; j<col; ++j){
if(board[i][j]==word.front()){
result = result || FindPath(board, word, i, j, 0, row, col, list);
}
}
}
return result;
}
bool FindPath(vector<vector<char> >& board, string& word, int i, int j, int num, int row, int col, int* list){
if(i<0||j<0||i>=row||j>=col||list[i*col+j]!=0||board[i][j]!=word[num]) return false;
else if(num==word.size()-1) return true;
else{
list[i*col+j] = 1;
bool restmp = FindPath(board, word, i-1, j, num+1, row, col, list)
|| FindPath(board, word, i+1, j, num+1, row, col, list)
|| FindPath(board, word, i, j-1, num+1, row, col, list)
|| FindPath(board, word, i, j+1, num+1, row, col, list);
list[i*col+j] = 0;
return restmp;
}
}
};
思路:典型的回溯法找路径问题,代码量稍微大一些,思路比较清楚,看代码注释即可;
84、柱状图中最大的矩形(困难)
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
输入: [2,1,5,6,2,3]
输出: 10
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
if(heights.empty()) return 0;
heights.push_back(0);
stack<int> stk;
int result = 0;
for(int i=0; i<heights.size(); ++i){
while(!stk.empty() && heights[i]<heights[stk.top()]){
int index = stk.top();
stk.pop();
result = max(result, heights[index] * (stk.empty()?(i):(i-stk.top()-1)) );
}
stk.push(i);
}
return result;
}
};
思路:一言难尽的一道题,跟接雨水那道题看似差不多,但完全不同,用到了单调递增栈来解决问题;代码思路很奇葩,遍历整个数组,元素入栈时如果栈顶元素更大,则一直出栈,直到新入栈的元素可以满足单调栈的规则;出栈的过程中计算每个出栈元素的覆盖到它的最大矩型的面积,其面积等于(当前遍历元素下标左侧)和(出栈元素的左侧第一个小于它的元素的右边界)之间的部分,还要考虑出栈元素左侧没有比他更小的数的情况(出栈后栈为空了)。但具体的求面积的思路很难讲,可以用测试用例带入然后实际感受一下;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)