图专题

图的 DFS

右手原则:在没有碰到重复顶点的情况下,分叉路口始终是向右手边走。每路过一个顶点就做一个记号。

左手原则:在没有碰到重复顶点的情况下,分叉路口始终是向左手边走。每路过一个顶点就做一个记号。

可以从图的任何一个顶点开始,进行深度优先遍历。假设我们从顶点 A 开始,遍历过程的每一步如下:

  • 标记 A 节点。
  • 根据右手原则访问顶点 B,并将 B 标记为已访问顶点。
  • 根据右手原则访问顶点 C,并将 C 标记为已访问顶点。
  • 根据右手原则访问顶点 D,并将 D 标记为已访问顶点。
  • 根据右手原则访问顶点 E,并将 E 标记为已访问顶点。
  • 根据右手原则访问顶点 F,并将 F 标记为已访问顶点。
  • 右手原则,应该先访问顶点 F 的邻接顶点 A,但发现 A 已被访问,则访问除 A 之外的最右侧顶点 G。
  • 右手原则,先访问顶点 B,顶点 B 已被访问;再访问顶点 D,顶点 D 已经被访问;最后访问顶点 H。

  • 发现顶点 H 的邻接顶点均已被访问,则退回到顶点 G;
  • 顶点 G 的邻接顶点均已被访问,则退回到顶点 F;
  • 顶点 F 的邻接顶点已被访问,则退回到顶点 E;
  • 顶点 E 的邻接顶点均已被访问,则退回到顶点 D;
  • 顶点 D 的邻接顶点 I 尚未被访问,则访问顶点 I;
  • 顶点 I 的邻接顶点均已被访问,则退回到顶点 D;

过程:

  • 选定一个未被访问过的顶点 V 作为起始顶点,标记为已访问过。
  • 搜索与 V 邻接的所有顶点,判断这些顶点是否被访问过。如果有未被访问过的顶点 W,选取 W 邻接的未被访问过的节点。依次重复进行。
  • 如果某个节点的所有邻接节点都被访问过,那么就回退出到最近被访问的节点,若该节点还有没有被访问的元素,那么选取该接嗲重复。直到与起始顶点 V 相邻的所有顶点都被访问过。

深度优先遍历(搜索)最简单的实现方式就是递归,由于图的存储方式不同

邻接矩阵的深度遍历操作+遍历包含顶点 i 的连通图

邻接矩阵的栈实现

栈是一种先进后出的数据结构,可以用来存储图中遍历时的节点信息。下面是邻接矩阵的栈实现示例。邻接矩阵的栈实现可以用来存储图遍历时的节点信息。

type Stack struct {
    data []int
}

func NewStack() *Stack {
    return &Stack{
        data: make([]int, 0),
    }
}

func (s *Stack) Push(val int) {
    s.data = append(s.data, val)
}

func (s *Stack) Pop() int {
    if len(s.data) == 0 {
        return -1
    }
    val := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return val
}

func (s *Stack) Top() int {
    if len(s.data) == 0 {
        return -1
    }
    return s.data[len(s.data)-1]
}

func (s *Stack) Empty() bool {
    return len(s.data) == 0
}
// graph 如果是  0 1 2 3 4
//            0 0 1 0 0 1
//            1 1 0 1 0 0
//            2 0 1 0 1 0
//            3 0 0 1 0 1
//            4 1 0 0 1 0

// 图可以是
// 0 [1,4]
// 1 [0,2]
// 2 [1,3]
// 3 [2,4]
// 4 [3,1]

func DFS(graph [][]int,start int){
    visited:= map[int]bool
    stack := []int{start}
    for len(stack)> 0{
        index :=  stack[len(stack)-1]
        stack = stack [:len(stack)-1]
        if visited[index] == true{
            continue
        }
        visited[index]= true
        // 在子节点中寻找下一个节点
        for _,v := range graph [index]{
            // visited[v]!= true && v==1 如果图是第一钟
            // visited[v]!= true  图是第二钟
            if visited[v]!= true {
                stack = append(stack,v)
            }
        }
    }
}

邻接矩阵的存储递归实现

