总的链接 :
面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台
200 . 岛屿数量
链接 :
总的思路 :
遍历 ,每次遍历到1的时候且之前没有访问过的结点 (这个用一个标记数组tag实现) 采用dfs / bfs对其周围的所有为1的结点tag[i][j]全部标记为true ;
BFS
class Solution {
public:
int dx[4] = {-1 , 0 , 1 , 0} ;
int dy[4] = {0 , -1 , 0 , 1} ;
int n , m ;
void bfs(int x , int y , vector<vector<char>>& grid , vector<vector<bool>>& tag){
queue<pair<int,int>> que ;
que.push({x,y}) ;
tag[x][y] = true ;
while(!que.empty()){
auto tmp = que.front() ;
que.pop() ;
int tx = tmp.first , ty = tmp.second ;
for(int i=0;i<4;i++){
int ntx = tx + dx[i] , nty = ty + dy[i] ;
if(ntx>=0 && ntx <n && nty>=0 && nty <m){
if(!tag[ntx][nty] && grid[ntx][nty] == '1'){
que.push({ntx,nty});
tag[ntx][nty] = true ;
}
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
n = grid.size() ;
m = grid[0].size() ;
int ans = 0 ;
vector<vector<bool>> tag = vector<vector<bool>>(n, vector<bool>(m, false));// 状态标记数组
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!tag[i][j] && grid[i][j] == '1'){
ans ++ ;
bfs(i , j , grid , tag) ;
}
}
}
return ans ;
}
};
DFS
class Solution {
public:
int dx[4] = {-1 , 0 , 1 , 0} ;
int dy[4] = {0 , -1 , 0 , 1} ;
int n , m ;
void dfs(int x , int y , vector<vector<char>>& grid , vector<vector<bool>>& tag){
for(int i=0;i<4;i++){
int ntx = x + dx[i] , nty = y + dy[i] ;
if(ntx>=0 && ntx <n && nty>=0 && nty <m){
if(!tag[ntx][nty] && grid[ntx][nty] == '1'){
tag[ntx][nty] = true ;
dfs(ntx,nty,grid,tag);
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
n = grid.size() ;
m = grid[0].size() ;
int ans = 0 ;
vector<vector<bool>> tag = vector<vector<bool>>(n, vector<bool>(m, false));// 状态标记数组
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!tag[i][j] && grid[i][j] == '1'){
ans ++ ;
dfs(i , j , grid , tag) ;
}
}
}
return ans ;
}
};
130 . 被围绕的区域 :
链接 :
思路 :
从地图周边出发,将周边空格相邻的'O'都做上标记,然后在遍历一遍地图,遇到 'O' 且没做过标记的,那么都是地图中间的'O',全部改成'X'就行。
这里有个小技巧 : 可以将遍历到的'0'位置改为其它特殊字符,比如A ;
class Solution {
private:
int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1}; // 保存四个方向
void dfs(vector<vector<char>>& board, int x, int y) {
board[x][y] = 'A';
for (int i = 0; i < 4; i++) { // 向四个方向遍历
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
// 超过边界
if (nextx < 0 || nextx >= board.size() || nexty < 0 || nexty >= board[0].size()) continue;
// 不符合条件,不继续遍历
if (board[nextx][nexty] == 'X' || board[nextx][nexty] == 'A') continue;
dfs (board, nextx, nexty);
}
return;
}
public:
void solve(vector<vector<char>>& board) {
int n = board.size(), m = board[0].size();
// 步骤一:
// 从左侧边,和右侧边 向中间遍历
for (int i = 0; i < n; i++) {
if (board[i][0] == 'O') dfs(board, i, 0);
if (board[i][m - 1] == 'O') dfs(board, i, m - 1);
}
// 从上边和下边 向中间遍历
for (int j = 0; j < m; j++) {
if (board[0][j] == 'O') dfs(board, 0, j);
if (board[n - 1][j] == 'O') dfs(board, n - 1, j);
}
// 步骤二:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == 'O') board[i][j] = 'X';
if (board[i][j] == 'A') board[i][j] = 'O';
}
}
}
};
133 . 克隆图
就是实现深拷贝 , 用递归实现 :
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
unordered_map<Node*,Node*> mp ;
Node* cloneGraph(Node* node) {
if(node==nullptr) return node ;
// 如果该结点已经访问过了,则直接从哈希表中取出对应的克隆结点返回
if(mp.find(node)!=mp.end()){
return mp[node] ;
}
// 克隆结点
Node* clnode = new Node(node->val) ;
mp[node] = clnode ;
for(auto& neighbor : node->neighbors){
clnode->neighbors.push_back(cloneGraph(neighbor));
}
return clnode ;
}
};
399 . 除法求值 :
链接 :
带权并查集 :
详细leetcode题解地址 :
代码 :
class Solution {
public:
int p[30];
double w[30] ;
int find(int x){
if(p[x]!=x){
int fa = find(p[x]) ;
w[x] = w[x] * w[p[x]] ;
p[x] = fa ;
}
return p[x] ;
}
void merge(int x,int y,double val){// 合并集合
int fx = find(x) , fy = find(y) ;
p[fx] = fy ;
w[fx] = val * 1.0 * w[y] / w[x] ;
}
// 构建有向图 , e 和 v 可以表示成一个图
// e中出现的变量就是图的顶点 , 分子和分母 可以表示成一个有向关系(形成一个带权有向图)
vector<double> calcEquation(vector<vector<string>>& e, vector<double>& v, vector<vector<string>>& qt) {
// e v q
// 可以将e中两个变量所在的集合进行[合并]
// 同在一个集合中的两个变量就可以通过某种方式计算出它们的比值 ;
// 具体的说可以将不同的变量的比值转换成相同变量的比值,
// 如果两个变量没有在同一个集合中 / 一个变量没有出现在任何集合中,直接返回-1.0
// 利用并查集 , 对于每一个结点x来说,除了维护父节点p[x]之外,还要维护其权值w,
// 其中[权值]w 指 : 结点 x 与 父亲p[x] 的 比值w[x] = v[x] / v[p[x]]
// 当查询结点 x 父亲时 ,如果 p[x]!=x , 先找到p[x]的祖宗f,将p[x]更新为f ;
// 也就是 将w[x] 跟新为 w[x] * w[f[x]] ;
// w[x] = v[x]/v[f] = v[x]/v[p[x]]*v[p[x]]/v[f] = w[x]*w[p[x]] ;
// 合并两个结点x,y时,我们需要先找到两者的父亲fx,fy,将p[fx]跟新为fy;
// v[x]/v[y]=k , 则 w[fx] = k * w[y] / w[x]
int n = e.size() ;
unordered_map<string , int> mp ;
int s = 0 ; // 种类数量
for(int i=0;i<n;i++){
if(mp.find(e[i][0]) == mp.end()){
mp[e[i][0]] = s++ ;
}
if(mp.find(e[i][1]) == mp.end()){
mp[e[i][1]] = s++ ;
}
}
for(int i=0;i<s;i++) p[i] = i , w[i] = 1.0 ;
for(int i=0;i<n;i++){
int va = mp[e[i][0]] , vb = mp[e[i][1]] ;
// 合并
merge(va,vb,v[i]);
}
vector<double> ans ;
for(const auto& q : qt){
double res = -1.0 ;
if(mp.find(q[0])!=mp.end() && mp.find(q[1])!=mp.end()){
int na = mp[q[0]] , nb = mp[q[1]] ;
int fa = find(na) , fb = find(nb) ;
if(fa == fb){
res = w[na] / w[nb] ;
}
// else{
// res = -1.0 ;
// }
}
ans.push_back(res) ;
}
return ans ;
}
};
暴力BFS :
这个就是暴力 , 找到每一种可能就行了 ;
class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
int nvars = 0;
unordered_map<string, int> variables;
int n = equations.size();
for (int i = 0; i < n; i++) {
if (variables.find(equations[i][0]) == variables.end()) {
variables[equations[i][0]] = nvars++;
}
if (variables.find(equations[i][1]) == variables.end()) {
variables[equations[i][1]] = nvars++;
}
}
// 对于每个点,存储其直接连接到的所有点及对应的权值
vector<vector<pair<int, double>>> edges(nvars);
for (int i = 0; i < n; i++) {
int va = variables[equations[i][0]], vb = variables[equations[i][1]];
edges[va].push_back(make_pair(vb, values[i]));
edges[vb].push_back(make_pair(va, 1.0 / values[i]));
}
vector<double> ret;
for (const auto& q: queries) {
double result = -1.0;
if (variables.find(q[0]) != variables.end() && variables.find(q[1]) != variables.end()) {
int ia = variables[q[0]], ib = variables[q[1]];
if (ia == ib) {
result = 1.0;
} else {
queue<int> points;
points.push(ia);
vector<double> ratios(nvars, -1.0);
ratios[ia] = 1.0;
while (!points.empty() && ratios[ib] < 0) {
int x = points.front();
points.pop();
for (const auto [y, val]: edges[x]) {
if (ratios[y] < 0) {
ratios[y] = ratios[x] * val;
points.push(y);
}
}
}
result = ratios[ib];
}
}
ret.push_back(result);
}
return ret;
}
};
207 . 课程表
拓扑序模板题 , 我们只需要验证是否存在拓扑序即可 ;
使用dfs / bfs都可以 ;
class Solution {
private:
// 存储有向图
vector<vector<int>> edges;
// 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
vector<int> visited;
// 判断有向图中是否有环
bool valid = true;
public:
void dfs(int u) {
visited[u] = 1;
for (int v: edges[u]) {
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
}
else if (visited[v] == 1) {
valid = false;
return;
}
}
visited[u] = 2;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
visited.resize(numCourses);
for (const auto& info: prerequisites) {
edges[info[1]].push_back(info[0]);
}
for (int i = 0; i < numCourses && valid; ++i) {
if (!visited[i]) {
dfs(i);
}
}
return valid;
}
};
210 . 课程表II
链接 :
和上题一样 :
class Solution {
private:
// 存储有向图
vector<vector<int>> edges;
// 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
vector<int> visited;
// 用数组来模拟栈,下标 0 为栈底,n-1 为栈顶
vector<int> result;
// 判断有向图中是否有环
bool valid = true;
public:
void dfs(int u) {
// 将节点标记为「搜索中」
visited[u] = 1;
// 搜索其相邻节点
// 只要发现有环,立刻停止搜索
for (int v: edges[u]) {
// 如果「未搜索」那么搜索相邻节点
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
}
// 如果「搜索中」说明找到了环
else if (visited[v] == 1) {
valid = false;
return;
}
}
// 将节点标记为「已完成」
visited[u] = 2;
// 将节点入栈
result.push_back(u);
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
visited.resize(numCourses);
for (const auto& info: prerequisites) {
edges[info[1]].push_back(info[0]);
}
// 每次挑选一个「未搜索」的节点,开始进行深度优先搜索
for (int i = 0; i < numCourses && valid; ++i) {
if (!visited[i]) {
dfs(i);
}
}
if (!valid) {
return {};
}
// 如果没有环,那么就有拓扑排序
// 注意下标 0 为栈底,因此需要将数组反序输出
reverse(result.begin(), result.end());
return result;
}
};