「图」的两种表示方法,邻接表就是链表,邻接矩阵就是二维数组。
岛屿问题
200. 岛屿数量
输入:grid = [ [“1”,”1”,”1”,”1”,”0”], [“1”,”1”,”0”,”1”,”0”], [“1”,”1”,”0”,”0”,”0”], [“0”,”0”,”0”,”0”,”0”] ] 输出:1
运用深度优先搜索,当发现一个陆地1时,深度搜索周围所有的1,并将访问过的陆地1替换为2。
var numIslands = function(grid) {if(grid.length===0 || grid[0].length===0) return 0;let count=0;for(let i=0;i<grid.length;i++){for(let j=0;j<grid[0].length;j++){if(grid[i][j]==='1'){dfs(grid,i,j)count++;}}}return count;};var dfs = function(grid,i,j){if(!(i>=0 && j>=0 && i< grid.length && j< grid[0].length)) return;if(grid[i][j]!=='1') return;grid[i][j]='2'dfs(grid,i-1,j)dfs(grid,i+1,j)dfs(grid,i,j-1)dfs(grid,i,j+1)}
695. 岛屿的最大面积
/*** @param {number[][]} grid* @return {number}*/var maxAreaOfIsland = function(grid) {const m=grid.lengthconst n = grid[0].lengthlet result=0;for(let i =0;i<m;i++){for(let j =0;j<n;j++){if(grid[i][j]){const ans=dfs(i,j,grid)result=Math.max(ans,result)}}}function dfs(i,j,graph){if(!(i>=0 && j>=0 && i<graph.length && j<graph[0].length)) return 0 ;if(graph[i][j]!==1) return 0graph[i][j]=2return dfs(i-1,j,graph)+dfs(i+1,j,graph)+dfs(i,j-1,graph)+dfs(i,j+1,graph)+1//相加得到岛屿的面积,就像后序遍历求二叉树树高一样}return result;};
130. 被围绕的区域
/*** @param {character[][]} board* @return {void} Do not return anything, modify board in-place instead.*/var solve = function(board) {const n=board.length;const m=board[0].length; //m,n可能不相等for(let i=0;i<n;i++){dfs(i,0,board)dfs(i,m-1,board)}for(let j=1;j<m;j++){dfs(0,j,board)dfs(n-1,j,board)}for(let i=0;i<n;i++){for(let j=0;j<m;j++){if(board[i][j]==='A'){board[i][j]='O'}else if(board[i][j]==='O'){board[i][j]='X'}}}function dfs(i,j,graph){if(!(i>=0 && j>=0 && i<graph.length && j<graph[0].length)) return;if(graph[i][j]!=='O') returngraph[i][j]='A'dfs(i-1,j,graph)dfs(i+1,j,graph)dfs(i,j-1,graph)dfs(i,j+1,graph)}return board;};
图的遍历
其实就是多叉树的遍历,不过可能会有环,所以需要加vistied标记
797. 所有可能的路径
/*** @param {number[][]} graph* @return {number[][]}*/var allPathsSourceTarget = function(graph) {let result=[],path=[]function backtracking(cur,graph){path.push(cur) //与回溯的区别,把path.push放在了循环外,使根节点能被添加到path中if(cur===graph.length-1){result.push([...path])path.pop() //这里记得popreturn;}for(let i of graph[cur]){backtracking(i,graph)}path.pop()}backtracking(0,graph);return result;};
207. 课程表
/*** @param {number} numCourses* @param {number[][]} prerequisites* @return {boolean}*/var canFinish = function(numCourses, prerequisites) {//如果有环则无法完成//根据prerequisites生成邻接表,然后遍历看有没有坏function getAdjvex(prerequisites){let list=Array.from(Array(numCourses),()=>[])for(let [k,v] of prerequisites){list[v].push(k)}return list}let graph=getAdjvex(prerequisites);function backtracking(cur,graph){if(path.indexOf(cur)>-1){ //说明有环result=false;return;}if(visited[cur]) return;//例如a,b同时都指向了c,为了节约遍历,如果c之前已被访问过,说明c之后的节点已被递归处理,returnvisited[cur]=true;path.push(cur)for(let i of graph[cur]){backtracking(i,graph)}path.pop();}let result = true;let visited=Array(numCourses).fill(false); //已访问节点let path=[] //当前访问路径for(let i=0;i<numCourses;i++){//图中并不是所有节点都相连,所以要用一个 for 循环将所有节点都作为起点调用一次 DFS 搜索算法。backtracking(i,graph)}return result;};
210. 课程表 II
两种方法:1、DFS拓扑排序
2、BFS拓扑排序
//DFS拓扑排序,在课程表1的基础上,增加了学习顺序数组,运用后序遍历的reverse结果。var findOrder = function(numCourses, prerequisites) {function getAdjvex(prerequisites){let list=Array.from(Array(numCourses),()=>[])for(let [k,v] of prerequisites){list[v].push(k)}return list}let graph=getAdjvex(prerequisites);function backtracking(cur,graph){if(path.indexOf(cur)>-1){isCircle=true;return;}if(visited[cur]) return;visited[cur]=true;path.push(cur)for(let i of graph[cur]){backtracking(i,graph)}result.push(cur);// 学习顺序path.pop();}let isCircle = false;let visited=Array(numCourses).fill(false); //已访问节点let path=[] //当前访问路径let result=[]for(let i=0;i<numCourses;i++){backtracking(i,graph)}return isCircle?[]:result.reverse();};
//BFS拓扑排序//记录每个节点的入度//一层层往下,如果入度为0则加入结果集。
并查集
https://labuladong.gitee.io/algo/2/20/51/
并查集是来解决图的连通性问题
- Union — 连接两个节点
- Find — 查找所属的连通分量
所以,并查集主要就是实现以下接口:
class UF {/* 将 p 和 q 连接 */public void union(int p, int q);/* 判断 p 和 q 是否连通 */public boolean connected(int p, int q);/* 返回图中有多少个连通分量 */public int count();/* 返回当前节点的根节点 */private int find(int x);}
如何表示节点与节点之间的连通性关系呢??
- 如果 p 和 q 连通,则它们有相同的根节点
547. 省份数量
/*** @param {number[][]} isConnected* @return {number}*/var findCircleNum = function(isConnected) {const uf=new UF(isConnected.length)for(let i=0;i<isConnected.length;i++){// for(let j=0;j<isConnected[i].length;j++){for(let j=0;j<i;j++){ //因为对称性if(isConnected[i][j]){uf.join(i,j)}}}return uf.getCount();};class UF{constructor(count){this.count=count;this.parent=Array.from(Array(count),(x,i)=> i)}join(a,b){const rootA=this.find(a);const rootB=this.find(b);if(rootA ===rootB) returnthis.parent[rootA]=rootB;this.count--;}find(x){while(this.parent[x]!==x){this.parent[x]=this.parent[this.parent[x]]// 压缩层高x=this.parent[x]}return x;}getCount(){return this.count;}}
128. 最长连续序列
最先想到sort排序然后动态规划求最长连续序列,但是由于时间复杂度O(n)的限制,该方法pass
1、使用并查集,建立相邻数字之间的联系,然后得到连通量;但其实n+1/n-1已经是隐形的联系了,所以不用并查集方法了。
2、使用map记录右边界,用空间换时间
/*** @param {number[]} nums* @return {number}*/var longestConsecutive = function(nums) {//时间复杂度为 O(n)let map=new Map();let result=0;for(let num of nums){map.set(num,num)}for(let num of nums){if(!map.get(num-1)){ //当还有n-1时就不处理,因为处理到n-1时会包含到nlet right=numwhile(map.get(right+1)!==undefined){right=map.get(right+1)}map.set(num,right);result=Math.max(result,right-num+1);}}return result;};