邻接矩阵的存储递归实现,可以用来查找连通分量,判断图是否连通。

func DFS(graph [][]int,visted map[int]bool,node int){
    if visted[node] == true{
        return
    }
    visted[node] == true
    // 寻找子节点
    for _,v := graph[node]{
        if visted[v] != true{
          DFS(graph,visted,v)
        }
    }
}
/*  */
func possibleBipartition(n int, dislikes [][]int) bool {
    graph := make([][]int,n)
    colored := make(map[int]int)
    // 图的建立
    for _,item := range dislikes{
        who1,who2:=item[0]-1,item[1]-1
        graph[who1] = append(graph[who1],who2)
        graph[who2] = append(graph[who2],who1)
    }
    //fmt.Println(graph)

    // 染色
    for i:=0;i<n;i++{
        if colored[i] == 0 && DFS(graph,i,1,colored) == false{
            return false
        }
    }
    return true
    // 图的遍历
}
// 图的遍历
func DFS(graph [][]int, node int, color int, colored map[int] int)bool{
    // color是当前节点的颜色
    // colored[v]是子节点的颜色
    colored[node] = color
    // 寻找子节点
    for _,v:= range graph[node]{
        // 使用深度优先搜索(DFS)或广度优先搜索(BFS)实现。具体来说,我们从一个任意节点开始递归地遍历整个图,并将每个节点染成黑点或白点。在遍历的过程中,如果当前节点和其相邻节点颜色相同,那么不是二分图。否则继续向下遍历,并将相邻节点染上与当前节点不同的颜色,直到所有节点都被访问完成。

        //
        if colored[v] == color{
            return false
        }
        if colored[v] == 0 && DFS(graph,v,-color ,colored)==false{
            return false
        }
    }
    return true
}
/*  */
func isBipartite(graph [][]int) bool {
    colored := make(map[int]int,len(graph))
    //如果图不是连通的,则需要对每个连通分量进行检测
    for i:=0;i<len(graph);i++{
        if colored[i] == 0 {
            if DFS(graph,colored,1,i)==false {
                return false
            }
        }
    }
    return true
}

func DFS(graph [][]int,colored map[int]int,color int,node int)bool{
    colored[node] = color
    for _,neighbor := range graph[node]{
        // 如果邻居节点没有被访问过继续向下遍历
        if colored[neighbor] == 0{
            if DFS(graph,colored,-color,neighbor)== false{
                return false
            }
            // 邻居节点已经被访问过,并且颜色与当前节点相同
        }else if colored[neighbor] != -color{
            return false
        }

    }
    return true
}


var firstWrongNode  *TreeNode
var secondWrongNode  *TreeNode
var preNode *TreeNode
func recoverTree(root *TreeNode) {
    preNode = nil
    firstWrongNode = nil
    secondWrongNode = nil
    inorderTraverse(root)
    firstWrongNode.Val , secondWrongNode.Val = secondWrongNode.Val,firstWrongNode.Val
}

func inorderTraverse(root *TreeNode)  {
    if root == nil{
        return
    }
    inorderTraverse(root.Left)
    // 最左边小于前躯节点的『前躯节点』是第一个错误指针
    // 最右边小于前躯节点的是第二个错误指针
    if preNode!= nil && root.Val < preNode.Val{
        if firstWrongNode == nil{
            firstWrongNode = preNode
            // 1 2 4 3 5
            //     p r
            //       p r

            // 如果是不相邻
            // 1 2 5 4 3
            //     p r
            //        p r
            secondWrongNode = root
        }else{
            secondWrongNode = root
        }

    }
    // 更改前躯节点
    preNode = root
    inorderTraverse(root.Right)
}
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func countNodes(root *TreeNode) int {
    if root == nil{
        return 0
    }
    return BFS(root)
}

func BFS(root *TreeNode) int{
    layer:= []*TreeNode{root}
    res := 0
    for len(layer) >0{
        nextLayer:= []*TreeNode{}
        for _,v := range layer{
            if v == nil{
                continue
            }
            if v.Left!=nil{
                nextLayer = append(nextLayer,v.Left)
            }
            if v.Right!= nil{
                nextLayer = append(nextLayer,v.Right)
            }
        }
        res = res+len(layer)
        if len(nextLayer) == 0{
            return res
        }
        layer = nextLayer
    }
    return 0
}

