// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
// 记录被遍历过的节点
vector<bool> visited;
// 记录从起点到当前节点的路径
vector<bool> onPath;
/* 图遍历框架 */
void traverse(Graph graph, int s) {
if (visited[s]) return;
// 经过节点 s,标记为已遍历
visited[s] = true;
// 做选择:标记节点 s 在路径上
onPath[s] = true;
for (int neighbor : graph.neighbors(s)) {
traverse(graph, neighbor);
}
// 撤销选择:节点 s 离开路径
onPath[s] = false;
}
题目来源:133. 克隆图
题解:
/*
// Definition for a Node.
class Node {
public:
int val;
vector neighbors;
Node() {
val = 0;
neighbors = vector();
}
Node(int _val) {
val = _val;
neighbors = vector();
}
Node(int _val, vector _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
unordered_map<Node*, Node*> visited;
Node* cloneGraph(Node* node) {
if(node==nullptr) return node;
if(visited.find(node)!=visited.end()){
return visited[node];
}
Node* clonenode=new Node(node->val);
visited[node]=clonenode;
for(auto& neighbor:node->neighbors)
{
clonenode->neighbors.emplace_back(cloneGraph(neighbor));
}
return clonenode;
}
};
题目来源:1443. 收集树上所有苹果的最少时间
题解:
class Solution {
public:
//g 图
//vis 判断节点是否被访问过,遍历过的节点不能再遍历,
//同时因为是无向图,vis间接的确定了父子节点的关系,在子节点访问其子节点的时候不可能再去访问父节点
//cost 只有以0节点刚进去的时候cost=0,在之后访问0节点的子节点时,cost都等于2(来回)
int dfs(int curn, vector<vector<int> >& g, vector<bool>& vis, vector<bool>& hasApple, int cost)
{
if(vis[curn])
return 0;
vis[curn]=true;
int childcost=0;
for(auto next:g[curn])
{
childcost+=dfs(next,g,vis,hasApple,2);//遍历当前节点的所有子节点
//如果childcost=0的话代表所有的子节点都没有苹果
}
//对应上面的情况1,所有子节点里都无苹果,且该节点本身也无苹果,走到该节点的开销=0
if(!hasApple[curn] && childcost==0)
return 0;
//对应上面的情况2,3 走到该节点的开销为所有摘子节点里的苹果的开销+走到该节点的开销cost
//如果childcost=0的话,对应情况3
//如果childcost!=0的话,对应情况2
return childcost+cost;
}
int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
vector<vector<int>> g(n);
vector<bool> vis(n,false);
for(auto edge:edges)
{
g[edge[0]].push_back(edge[1]);
g[edge[1]].push_back(edge[0]);
}
return dfs(0,g,vis,hasApple,0);//第一层由0节点出发,开销是0
}
};
题解:
// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
class Solution {
public:
// 记录一次递归堆栈中的节点
vector<bool> onPath;
// 记录遍历过的节点,防止走回头路
vector<bool> visited;
// 记录图中是否有环
bool hasCycle = false;
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph = buildGraph(numCourses, prerequisites);
visited = vector<bool>(numCourses);
onPath = vector<bool>(numCourses);
for (int i = 0; i < numCourses; i++) {
// 遍历图中的所有节点
traverse(graph, i);
}
// 只要没有循环依赖可以完成所有课程
return !hasCycle;
}
void traverse(vector<vector<int>>& graph, int s) {
if (onPath[s]) {
// 出现环
hasCycle = true;
}
if (visited[s] || hasCycle) {
// 如果已经找到了环,也不用再遍历了
return;
}
// 前序代码位置
visited[s] = true;
onPath[s] = true;
for (int t : graph[s]) {
traverse(graph, t);
}
// 后序代码位置
onPath[s] = false;
}
vector<vector<int>> buildGraph(int numCourses, vector<vector<int>>& prerequisites) {
// 代码见前文
}
};
题解:
// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
#include
#include
using namespace std;
// 主函数
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
// 建图,有向边代表「被依赖」关系
vector<vector<int>> graph = buildGraph(numCourses, prerequisites);
// 构建入度数组
vector<int> indegree(numCourses, 0);
for (auto edge : prerequisites) {
int from = edge[1], to = edge[0];
// 节点 to 的入度加一
indegree[to]++;
}
// 根据入度初始化队列中的节点
queue<int> q;
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
// 节点 i 没有入度,即没有依赖的节点
// 可以作为拓扑排序的起点,加入队列
q.push(i);
}
}
// 记录遍历的节点个数
int count = 0;
// 开始执行 BFS 循环
while (!q.empty()) {
// 弹出节点 cur,并将它指向的节点的入度减一
int cur = q.front();
q.pop();
count++;
for (int next : graph[cur]) {
indegree[next]--;
if (indegree[next] == 0) {
// 如果入度变为 0,说明 next 依赖的节点都已被遍历
q.push(next);
}
}
}
// 如果所有节点都被遍历过,说明不成环
return count == numCourses;
}
// 建图函数
vector<vector<int>> buildGraph(int n, vector<vector<int>>& edges) {
// 见前文
}
题解:
// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
class Solution {
public:
// 记录后序遍历结果
vector<int> postorder;
// 记录是否存在环
bool hasCycle = false;
vector<bool> visited, onPath;
// 主函数
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph = buildGraph(numCourses, prerequisites);
visited = vector<bool>(numCourses, false);
onPath = vector<bool>(numCourses, false);
// 遍历图
for (int i = 0; i < numCourses; i++) {
traverse(graph, i);
}
// 有环图无法进行拓扑排序
if (hasCycle) {
return {};
}
// 逆后序遍历结果即为拓扑排序结果
reverse(postorder.begin(), postorder.end());
vector<int> res(numCourses);
for (int i = 0; i < numCourses; i++) {
res[i] = postorder[i];
}
return res;
}
// 图遍历函数
void traverse(vector<vector<int>>& graph, int s) {
if (onPath[s]) {
// 发现环
hasCycle = true;
}
if (visited[s] || hasCycle) {
return;
}
// 前序遍历位置
onPath[s] = true;
visited[s] = true;
for (int t : graph[s]) {
traverse(graph, t);
}
// 后序遍历位置
postorder.push_back(s);
onPath[s] = false;
}
// 建图函数
vector<vector<int>> buildGraph(int numCourses, vector<vector<int>>& prerequisites) {
// 代码见前文。
}
};
题解:
// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
// 建图,和环检测算法相同
vector<vector<int>> graph(numCourses);
for (auto edge : prerequisites) {
int from = edge[1], to = edge[0];
graph[from].push_back(to);
}
// 计算入度,和环检测算法相同
vector<int> indegree(numCourses, 0);
for (auto edge : prerequisites) {
int from = edge[1], to = edge[0];
indegree[to]++;
}
// 根据入度初始化队列中的节点,和环检测算法相同
queue<int> q;
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
// 记录拓扑排序结果
vector<int> res;
// 开始执行 BFS 算法
while (!q.empty()) {
int cur = q.front();
q.pop();
// 弹出节点的顺序即为拓扑排序结果
res.push_back(cur);
for (int next : graph[cur]) {
indegree[next]--;
if (indegree[next] == 0) {
q.push(next);
}
}
}
if (res.size() != numCourses) {
// 存在环,拓扑排序不存在
return vector<int>{};
}
return res;
}
// 建图函数
vector<vector<int>> buildGraph(int n, vector<vector<int>>& edges) {
// 见前文
}
题目来源:130.被围绕的区域
题解:
class Solution {
public:
int n, m;
void dfs(vector<vector<char>>& board, int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= m || board[x][y] != 'O') {
return;
}
board[x][y] = 'A';
dfs(board, x + 1, y);
dfs(board, x - 1, y);
dfs(board, x, y + 1);
dfs(board, x, y - 1);
}
void solve(vector<vector<char>>& board) {
n = board.size();
if (n == 0) {
return;
}
m = board[0].size();
for (int i = 0; i < n; i++) {
dfs(board, i, 0);
dfs(board, i, m - 1);
}
for (int i = 1; i < m - 1; i++) {
dfs(board, 0, i);
dfs(board, n - 1, i);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == 'A') {
board[i][j] = 'O';
} else if (board[i][j] == 'O') {
board[i][j] = 'X';
}
}
}
}
};
题目来源:验证二叉树
题解:
class Solution {
public:
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
vector<int> indeg(n);
for(int i=0;i<n;i++){
if(leftChild[i]!=-1)
{
indeg[leftChild[i]]++;
}
if(rightChild[i]!=-1){
indeg[rightChild[i]]++;
}
}
int root=-1;
for(int i=0;i<n;i++){
if(!indeg[i]){
root=i;
break;
}
}
if(root==-1)
return false;
vector<bool> visited(n,false);
queue<int> q;
visited[root]=true;
q.push(root);
while(!q.empty()){
int u=q.front();
q.pop();
if(leftChild[u]!=-1){
if(visited[leftChild[u]])
return false;
visited[leftChild[u]]=true;
q.push(leftChild[u]);
}
if(rightChild[u]!=-1){
if(visited[rightChild[u]])
return false;
visited[rightChild[u]]=true;
q.push(rightChild[u]);
}
}
int cnt=0;
for(int i=0;i<n;i++){
if(visited[i]==true)
cnt++;
}
if(cnt==n)
return true;
else
return false;
}
};
题目来源:200.岛屿数量
题解:
class Solution {
public:
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
vector<int> indeg(n);
for(int i=0;i<n;i++){
if(leftChild[i]!=-1)
{
indeg[leftChild[i]]++;
}
if(rightChild[i]!=-1){
indeg[rightChild[i]]++;
}
}
int root=-1;
for(int i=0;i<n;i++){
if(!indeg[i]){
root=i;
break;
}
}
if(root==-1)
return false;
vector<bool> visited(n,false);
queue<int> q;
visited[root]=true;
q.push(root);
while(!q.empty()){
int u=q.front();
q.pop();
if(leftChild[u]!=-1){
if(visited[leftChild[u]])
return false;
visited[leftChild[u]]=true;
q.push(leftChild[u]);
}
if(rightChild[u]!=-1){
if(visited[rightChild[u]])
return false;
visited[rightChild[u]]=true;
q.push(rightChild[u]);
}
}
int cnt=0;
for(int i=0;i<n;i++){
if(visited[i]==true)
cnt++;
}
if(cnt==n)
return true;
else
return false;
}
};
题目来源:947. 移除最多的同行或同列石头
题解:
// 并查集特殊模板2
class UnionFind{
// 本题因为是按行列进行查找,优化可以直接采用哈希表
public:
// 哈希表映射关系[key,parent]
unordered_map<int,int> parent;
// 总共有多少不连通的并查集
int count = 0;
// 并查集查找首领节点操作
int Find(int index){
// 一开始构建并查集时候,假如key值不在并查集中则构建哈希表映射,count++
if(parent.find(index) == parent.end()){
parent[index] = index;
count++;
}
// 查找并查集的首领节点并优化
if(parent[index] != index)
parent[index] = Find(parent[index]);
return parent[index];
}
// 并查集合并操作
void Uniod(int index1,int index2){
int parent1 = Find(index1);
int parent2 = Find(index2);
// *这步骤很重要,直接把两节点首领一样的结果返回过滤,否则会让count多减1*
if(parent1 == parent2)
return;
parent[parent1] = parent2;
count--;
}
};
class Solution {
public:
// 思路:将石头行列的数值构建并查集,因此行或列需要加10001区分开
// 合并的意思所有横坐标为 x 的石头和所有纵坐标为 y 的石头都属于同一个并查集
// 最后返回石头数量 - 并查集的数量,就是题目要求的最多
int removeStones(vector<vector<int>>& stones) {
UnionFind uf;
for(auto& stone:stones){
uf.Uniod(stone[0] + 10001,stone[1]);
}
return stones.size() - uf.count;
}
};
题目来源:368. 最大整除子集
题解:
//法一:动态规划【没懂】
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
int len = nums.size();
sort(nums.begin(), nums.end());
// 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数
vector<int> dp(len, 1);
int maxSize = 1;
int maxVal = dp[0];
for (int i = 1; i < len; i++) {
for (int j = 0; j < i; j++) {
// 题目中说「没有重复元素」很重要
if (nums[i] % nums[j] == 0) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
if (dp[i] > maxSize) {
maxSize = dp[i];
maxVal = nums[i];
}
}
// 第 2 步:倒推获得最大子集
vector<int> res;
if (maxSize == 1) {
res.push_back(nums[0]);
return res;
}
for (int i = len - 1; i >= 0 && maxSize > 0; i--) {
if (dp[i] == maxSize && maxVal % nums[i] == 0) {
res.push_back(nums[i]);
maxVal = nums[i];
maxSize--;
}
}
return res;
}
};
//法二:并查集
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
int n = nums.size();
if(n == 0) return {};
vector<int> father(n), level(n);
sort(nums.begin(), nums.end());
for(int i = 0; i < n; ++i) father[i] = i; // 初始化父亲节点
for(int i = 0; i < n; ++i){
for(int j = i - 1; j >= 0; --j){
if(nums[i] % nums[j] == 0 && level[i] < =level[j]){
father[i] = j;
level[i] = level[j] + 1;
}
}
}
// 取得最深元素的角标
int x = max_element(level.begin(), level.end()) - level.begin();
vector<int> ans = {nums[x]};
while(x != father[x]){
x = father[x];
ans.push_back(nums[x]);
}
return ans; // 此处输出的数组元素是从大到小的顺序,不再排序也能过此题
// return {ans.rbegin(), ans.rend()}; // 按从小到大的顺序返回稍微慢一点
}
};
并查集难题:765.情侣牵手
//贪心算法题解
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
int n=row.size();
vector<int> index(n);
for(int i=0;i<n;i++)
index[row[i]]=i;
int count=0;
for(int i=0;i<n-1;i+=2){
int p1=row[i];
int p2=(p1&1)==0?p1+1:p1-1;
if(row[i+1]==p2) continue;
int p2index=index[p2];
swap(row,index,i+1,p2index);
count++;
}
return count;
}
void swap(vector<int>& row,vector<int>& index,int i,int j){
int temp=row[i];
row[i]=row[j];
row[j]=temp;
index[row[i]]=i;
index[row[j]]=j;
}
};
题目来源:1584. 连接所有点的最小费用
题解:
//方法一:Kruskal 算法
class DisjointSetUnion {
private:
vector<int> f, rank;
int n;
public:
DisjointSetUnion(int _n) {
n = _n;
rank.resize(n, 1);
f.resize(n);
for (int i = 0; i < n; i++) {
f[i] = i;
}
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
int unionSet(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) {
return false;
}
if (rank[fx] < rank[fy]) {
swap(fx, fy);
}
rank[fx] += rank[fy];
f[fy] = fx;
return true;
}
};
struct Edge {
int len, x, y;
Edge(int len, int x, int y) : len(len), x(x), y(y) {
}
};
class Solution {
public:
int minCostConnectPoints(vector<vector<int>>& points) {
auto dist = [&](int x, int y) -> int {
return abs(points[x][0] - points[y][0]) + abs(points[x][1] - points[y][1]);
};
int n = points.size();
DisjointSetUnion dsu(n);
vector<Edge> edges;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
edges.emplace_back(dist(i, j), i, j);
}
}
sort(edges.begin(), edges.end(), [](Edge a, Edge b) -> int { return a.len < b.len; });
int ret = 0, num = 1;
for (auto& [len, x, y] : edges) {
if (dsu.unionSet(x, y)) {
ret += len;
num++;
if (num == n) {
break;
}
}
}
return ret;
}
};
//方法二:Prim
class Solution {
public:
int dist(vector<int> a, vector<int> b)
{
return abs(a[0]-b[0])+abs(a[1]-b[1]);
}
int minCostConnectPoints(vector<vector<int>>& points) {
int n = points.size();
vector<bool> visited(n, false);
visited[0] = true;
vector<int> mindist(n, 0);
for(int i = 1; i < n; i++)
{
mindist[i] = dist(points[0], points[i]);
}
mindist[0] = 0;
int cost = 0;
for(int k = 0; k < n-1; k++)
{
int pos = 0;
int minn = INT_MAX;
for(int i = 0; i < n; i++)
{
if(!visited[i] && minn > mindist[i])
{
minn = mindist[i];
pos = i;
}
}
cost += minn;
visited[pos] = true;
mindist[pos] = 0;
for(int i = 0; i < n; i++)
{
if(!visited[i] && dist(points[pos], points[i]) < mindist[i])
mindist[i] = dist(points[pos], points[i]);
}
}
return cost;
}
};
题目来源:743. 网络延迟时间
题解:
class Solution {
public:
int networkDelayTime(vector<vector<int>> ×, int n, int k) {
int inf = INT_MAX/2;
vector<vector<int>> g(n, vector<int>(n, inf));
for (auto &t : times) {
int x = t[0] - 1, y = t[1] - 1;
g[x][y] = t[2];
}
vector<int> dist(n, inf);
dist[k - 1] = 0;
vector<int> used(n);
for (int i = 0; i < n; ++i) {
int mindist = inf;
int index=-1;
for (int y = 0; y < n; ++y) {
if (!used[y] && dist[y] < mindist) {
mindist=dist[y];
index=y;
}
}
if(index!=-1){
used[index] = true;
for (int y = 0; y < n; ++y) {
dist[y] = min(dist[y], dist[index] + g[index][y]);
}
}
}
int ans = *max_element(dist.begin(), dist.end());
return ans == inf ? -1 : ans;
}
};
//小根堆
class Solution {
public:
int networkDelayTime(vector<vector<int>> ×, int n, int k) {
const int inf = INT_MAX / 2;
vector<vector<pair<int, int>>> g(n);
for (auto &t : times) {
int x = t[0] - 1, y = t[1] - 1;
g[x].emplace_back(y, t[2]);
}
vector<int> dist(n, inf);
dist[k - 1] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
q.emplace(0, k - 1);
while (!q.empty()) {
auto p = q.top();
q.pop();
int time = p.first, x = p.second;
if (dist[x] < time) {
continue;
}
for (auto &e : g[x]) {
int y = e.first, d = dist[x] + e.second;
if (d < dist[y]) {
dist[y] = d;
q.emplace(d, y);
}
}
}
int ans = *max_element(dist.begin(), dist.end());
return ans == inf ? -1 : ans;
}
};
题目来源:1514. 概率最大的路径
题解:
class Solution {
public:
double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
vector<vector<pair<double, int>>> graph(n);
for (int i = 0; i < edges.size(); i++) {
auto& e = edges[i];
graph[e[0]].emplace_back(succProb[i], e[1]);
graph[e[1]].emplace_back(succProb[i], e[0]);
}
priority_queue<pair<double, int>> que;
vector<double> prob(n, 0);
que.emplace(1, start);
prob[start] = 1;
while (!que.empty()) {
auto [pr, node] = que.top();
que.pop();
if (pr < prob[node]) {
continue;
}
for (auto& [prNext, nodeNext] : graph[node]) {
if (prob[nodeNext] < prob[node] * prNext) {
prob[nodeNext] = prob[node] * prNext;
que.emplace(prob[nodeNext], nodeNext);
}
}
}
return prob[end];
}
};
题目来源:1631. 最小体力消耗路径
题解:
class Solution {
public:
struct State {
int x, y, effortFromStart;
bool operator>(const State &other) const {
return effortFromStart > other.effortFromStart;
}
};
// 计算从 (curX, curY) 出发可以到达哪些位置,返回“邻居”坐标列表
vector<vector<int>> adj(const vector<vector<int>>& heights, int curX, int curY) {
int m = heights.size(), n = heights[0].size();
vector<vector<int>> ret;
if (curX > 0) {
ret.push_back({curX - 1, curY});
}
if (curY > 0) {
ret.push_back({curX, curY - 1});
}
if (curX < m - 1) {
ret.push_back({curX + 1, curY});
}
if (curY < n - 1) {
ret.push_back({curX, curY + 1});
}
return ret;
}
int minimumEffortPath(vector<vector<int>>& heights) {
int m = heights.size(), n = heights[0].size();
// 定义:从 (0, 0) 到 (i, j) 的最小体力消耗是 effortTo[i][j]
vector<vector<int>> effortTo(m, vector<int>(n));
// dp table 初始化为正无穷
for (int i = 0; i < m; i++) {
fill(effortTo[i].begin(), effortTo[i].end(), INT_MAX);
}
// base case,起点到起点的最小消耗就是 0
effortTo[0][0] = 0;
// 优先级队列,effortFromStart 较小的排在前面
priority_queue<State, vector<State>, greater<State>> pq;
// 从起点 (0, 0) 开始进行 BFS
pq.emplace(State{0, 0, 0});
while (!pq.empty()) {
State curState = pq.top();
pq.pop();
int curX = curState.x;
int curY = curState.y;
int curEffortFromStart = curState.effortFromStart;
// 到达终点提前结束
if (curX == m - 1 && curY == n - 1) {
return curEffortFromStart;
}
if (curEffortFromStart > effortTo[curX][curY]) {
continue;
}
// 将 (curX, curY) 的相邻坐标装入队列
for (vector<int> &neighbor : adj(heights, curX, curY)) {
int nextX = neighbor[0];
int nextY = neighbor[1];
// 计算从 (curX, curY) 达到 (nextX, nextY) 的消耗
int effortToNextNode = max(
effortTo[curX][curY],
abs(heights[curX][curY] - heights[nextX][nextY])
);
// 更新 dp table
if (effortTo[nextX][nextY] > effortToNextNode) {
effortTo[nextX][nextY] = effortToNextNode;
pq.emplace(State{nextX, nextY, effortToNextNode});
}
}
}
// 正常情况不会达到这个 return
return -1;
}
};