由于要面试的缘故,在看算法导论的图算法一节,决定把基本的算法都用java代码实现出来。
1. 图的表示,使用链接表的形式。
class TreeNode{
int nodeNum ; //节点编号
TreeNode parent = null; //遍历时的父节点
int dis = Integer.MAX_VALUE;// 距离源节点的路径
int discoverTime =0; //在DFS遍历时发现的时间
int finishTime =0;//DFS遍历时结束的时间
Color color = Color.White;//初始时的颜色标记
List<Integer> edges = new ArrayList<Integer>();//节点的边集合
public TreeNode(int i) {
nodeNum = i;
}
public int getNodeNum() {
return nodeNum;
}
public void setNodeNum(int nodeNum) {
this.nodeNum = nodeNum;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
public int getDis() {
return dis;
}
public void setDis(int dis) {
this.dis = dis;
}
public int getDiscoverTime() {
return discoverTime;
}
public void setDiscoverTime(int discoverTime) {
this.discoverTime = discoverTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
}
2. 图的构建:
TreeNode[] heads = null;
final int NODENUM ;
public GraphicModelII(int nodeNum) {
NODENUM = nodeNum;
heads = new TreeNode[NODENUM+1];
for(int i=1;i<=nodeNum;i++){
heads[i] = new TreeNode(i);
}
}
public void addEdge(int src,int des){
heads[src].edges.add(des);
}
3. 广度优先搜索
public void BFS(int src){
Queue<TreeNode> searchs = new LinkedList<TreeNode>();
heads[src].color = Color.Gray;
heads[src].dis= 0;
searchs.add(heads[src]);
while(!searchs.isEmpty()){
TreeNode cur = searchs.poll();
System.out.println("Node : "+cur.nodeNum);
for(Integer des:cur.edges){
TreeNode now = heads[des];
if(now.color == Color.White){
now.color = Color.Gray;
now.dis = cur.dis+1;
now.parent = cur;
searchs.add(now);
}
}
cur.color = Color.Black;
}
}
4. 深度优先搜索
public void DFS(){
systemTime = 0;
for(int i=1;i <=NODENUM;i++){
TreeNode node = heads[i];
if(node.color==Color.White){
DFS_Visit(node);
}
}
}
public void DFS_Visit(TreeNode node){
node.color = Color.Gray;
systemTime++;
node.discoverTime = systemTime;
for(Integer des:node.edges){
TreeNode now = heads[des];
if(now.color==Color.White){
now.parent = node;
DFS_Visit(now);
}
}
node.color = Color.Black;
systemTime++;
node.finishTime = systemTime;
topoSortUtil.push(node);
System.out.println(node.nodeNum+"start:\t"+node.discoverTime+" end:\t"+node.finishTime);
}
5. topo 排序:
topo 排序的算法
使用DFS来完成,对于每一个发现的节点,压入栈中
Stack<TreeNode> topoSortUtil = new Stack<TreeNode>();
对应DFS中 黄色标记代码
public void topoSort(){
topoSortUtil.clear();
DFS();
while(!topoSortUtil.isEmpty()){
System.out.println(topoSortUtil.pop().nodeNum);
}
}
6. 求强连通分支SCC。
第一步 使用DFS算法,把每个节点遍历完成时写入到topoSortUtil 中,栈顶的元素是最后才遍历结束的节点。
第二步,求原图G的转置, 把原图中 u -v的边 变为 v-u.
见reverTopo 函数,遍历之后的图用heads_reverse 表示
第三步 使用DFS算法,求SCC。需要注意的是,在原始的DFS中
public void DFS(){
systemTime = 0;
for(int i=1;i <=NODENUM;i++){
TreeNode node = heads[i];
if(node.color==Color.White){
DFS_Visit(node);
}
}
}
for 循环 的节点顺序无要求,但是在求SCC时,需要使用topoSortUtil,最晚结束遍历的元素作为第二次遍历的第一个元素,并依此类推。
public void SCC(){
DFS();
List<TreeNode> scc = new ArrayList<TreeNode>();
reverTopo();
while(!topoSortUtil.isEmpty()){
TreeNode cur = heads_reverse[topoSortUtil.pop().nodeNum];
if(cur.color==Color.White){
DFS_Visit_Scc(cur,scc);
}
for(TreeNode node:scc){
System.out.print(node.nodeNum);
}
System.out.println();
scc.clear();
}
}
public void DFS_Visit_Scc(TreeNode node,List<TreeNode> scc){
node.color = Color.Gray;
systemTime++;
node.discoverTime = systemTime;
for(Integer des:node.edges){
TreeNode now = heads_reverse[des];
if(now.color==Color.White){
now.parent = node;
DFS_Visit_Scc(now,scc);
}
}
node.color = Color.Black;
systemTime++;
node.finishTime = systemTime;
scc.add(node);
}
private void reverTopo(){
for(int i=1;i <=NODENUM;i++){
TreeNode node = heads[i];
int src = node.nodeNum;
for(Integer des:node.edges){
TreeNode reverNode = heads_reverse[des];
reverNode.edges.add(src);
}
}
}
完整代码
package graphic;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
public class GraphicModelII {
TreeNode[] heads = null;
final int NODENUM ;
static int systemTime = 0;
Stack<TreeNode> topoSortUtil = new Stack<TreeNode>();
TreeNode[] heads_reverse = null;
public GraphicModelII(int nodeNum) {
NODENUM = nodeNum;
heads = new TreeNode[NODENUM+1];
heads_reverse =new TreeNode[NODENUM+1];
for(int i=1;i<=nodeNum;i++){
heads[i] = new TreeNode(i);
heads_reverse[i] = new TreeNode(i);
}
}
public void addEdge(int src,int des){
heads[src].edges.add(des);
}
public void BFS(int src){
Queue<TreeNode> searchs = new LinkedList<TreeNode>();
heads[src].color = Color.Gray;
heads[src].dis= 0;
searchs.add(heads[src]);
while(!searchs.isEmpty()){
TreeNode cur = searchs.poll();
System.out.println("Node : "+cur.nodeNum);
for(Integer des:cur.edges){
TreeNode now = heads[des];
if(now.color == Color.White){
now.color = Color.Gray;
now.dis = cur.dis+1;
now.parent = cur;
searchs.add(now);
}
}
cur.color = Color.Black;
}
}
public void DFS(){
systemTime = 0;
for(int i=1;i <=NODENUM;i++){
TreeNode node = heads[i];
if(node.color==Color.White){
DFS_Visit(node);
}
}
}
public void DFS_Visit(TreeNode node){
node.color = Color.Gray;
systemTime++;
node.discoverTime = systemTime;
for(Integer des:node.edges){
TreeNode now = heads[des];
if(now.color==Color.White){
now.parent = node;
DFS_Visit(now);
}
}
node.color = Color.Black;
systemTime++;
node.finishTime = systemTime;
topoSortUtil.push(node);
System.out.println(node.nodeNum+"start:\t"+node.discoverTime+" end:\t"+node.finishTime);
}
public void topoSort(){
topoSortUtil.clear();
DFS();
while(!topoSortUtil.isEmpty()){
System.out.println(topoSortUtil.pop().nodeNum);
}
}
public void SCC(){
DFS();
List<TreeNode> scc = new ArrayList<TreeNode>();
reverTopo();
while(!topoSortUtil.isEmpty()){
TreeNode cur = heads_reverse[topoSortUtil.pop().nodeNum];
if(cur.color==Color.White){
DFS_Visit_Scc(cur,scc);
}
for(TreeNode node:scc){
System.out.print(node.nodeNum);
}
System.out.println();
scc.clear();
}
}
public void DFS_Visit_Scc(TreeNode node,List<TreeNode> scc){
node.color = Color.Gray;
systemTime++;
node.discoverTime = systemTime;
for(Integer des:node.edges){
TreeNode now = heads_reverse[des];
if(now.color==Color.White){
now.parent = node;
DFS_Visit_Scc(now,scc);
}
}
node.color = Color.Black;
systemTime++;
node.finishTime = systemTime;
scc.add(node);
}
private void reverTopo(){
for(int i=1;i <=NODENUM;i++){
TreeNode node = heads[i];
int src = node.nodeNum;
for(Integer des:node.edges){
TreeNode reverNode = heads_reverse[des];
reverNode.edges.add(src);
}
}
}
public static void main(String[] args) {
// GraphicModelII graph = new GraphicModelII(5);
// graph.addEdge(1, 2);
// graph.addEdge(1, 5);
// graph.addEdge(2, 1);
// graph.addEdge(2, 5);
// graph.addEdge(2, 4);
// graph.addEdge(2, 3);
// graph.addEdge(3, 2);
// graph.addEdge(3, 4);
// graph.addEdge(4, 2);
// graph.addEdge(4, 5);
// graph.addEdge(5, 4);
// graph.addEdge(5, 2);
// graph.addEdge(5, 1);
// graph.BFS(1);
//graph.DFS();
// GraphicModelII graph = new GraphicModelII(9);
// graph.addEdge(1, 2);
// graph.addEdge(1, 8);
// graph.addEdge(2, 3);
// graph.addEdge(2, 8);
// graph.addEdge(3, 6);
// graph.addEdge(4, 3);
// graph.addEdge(4, 5);
// graph.addEdge(5, 6);
// graph.addEdge(7, 8);
// graph.topoSort();
//test scc
GraphicModelII graph = new GraphicModelII(8);
graph.addEdge(1, 2);
graph.addEdge(2, 3);
graph.addEdge(3, 4);
graph.addEdge(4, 3);
graph.addEdge(2, 5);
graph.addEdge(2, 6);
graph.addEdge(3, 7);
graph.addEdge(4, 8);
graph.addEdge(5, 1);
graph.addEdge(5, 6);
graph.addEdge(6, 7);
graph.addEdge(7, 6);
graph.addEdge(7,8);
graph.addEdge(8,8);
//graph.topoSort();
graph.SCC();
}
}
class TreeNode{
int nodeNum ;
TreeNode parent = null;
int dis = Integer.MAX_VALUE;
int discoverTime =0;
int finishTime =0;
Color color = Color.White;
List<Integer> edges = new ArrayList<Integer>();
public TreeNode(int i) {
nodeNum = i;
}
public int getNodeNum() {
return nodeNum;
}
public void setNodeNum(int nodeNum) {
this.nodeNum = nodeNum;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
public int getDis() {
return dis;
}
public void setDis(int dis) {
this.dis = dis;
}
public int getDiscoverTime() {
return discoverTime;
}
public void setDiscoverTime(int discoverTime) {
this.discoverTime = discoverTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
}