/* 124. 二叉树中的最大路径和 */
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var maxPath int
func maxPathSum(root *TreeNode) int {
    maxPath = math.MinInt32
    Traverse(root)
    return maxPath
}

func Traverse(root *TreeNode){
    if root == nil{
        return
    }
    pathSum := DFS(root)
    maxPath = max(maxPath,pathSum)
    Traverse(root.Left)
    Traverse(root.Right)
}

func DFS(root *TreeNode) int{
    if root == nil{
        return 0
    }
    left := DFS(root.Left)
    right := DFS(root.Right)
    // 左子树+ 右子树+ 根节点(根部节点是必选)
    maxPath = max(maxPath,max(left,0) + max(right,0) + root.Val)
    //fmt.Println(maxPath)
    return max(max(left,right),0) + root.Val
}




func max(a int ,b int) int{
    // 如果是小于0的数就抛弃
    if a>b {
       return a
    }
    return b
}

var res [][]int
var target int
func pathSum(root *TreeNode, targetSum int) [][]int {
    res = [][]int{}
    target =  targetSum
    DFS(root,[]int{},0)
    return res
}

func DFS(root *TreeNode,path []int,sum int) {
    if root == nil{
        return
    }
    path = append(path,root.Val)
    sum = sum + root.Val
    if sum == target && root.Left == nil && root.Right == nil{
        tmp := make([]int, len(path))
        copy(tmp, path)
        res = append(res, tmp)
       // 直接这么写是有问题,因为res存储的是对切片的引用,当切片在递归的时候被更改,res也会被更改
    }
    DFS(root.Left,path,sum)
    DFS(root.Right,path,sum)
    path = path[:len(path)-1]
}
/*834. 树中距离之和*/
/*
给定一个无向、连通的树。树中有 n 个标记为 0...n-1 的节点以及 n-1 条边 。

给定整数 n 和数组 edges , edges[i] = [ai, bi]表示树中的节点 ai 和 bi 之间有一条边。

返回长度为 n 的数组 answer ,其中 answer[i] 是树中第 i 个节点与所有其他节点之间的距离之和。
下面的代码有什么问题吗
*/
var graph [][]int
func sumOfDistancesInTree(n int, edges [][]int) []int {
    // 构建邻接表
    graph= make([][]int,n)
    for _,v := range edges{
        who1:=v[0]
        who2:=v[1]
        graph[who1] = append(graph[who1],who2)
        graph[who2] = append(graph[who2],who1)
    }
    /*
    0 [1 2]  0
    1 [0]
    2 [0 3 4 5]
    3 [2]
    4 [2]
    5 [2]
     */
    return traverse(graph,n)
}

func traverse(graph [][]int,n int)[]int{
    res := []int{}
    for i:=0;i<n;i++{
        visted:= make(map[int]bool,n)
        res = append(res,DFS(i,0,visted))
    }
    return res
}

func DFS(root int,nodeNum int,visted map[int]bool)int{
    if visted[root] == true{
        return nodeNum-1
    }
    visted[root] = true
    nodeNum = nodeNum+1
    res :=0
    for _ ,v := range graph[root]{
        res = res +DFS(v,nodeNum,visted)
    }
    return res
}

func DFS(){

}
 /*
    0 [1 2]  0
    1 [0]
    2 [0 3 4 5]
    3 [2]
    4 [2]
    5 [2]
     */
var (
    graph     [][]int
    answer    []int
    subSize   []int // 以node为根节点构成的子树的个数
    totalDist []int // 以node为根节点构成的树,Node 到他们所有的距离的和
)

func sumOfDistancesInTree(n int, edges [][]int) []int {
    graph = make([][]int, n)
    for _, e := range edges {
        u, v := e[0], e[1]
        graph[u] = append(graph[u], v)
        graph[v] = append(graph[v], u)
    }

    answer = make([]int, n)
    subSize = make([]int, n)
    totalDist = make([]int, n)

    dfs1(0, -1)
    dfs2(0, -1)

    return answer
}

