前言
暑假不能再摆烂力,分享一下最近刷力扣题目的一些感悟。
一、力扣874. 模拟行走机器人
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
}
};
这道题目我第一时间想到的是创建一个二维数组表示地图然后逐个逐个判断,但是看了一下数据大小,发现内存必然超限,然后想把障碍物的数据用unordered_map<int,int>hash来储存,然后遍历commands每一个数据,遇到障碍物就相当于机器人的x和y组成的vector数组在哈希表里出现过,便回退一次,然后记录一下最大值,可是无法通过测试案例(内存超限)。
后来查看题解发现题解是用了一个unordered_set<int>来储存数据,具体做法是将x*60010+y储存起来,这样能保证每个障碍点的数据不同得以一一匹配。
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
int x_dir=0,y_dir=1,pos_x=0,pos_y=0;
int maxway=0;
unordered_set<int>hash_set;
for (auto obstacle: obstacles) {
hash_set.emplace(obstacle[0] * 60010 + obstacle[1]);
}
for(int i=0;i<commands.size();i++){
if(commands[i]<0){
if(x_dir==0&&y_dir==1){
if(commands[i]==-1){x_dir=1,y_dir=0;continue;}
if(commands[i]==-2){x_dir=-1,y_dir=0;continue;}
}
if(x_dir==1&&y_dir==0){
if(commands[i]==-1){x_dir=0,y_dir=-1;continue;}
if(commands[i]==-2){x_dir=0,y_dir=1;continue;}
}
if(x_dir==0&&y_dir==-1){
if(commands[i]==-1){x_dir=-1,y_dir=0;continue;}
if(commands[i]==-2){x_dir=1,y_dir=0;continue;}
}
if(x_dir==-1&&y_dir==0){
if(commands[i]==-1){x_dir=0,y_dir=1;continue;}
if(commands[i]==-2){x_dir=0,y_dir=-1;continue;}
}
}
else{
while(commands[i]>0){
pos_x+=x_dir;pos_y+=y_dir;
if(hash_set.find(pos_x*60010+pos_y)!=hash_set.end()){pos_x-=x_dir;pos_y-=y_dir;break;}
commands[i]--;
}
maxway=max(maxway,abs(pos_x)*abs(pos_x)+abs(pos_y)*abs(pos_y));
}
}
return maxway;
}
};
(注意:unordered_set无法哈希vector,我一开始还想用unordered_set<vector<iint>>来储存数据,这里就是将一个数据放大达到目的)
二、力扣 93. 复原 IP 地址
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。
给定一个只包含数字的字符串 s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s
中插入 '.'
来形成。你 不能 重新排序或删除 s
中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"]
示例 2:
输入:s = "0000" 输出:["0.0.0.0"]
示例 3:
输入:s = "101023" 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
提示:
1 <= s.length <= 20
s
仅由数字组成
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
}
};
这道题我一开始没想到用回溯来解决,因为数据量并不大还想着通过操作字符串的手段来解决,后来才发现可以用回溯算法。
这个题目就相当于把三个'.'塞入字符串里面然后判断每一段字符串是否符合要求(注意:以'0'开头的字符串也是不符合要求的)
这个题目不用去重啥的,因为数据量比较小剪枝也就随便剪了,主要是回溯算法的这个思想以后昨做题不能再忘记了。
class Solution {
private:
vector<string> result;// 记录结果,最后返回result
// startIndex: 搜索的起始位置,pointNum:添加逗点的数量
void backtracking(string& s, int startIndex, int pointNum) {
if (pointNum == 3) { // 逗点数量为3时,分隔结束
// 判断第四段子字符串是否合法,如果合法就放进result中
if (isValid(s, startIndex, s.size() - 1)) {
result.push_back(s);
}
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗点之后下一个子串的起始位置为i+2
pointNum--;
s.erase(s.begin() + i + 1);
} else break; // 不合法,直接结束本层循环
}
}
// 判断字符串s区间[start, end]所组成的数字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;
}
if (s[start] == '0' && start != end) { // 0开头的数字不合法
return false;
}
int num = 0;
for (int i = start; i <= end; i++) {
if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法
return false;
}
num = num * 10 + (s[i] - '0');
if (num > 255) { // 如果大于255了不合法
return false;
}
}
return true;
}
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
backtracking(s, 0, 0);
return result;
}
};
可能是没学多久的原因吧,对于回溯算法的题目不能立马反应过来
三、力扣 51 N皇后问题
51. N 皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n
个皇后放置在 n×n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
输入:n = 4 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] 解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1 输出:[["Q"]]
提示:
1 <= n <= 9
实在搞不懂这种经典的bfs力扣能给个困难难度,说白了也就是一个剪枝比较复杂的回溯算法,可能之前接触过的原因,没觉得太难(ps:这是回溯给我的最后一点体面力)。
具体的解题思路就是利用回溯的方法暴力得得出每行每一个皇后的可能的位置,剪枝方法就是在每放置一个棋子前判断该位置是否会与之前放置过的棋子水平,竖直,斜向相交,那么之前放置的棋子的数据就要存储在一张表里面,直到判断至最后一行时便返回寻求其他位置。
class Solution {
private:
vector<vector<string>>result;//最终结果
vector<string>elem;
vector<vector<int>>check_map;//储存之前棋子的位置
//一个check函数判断当下位置是否符合要求
bool check(int x,int y){
for(int i=0;i<check_map.size();i++){
if(check_map[i][0]==x){return false;}//不能竖直相交
if(x+check_map[i][1]==y+check_map[i][0]){return false;}//斜向相交,斜率等于1
if(x+y==check_map[i][0]+check_map[i][1]){return false;}//斜向相交,斜率等于-1
}//不用判断是否水平相交,因为一行只会有一个棋子
return true;
}
//接下来便是回溯每一种情况了
void backtracking(int n){
if(elem.size()==n){result.push_back(elem);return;}//当到达最后一层棋格后边返回。也是终止条件
for(int i=0;i<n;i++){
//检查接下来的位置是否符合条件
if(check(i,elem.size())){
string str="";
for(int t=0;t<n;t++){str+=".";}
str[i]='Q';
elem.push_back(str);
vector<int>v;
v.push_back(i);v.push_back(elem.size()-1);
check_map.push_back(v);
backtracking(n);
elem.pop_back();
check_map.pop_back();
}
}
}
public:
vector<vector<string>> solveNQueens(int n) {
backtracking(n);
return result;
}
};
这种比较经典的bfs应该要记录一下,回溯的这种思想以后要多加练习。
总结:
以后在对于哈希表使用时内存超限或者题目有限制时应该想到上面这种放大的方法,将一个数据放大再加上另外一个数据以达到管理储存不同数据的效果。