目录
1.有向无环图(DAG图)
2.AOV网:顶点活动图
3.拓扑排序
4.实现拓扑排序
力扣.207课程表
牛客.最长回文子序列
1.有向无环图(DAG图)
入度:表示有多少条边指向它
出度:有多少条边向外指出他
2.AOV网:顶点活动图
3.拓扑排序
找到做事情的先后顺序
一些活动必须要在某些活动之后,比如洗脚,我们在洗脚之前可以先烧水,接凉水,等等,所以他的顺序是不唯一的,哪个在前面都可以,当完成一个动作,可以删除一个动作
4.实现拓扑排序
1.找到一个入度为0的点,然后输出
2.删除与该点连接的边
重复1和2的操作,直到图中没有点或者没有入度为0的点为止。
没有入度为0的点,就是有环(所以这也是拓扑排序的重要作用,判断图中是否有环)
借助队列,来一次BFS即可
1.初始化:把所有入度为0点点,加入到队列中
2.
(1)当队列不为空时候,拿出队头元素,加入到最终结果中
(2)删除与该元素相连的边
(3)判断与删除边连接的点,是否入度变成0,如果入度变成0,加入到队列中
力扣.207课程表
能否拓扑排序:是看图中是否有环
有环是false;无环就是true;
如何建图呢?
建立图的概念:灵活使用容器
1.看稠密(看数据量)
邻接表:像是一个链表
List<List<Integer>edges; 链表嵌套链表
Map<Integer,List<Integer>>edges
可以改成String
2.根据算法流程,灵活建图
如何找到入度 int[]in=new int[n]
public boolean canFinish(int n, int[][] p) { //1.准备工作 int[]in=new int[n]; //统计每一个顶点的入度 //邻接表存图 Map<Integer,List<Integer>>edges=new HashMap<>(); for(int i=0;i<p.length;i++){ int a=p[i][0],b=p[i][1];//b->a,先学b //看此时是否存在b if(!edges.containsKey(b)){ edges.put(b,new ArrayList<>()); } edges.get(b).add(a);//把a添加到b所连接的数组了 in[a]++; //a的入度+1 } //3.拓扑排序 //(1)先把入度为0的点,加入到队列中 Queue<Integer>q=new LinkedList<>(); for(int i=0;i<n;i++){ //判断哪一个点的入度为0 if(in[i]==0)q.add(i); } //(2)bfs while(!q.isEmpty()){ int t=q.poll(); //找到了,把里面的返回,假如找不到则返回一个空 for(int a:edges.getOrDefault(t,new ArrayList<>())){ in[a]--;//等同于删除边 if(in[a]==0) q.add(a); } } //4.判断是否有环 for(int x:in){ if(x!=0)return false; } return true; }
public static PrintWriter out =new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static Read in=new Read();
public static void main(String[] args) throws IOException{
int n = in.nextInt();
int m = in.nextInt();
Map<Integer, List<Integer>>edges = new HashMap<>();
int[][]p = new int[m][2];
int k = 0;
while (k < m) {
p[k][0] = in.nextInt();
p[k][1] = in.nextInt();
k++;
}
//建图,统计每一个顶点的入度
int[]in1 = new int[n + 1];
for (int i = 0; i < m; i++) {
int a = p[i][0];
int b = p[i][1]; // a->b
if (!edges.containsKey(a)) {
edges.put(a, new ArrayList<>());
}
edges.get(a).add(b);
in1[b]++;
}
//拓扑排序
Queue<Integer>q = new LinkedList<>();
for (int i = 1; i <=n; i++) {
if (in1[i] == 0) {
q.add(i);
}
}
int[]ret=new int[n];//统计拓扑排序结果
int pos=0;//统计第几个
while (!q.isEmpty()) {
//出度,检查入度为0的点
int t = q.poll();
ret[pos]=t;
pos++; //a->b
//它是获取开始入度为0的点,然后获取它连接的边,用l代替连接的那些点,然后进行l--,就是对应的边减少一块。他这个可以是a ,可以是别的,只是一个代号这种,他的含义是,获取入度为0的点,然后找到他的a,在a--,这样b的度就会减少
for(int l:edges.getOrDefault(t,new ArrayList<>())){
in1[l]--;
if(in1[l]==0){
q.add(l);
}
}
}
if(pos==n){
//输出结果
for(int i=0;i<n-1;i++){
out.print(ret[i]+" ");
}
out.print(ret[n-1]);
}else{
System.out.print(-1);
}
out.close();
}
}
//自定义输入
class Read{
StringTokenizer st=new StringTokenizer("");
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
String next() throws IOException{
while(!st.hasMoreTokens()){
st=new StringTokenizer(bf.readLine());
}
return st.nextToken();
}
String nextLine() throws IOException{
return bf.readLine();
}
int nextInt() throws IOException{
return Integer.parseInt(next());
}
牛客.最长回文子序列
选择的方法是区间dp,动态规划
初始化:
填表顺序:
需要从下往上,从左往右填表
返回dp[0][n-1]
public int longestPalindromeSubSeq (String ss) { //区间dp,动态规划 char[]s=ss.toCharArray(); int n=s.length; int[][]dp=new int[n][n]; for(int i=n-1;i>=0;i--){ dp[i][i]=1; for(int j=i+1;j<n;j++){ if(i==j)dp[i][j]=1; else if(i<j){ if(s[i]==s[j]){ dp[i][j]=dp[i+1][j-1]+2; }else{ dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]); } } } } return dp[0][n-1]; } }