// 计算子树大小和以及从当前节点出发到其他节点的距离和,初始状态下父节点为 -1
func dfs1(node, parent int) {
    // 以node为根节点构成的子树的个数
    subSize[node] = 1
    for _, child := range graph[node] {
        if child != parent {
            dfs1(child, node)
            // 以node为根节点构成的子树的个数
            subSize[node] = subSize[node]+subSize[child]
            // 以node为根节点构成的树,Node 到他们所有的距离的和
            totalDist[node] = totalDist[node]+totalDist[child] + subSize[child]
        }
    }
}

// 根据 dfs1 的结果计算每个节点到其他节点的距离和
func dfs2(node, parent int) {
    answer[node] = totalDist[node]

    for _, child := range graph[node] {
        if child != parent {
            // 每行代码都是干什么的?
            oldDistNode := totalDist[node]
            oldDistChild := totalDist[child]
            oldSizeNode := subSize[node]
            oldSizeChild := subSize[child]

            // 先处理从 node 出发到其他节点的距离和
            totalDist[node] -= totalDist[child] + subSize[child]
            subSize[node] -= subSize[child]
            totalDist[child] += totalDist[node] + subSize[node]
            subSize[child] += subSize[node]

            // 对子节点进行递归遍历
            dfs2(child, node)

            // 恢复现场,以便对其他子节点进行计算
            subSize[child] = oldSizeChild
            totalDist[child] = oldDistChild
            subSize[node] = oldSizeNode
            totalDist[node] = oldDistNode
        }
    }
}
Σ(distance between i and j) for all j in subtree(i)

这个符号表示的是:对于一个节点 i,它的子树中的所有节点 j 到节点 i 的距离之和。其中 Σ 表示求和,distance between i and j 表示 i 和 j 之间的距离。

举个例子,假设我们有如下一棵树:

         1
       / | \
      2  3  4
    /   |    \
   5    6     7
如果我们考虑节点 2,它的子树包含了节点 2 和节点 5。对于这两个节点,它们到节点 2 的距离分别是 0 和 1,因此 Σ(distance between i and j) for all j in subtree(i) 就等于 0 + 1 = 1。

同样地,如果我们考虑节点 1,它的子树包含了所有节点。那么 Σ(distance between i and j) for all j in subtree(i) 就等于:


distance(1, 2) + distance(1, 3) + distance(1, 4) + distance(1, 5) + distance(1, 6) + distance(1, 7)
也就是从节点 1 到其他所有节点的距离之和。





Σ(distance between i and j) for all j NOT in subtree(i)


      1
     / \
    2   3
   / \   \
  4   5   6
     / \
    7   8
    请向这样画图让我理解
     nodeNum[root] += nodeNum[v] + 1
    distSum[root] += distSum[v] + nodeNum[v] + 1

假设我们要计算节点 2 的距离。首先,我们需要计算从节点 2 到根节点 1 的路径,以及不在节点 2 子树中的所有节点到根节点 1 的路径。可以得到如下路径:


    Node          Path to Root
    -------------------------------------------------------
    1             1
    3             1 -> 3
    6             1 -> 3 -> 6
    2             1 -> 2
    4             1 -> 2 -> 4
    5             1 -> 2 -> 5
    7             1 -> 2 -> 5 -> 7
    8             1 -> 2 -> 5 -> 8

接下来,我们需要找到节点 2 和每个不在其子树中的节点 j 之间的距离。对于节点 4 来说,它在节点 2 子树中,因此不需要计算。对于其它节点,我们可以通过以下步骤来计算它们和节点 2 之间的距离:

首先,我们需要找到节点 2 和节点 j 的 LCA(最近公共祖先)节点。对于节点 3,LCA 是节点 1;对于节点 6,LCA 是节点 1;对于节点 5,LCA 是节点 2;对于节点 7,LCA 是节点 5;对于节点 8,LCA 是节点 5。


最后,将这两个距离相加即可得到节点 2 和节点 j 之间的距离。
以下是每个节点和节点 2 之间的距离:

