算法准备-5.29
1. 打家劫舍
-
描述:你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
-
思路:此题考查动态规划算法,只有一间屋子时,只能偷一间,有两间屋子时,偷金额较大的那间,当数量大于2时,是否偷第k间屋子的标准在于第k间屋子的金额加上前k-2间屋子的最大抢劫金额 比 前k-1间屋子的最大抢劫金额要大。
-
题解:
class Solution { public int rob(int[] nums) { if(nums==null||nums.length==0) { return 0; } else if(nums.length==1) { return nums[0]; } else if(nums.length==2) { return Math.max(nums[0],nums[1]); } int[]dp =new int[nums.length]; dp[0]=nums[0]; dp[1]=Math.max(nums[0],nums[1]); for(int i=2;i<nums.length;i++) { dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]); } return dp[nums.length-1]; } }
2. 找到最终的安全状态
-
描述:在有向图中, 我们从某个节点和每个转向处开始, 沿着图的有向边走。 如果我们到达的节点是终点 (即它没有连出的有向边), 我们停止。
现在, 如果我们最后能走到终点,那么我们的起始节点是最终安全的。 更具体地说, 存在一个自然数 K, 无论选择从哪里开始行走, 我们走了不到 K 步后必能停止在一个终点。
哪些节点最终是安全的? 结果返回一个有序的数组。
该有向图有 N 个节点,标签为 0, 1, …, N-1, 其中 N 是 graph 的节点数. 图以以下的形式给出: graph[i] 是节点 j 的一个列表,满足 (i, j) 是图的一条有向边。
-
思路:此题考查的算法是图的深度优先搜索。我们先将所有节点标记为白色,即状态1,此时所有节点都未曾访问。我们从任意结点开始,做深度优先搜索,每次搜索将节点标记为灰色,即状态2,表示已被访问或在环中,当遍历过程中遇见灰色节点时,则将此次遍历的所有节点保持为灰色;如果在遍历过程中没有遇见灰色节点,则我们回溯到这个节点的时候将其置为黑色节点,表示是一个安全的节点。
-
题解:
class Solution { public List<Integer> eventualSafeNodes(int[][] graph) { int length=graph.length; int[] color=new int[length]; List<Integer> ans=new ArrayList(); for(int i=0;i<length;++i) { if(dfs(i,color,graph)) { ans.add(i); } } return ans; } public boolean dfs(int i,int[] color,int[][] graph) { if(color[i]>0) { return color[i]==2; } color[i]=1; for(int link:graph[i]) { if(color[link]==1||!dfs(link,color,graph)) { return false; } } color[i]=2; return true; } }
-
注解:还有一种算法,即利用拓扑排序
拓扑排序
-
定义:
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个**操作称之为拓扑排序**。
-
步骤:
先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。 一直做改操作,直到所有的节点都被分离出来。 如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。
-
此题如何利用拓扑排序呢?
我们在此题中需要不能走到环的节点,首先那些出度为0的节点必然可以,删除这些节点并删除指向他们的边后,在此寻找出度为0的节点。我们构建一个反向图,改变所有有向边的指向,此时我们的算法过程即为拓扑排序,
-
题解:
class Solution { public List<Integer> eventualSafeNodes(int[][] graph) { int length=graph.length; boolean[] safe=new boolean[length]; List<Set<Integer>> g=new ArrayList(); List<Set<Integer>> rg=new ArrayList(); for(int i=0;i<length;i++) { g.add(new HashSet()); rg.add(new HashSet()); } Queue<Integer> queue=new LinkedList(); for(int i=0;i<length;i++) { if(graph[i].length==0) { queue.offer(i); } for(int j:graph[i]) { g.get(i).add(j); rg.get(j).add(i); } } while(!queue.isEmpty()) { int k=queue.poll(); safe[k]=true; for(int i:rg.get(k)) { g.get(i).remove(k); if(g.get(i).isEmpty()) { queue.offer(i); } } } List<Integer> ans=new ArrayList(); for(int i=0;i<length;i++) { if(safe[i]) { ans.add(i); } } return ans; } }
-