Node     Distance to Node 2
----------------------------------
1        1
2        0
3        2
4        1
5        1
7        2
8        2

最后,我们将这些距离相加即可得到节点 2 的距离:

2 + 1 + 3 + 2 + 2 = 12

因此,我们得出节点 2 的距离为 12。


// 这个代码用来求任意两个节点之间的距离,代码有没有问题
type Node struct {
    val      int
    children []*Node
}
var parent map[*Node]*Node

// 计算节点 i 到节点 j 之间的距离
func distance(i *Node, j *Node) int {
    // 如果节点 i 和节点 j 相等,则它们之间的距离为 0
    if i == j {
        return 0
    }

    // 找到节点 i 和节点 j 的 LCA(最近公共祖先)节点
    lca := findLCA(i, j)

    // 计算节点 i 和节点 j 之间的距离
    dist_i_to_lca := depth(i) - depth(lca)
    dist_j_to_lca := depth(j) - depth(lca)
    return dist_i_to_lca + dist_j_to_lca
}

// 找到节点 i 和节点 j 的 LCA 节点
func findLCA(i *Node, j *Node) *Node{
    stack := []*Node{i}
    //  parent[k] k是孩子,v 是父
    parent := make(map[*Node]*Node)
    parent[i] = nil
    for len(stack ) >0 {
        curr:= stack[len(stack)-1]
        stack = stack [:len(stack)-1]
        for _,child := range curr.children{
            stack = append(stack,child)
            parent[child]  = curr
        }
    }
    // 找到节点 i 和节点 j 分别到根节点的路径
    // 记录i的所有祖先节点
    path_i := make(map[*Node]bool)
    for i!= nil{
       path_i[i] = true
       i = parent[i]
    }

    for  path_i[j]== false{
        j = parent[j]
    }
    return j
}

// 计算节点的深度(从根节点开始)
func depth(node *Node) int{
    d:=0
    for node!= nil{
        node = parent[node]
        depth++
    }
    return depth
}


var  nodeNum []int
var  distSum []int
var graph [][]int
var N int
func sumOfDistancesInTree(n int, edges [][]int) []int {
    nodeNum = make([]int,n)
    distSum = make([]int,n)
    graph = make([][]int, n)
    N= n
    for _, e := range edges {
        u, v := e[0], e[1]
        graph[u] = append(graph[u], v)
        graph[v] = append(graph[v], u)
    }
    DFS(0,-1)
    DFS2(0,-1)
   return distSum
}

func DFS(root int ,father int){
    //  1
    // / \
    // 2  3
    // |
    // 4
    // 子树有几个节点,那么1-2这个路径就要走几次
    // distSum[i] 是以i为根节点的子树到i的距离
    // 每个子树的节点个数nodeNum[i]
    // distSum[4] =0 distSum[3]=0 distSum[2]=1
    // nodeNum[2] = 2 nodeNum[3]= 1 nodeNum[4] = 1
    // distSum[1]= distSum[2]+distSum[3]+ distSum[4]+ nodeNum[2] +nodeNum[3] + nodeNum[4]

    // 通过节点distSum[1] 计算distSum[2],就是1—2 1-3每个都少走了一步
    // distSum[2]= distSum[1]-nodeNum[2]
    // 子树以外的节点呢,有N-nodeNum[2]个,从计算distSum[0]变成计算distSum[2]:从节点 0 到这N-nodeNum[2]个,变成从节点 2 到这N-nodeNum[2]个,每个节点都多走了一步,一共多走了N-nodeNum[2]步。

    // distSum[i]=distSum[root]−nodeNum[i]+(N−nodeNum[i])

    neighbors := graph[root]
    for _,v := range neighbors {
        if v == father{
            continue
        }
        DFS(v,root)
        //在 nodeNum[root] += nodeNum[v] + 1 中,nodeNum 是一个记录每个节点子树大小的数组。nodeNum[root] 表示以 root 为根的子树中包含的节点总数,而 nodeNum[v] 表示以 v 为根的子树中包含的节点总数。因此,nodeNum[root] += nodeNum[v] + 1 的意思是将 v 子树的大小加到 root 子树的大小中,并将 v 点本身也计算在内(即加 1)。

       nodeNum[root] += nodeNum[v] + 1
       distSum[root] += distSum[v] + nodeNum[v] + 1
       请列出每个点对应的nodeNum和distSum
       // 1 0
       // 2 1
       // 3 1
       // 4 2
       // 5 2
       // 6 2
       // 7 3
       // 8 4
    }

}

func DFS2(root int,father int){
   neighbors := graph[root]
    for _,v := range neighbors {
        if v== father{
            continue
        }
        distSum[v] = distSum[root] - nodeNum[v] - 1 + (N - nodeNum[v] - 1)
        DFS2(v,root)
    }
}

请列出在DFS以及DFS2过程中nodeNum 以及 distSum
         1
       /   \
      2     3
     / \     \
    4   5     6
         \
          7
         /
        8
以节点 1 为根的子树中,所有节点之间的距离总和为 $14+16+11+2+3=46$。但是注意,题目给出的代码中还需要加上每个节点与根节点之间的距离。在本例中,节点 2 到根节点的距离为 $1$,节点 3 到根节点的距离为 $1$,节点 4 到根节点的距离为 $2$,节点 5 到根节点的距离为 $2$,节点 6 到根节点的距离为 $2$,节点 7 到根节点的距离为 $3$,节点 8 到根节点的距离为 $3$。因此,所有节点与根节点之间的距离总和为 $1+1+2+2+2+3+3=14$。


/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

var res int
func maxAncestorDiff(root *TreeNode) int {
    res = 0
    DFS(root,root.Val,root.Val)
    return res
}

func DFS(root *TreeNode, minN int, maxN int) int{
    if root == nil{
        return  maxN - minN
    }
    left:=DFS(root.Left, min(root.Val, minN), max(root.Val, maxN))
    right:=DFS(root.Right, min(root.Val, minN), max(root.Val, maxN))
    res = max(res,max(left,right))
    return res
}


func max(a int ,b int ) int{
    if a> b {
        return a
    }
    return b
}

func min(a int ,b int ) int{
    if a<b {
        return a
    }
    return b
}
func abs( a int) int{
    if a > 0{
        return a
    }
    return -a
}
var fatherNode map[*TreeNode]*TreeNode
/* 寻找任意两个点之间的距离 */
func DFS(root *TreeNode,father *TreeNode){
    if root == nil{
        return
    }
    fatherNode[root] = father
    DFS(root.Left,root)
    DFS(root.Right,root)
}

// 寻找公共祖先
func FindLCA(i *TreeNode ,j *TreeNode)*TreeNode{
    path_i:= map[*TreeNode]bool{}
    for i!=nil{
        path_i[i]=true
        i = fatherNode[i]
    }
    for path_i[j]==false{
        j = fatherNode[j]
    }
    return j
}


func Distance(i *TreeNode ,j *TreeNode) int{
    if i==j{
        return 0
    }
    lca:= FindLCA(i,j)
    d1:= abs(depth(lca)-depth(i))
    d2:= abs(depth(lca)-depth(j))
    return d1+d2
}

func depth(i *TreeNode)int{
    d:=0
    for i!=nil{
        d++
        i =fatherNode[i]
    }
    return d
}

func abs( a int) int{
    if a > 0{
        return a
    }
    return -a
}
/*  */
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
 var res []int
 var K int
func distanceK(root *TreeNode, target *TreeNode, k int) []int {
    res = []int{}
    fatherNode = map[*TreeNode]*TreeNode{}
    K = k
    DFS(root,nil)
    //fmt.Println(Distance(root.Right,root.Left))
    traverse(root,target)
    return res
}

func traverse(root *TreeNode,target *TreeNode){
    if root == nil{
        return
    }
    dis:= Distance(root,target)
    if dis == K{
        res = append(res,root.Val)
    }
    traverse(root.Left,target)
    traverse(root.Right,target)
}

var fatherNode map[*TreeNode]*TreeNode
/* 寻找任意两个点之间的距离 */
func DFS(root *TreeNode,father *TreeNode){
    if root == nil{
        return
    }
    fatherNode[root] = father
    DFS(root.Left,root)
    DFS(root.Right,root)
}

// 寻找公共祖先
func FindLCA(i *TreeNode ,j *TreeNode)*TreeNode{
    path_i:= map[*TreeNode]bool{}
    for i!=nil{
        path_i[i]=true
        i = fatherNode[i]
    }
    for path_i[j]==false{
        j = fatherNode[j]
    }
    return j
}

func Distance(i *TreeNode ,j *TreeNode) int{
    if i==j{
        return 0
    }
    lca:= FindLCA(i,j)
    d1:= abs(depth(lca)-depth(i))
    d2:= abs(depth(lca)-depth(j))
    return d1+d2
}

func depth(i *TreeNode)int{
    d:=0
    for i!=nil{
        d++
        i =fatherNode[i]
    }
    return d
}

func abs( a int) int{
    if a > 0{
        return a
    }
    return -a
}
/* n 个城市其中一些彼此相连另一些没有相连如果城市 a 与城市 b 直接相连且城市 b 与城市 c 直接相连那么城市 a 与城市 c 间接相连

省份 是一组直接或间接相连的城市组内不含其他没有相连的城市

给一个 n x n 的矩阵 isConnected 其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连 isConnected[i][j] = 0 表示二者不直接相连

返回矩阵中 省份 的数量/*


var graph [][]int
var visted map[int]bool
func findCircleNum(isConnected [][]int) int {
    graph = isConnected
    visted = map[int]bool{}
    DFS(0,visted)
    return len(isConnected)-len(visted)+1
}

func DFS(father int,visted map[int]bool){
    visted[father]=true
    for son,v := range graph[father]{
        if v == 1 && son != father && visted[son]==false{
            DFS(son,visted)
        }
    }
}
var graph [][]int

func findCircleNum(isConnected [][]int) int {
    count:=0
    graph = isConnected
    visted :=map[int]bool{}
    for k,_:= range isConnected{
        if visted[k] == true{
            continue
        }
        DFS(k,visted)
        count++
    }
    return count
}


func DFS(father int,visted map[int]bool){
    visted[father]=true
    for son,v := range graph[father]{
        if v == 1 && son != father && visted[son]==false{
            DFS(son,visted)
        }
    }
}
/*有一个有 n 个节点的有向图,节点按 0 到 n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示, graph[i]是与节点 i 相邻的节点的整数数组,这意味着从节点 i 到 graph[i]中的每个节点都有一条边。

如果一个节点没有连出的有向边,则它是 终端节点 。如果没有出边,则节点为终端节点。如果从该节点开始的所有可能路径都通向 终端节点 ,则该节点为 安全节点 。

返回一个由图中所有 安全节点 组成的数组作为答案。答案数组中的元素应当按 升序 排列。图中可能包含自环,这个需要注意

/*
0 1 2
1 2 3
2 5
3 0
4 5
5
6
     0          4      5  6
   /   \      /
  1     2     5
  /\    |
  2 3   5
    |
    0

1.可能构成环,需要对每个根底的树做判断,判断节点是否出现
2. 四颗树有关系,全局的变量记录某个节点是不是安全节点或者是终端节点

 */

func eventualSafeNodes(graph [][]int) []int {
    result := []int{}
    terminal, safe, visited := make(map[int]bool, len(graph)), make(map[int]bool, len(graph)), make(map[int]bool, len(graph))

    // Find terminal nodes
    for i := 0; i < len(graph); i++ {
        if len(graph[i]) == 0 {
            terminal[i] = true
            safe[i] = true
        }
    }

    // Check safety for each node
    for i := 0; i < len(graph); i++ {
        if isNodeSafe(i, safe, terminal, visited, graph) {
            result = append(result, i)
        }
    }

    return result
}

func isNodeSafe(node int, safe map[int]bool, terminal map[int]bool, visited map[int]bool, graph [][]int) bool {
    if visited[node] {
        return false // Already visited in this path
    }
    if terminal[node] || safe[node] {
        return true
    }

    visited[node] = true
    for _, son := range graph[node] {
        if !isNodeSafe(son, safe, terminal, visited, graph) {
            return false
        }
    }
    // 为什么需要 visited[node] = false?
    visited[node] = false // Mark as unvisited for future paths
    safe[node] = true     // Mark this node as safe
    return true
}



func eventualSafeNodes(graph [][]int) []int {
    n := len(graph)
    // 反向图
    为什么要建立反向图
    reverseGraph := make([][]int, n)
    // 入度数组
    inDegrees是出度节点吧
    inDegrees := make([]int, n)
    for i := 0; i < n; i++ {
        for _, v := range graph[i] {
            reverseGraph[v] = append(reverseGraph[v], i)
            inDegrees[i]++
        }
    }

    // 终端节点入队
    queue := []int{}
    for i := 0; i < n; i++ {
        if inDegrees[i] == 0 {
            queue = append(queue, i)
        }
    }

    // 拓扑排序
    result := []int{}
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        // 加入结果集
        result = append(result, node)
        // 不断的从终端节点出发判断能不能达到某个顶点
        for _, v := range reverseGraph[node] {
            inDegrees[v]--
            // 将入度为0的节点入队
            if inDegrees[v] == 0 {
                queue = append(queue, v)
            }
        }
    }

    sort.Ints(result)
    return result
}

广度优先遍历模仿递归的前序遍历

type Node struct {
    Val int
    Left *Node
    Right *Node
}

func PreorderTraversal(root *Node) []int {
    var res []int
    if root == nil {  // 处理特殊情况
        return res
    }

    stack := []*Node{root}
    for len(stack) > 0 {  // 广度优先遍历
        node := stack[len(stack)-1]
        stack = stack[:len(stack)-1]

        if node != nil {
            res = append(res, node.Val)  // 前序遍历顺序:根、左、右
            stack = append(stack, node.Right)  // 右子节点入栈
            stack = append(stack, node.Left)  // 左子节点入栈
        }
    }

    return res
}

广度优先遍历模仿递归的中序遍历

/*
            0
          /   \
        1       2
       / \     / \
      3   4   5   6
     / \
    7   8
          7 3 8 1 4 0 5 2 6

 */
//画一个节点从0~10的完全二叉树
type Node struct {
    Val int
    Left *Node
    Right *Node
}

func InorderTraversal(root *Node) []int {
    var res []int
    if root == nil {  // 处理特殊情况
        return res
    }

    stack := []*Node{}
    node := root
    // node 如果不为空,那么就不断递归左节点
    for len(stack) > 0 || node != nil {  // 广度优先遍历
        // 先把所有的左子树入栈
        for node != nil{
            stack = append(stack,node)
            node = node.Left
        }
        // 从底向上弹出根节点,放入右节点
        // 回退
        node = stack [len(stack)-1]
        stack= stack [:len(stcak)-1]
        // 根
        res = append(res,node.Val)
        node = node.Right
    }

    return res
}


广度优先遍历模仿递归的后序遍历

/*
            0
          /   \
        1       2
       / \     / \
      3   4   5   6
     / \
    7   8
        7 8 3 4 1 5 6 2 0
        // 0 2 1 4 3 8 7
        // 7 8

 */
type Node struct {
    Val int
    Left *Node
    Right *Node
}

func PostorderTraversal(root *Node) []int {
    var res []int
    if root == nil {  // 处理特殊情况
        return res
    }

    stack1 := []*Node{root}
    stack2 := []*Node{}
    for len(stack1) > 0 {  // 广度优先遍历
        node := stack1[len(stack1)-1]
        stack1 = stack1[:len(stack1)-1]

        stack2 = append(stack2, node)
        if node.Left != nil {  // 左节点入栈1
            stack1 = append(stack1, node.Left)
        }
        if node.Right != nil {  // 右节点入栈1
            stack1 = append(stack1, node.Right)
        }
    }

    for len(stack2) > 0 {  // 反转栈2,得到后序遍历结果
        node := stack2[len(stack2)-1]
        stack2 = stack2[:len(stack2)-1]
        res = append(res, node.Val)
    }

    return res
}

Tags: leetcode
Share: X (Twitter) Facebook LinkedIn