算法套路十九——树形DP

news2024/11/25 8:18:25

算法套路十九——树形DP

树形 DP,即在树上进行的 DP。由于树固有的递归性质,这里的DP是指是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,故虽然带有DP,但一般都是通过递归来进行。

算法示例一:LeetCode543. 二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
在这里插入图片描述

易知二叉树的直径就是左、右子树的深度之和,故我们定义dfs求树的深度,再定义全局变量ans记录最长直径,在dfs中根据每个结点的左、右深度来更新ans直径,最后调用dfs,即可返回ans。

func diameterOfBinaryTree(root *TreeNode) int {
    var dfs func(root *TreeNode) int
    //ans记录直径
    ans := 0
    //dfs是返回的子树最大深度,但在dfs中通过左右深度来修改当前遍历到的最长直径
    dfs = func(root *TreeNode) int {
        // 如果当前节点为空,则返回-1
        if root == nil {
            return -1
        }
        // 递归计算左右子树的深度,加上当前结点则深度+1
        left := dfs(root.Left) + 1
        right := dfs(root.Right) + 1
        // 如果左右子树深度之和大于当前最大直径,则更新最大直径
        ans = max(ans, left+right)
        // 返回当前子树的最大深度
        return max(left, right)
    }
    dfs(root)
    // 返回遍历过程中找到的最大直径
    return ans
}
func max(a,b int)int{if a>b{return a};return b}

算法示例二:LeetCode2246. 相邻字符不同的最长路径

给你一棵 树(即一个连通、无向、无环图),根节点是节点 0 ,这棵树由编号从 0 到 n - 1 的 n 个节点组成。用下标从 0 开始、长度为 n 的数组 parent 来表示这棵树,其中 parent[i] 是节点 i 的父节点,由于节点 0 是根节点,所以 parent[0] == -1 。
另给你一个字符串 s ,长度也是 n ,其中 s[i] 表示分配给节点 i 的字符。
请你找出路径上任意一对相邻节点都没有分配到相同字符的 最长路径 ,并返回该路径的长度。
在这里插入图片描述

因为本题不一定是二叉树,我们首先要利用parent数组转换为孩子数组children ,children[i]记录所有节点i的孩子数组。
之后循环枚举neighbors,并定义maxLen 表示遍历过的父、子间的最长路径,len 表示当前这一条路径长度,若i节点与邻居节点字符不同,则判断是否先用maxLen 更新 ans,再更新 maxLen 巧妙得到 第一大和第二大的值,将最大值与次大值相加则可。

func longestPath(parent []int, s string) int {
    n := len(parent)
    children := make([][]int, n) // 孩子列表,表示每个节点的孩子节点
    for i := 1; i < n; i++ { // 依据父亲节点填充孩子列表
        children[parent[i]] = append(children[parent[i]], i)
    }
    ans := 0 // 最长路径长度
    // 定义dfs函数,查找树从i节点向下的最长路径长度
    var dfs func(int) int 
    dfs = func(i int) int {
        maxLen := 0 // 记录从i向下最长的异色路径的长度
        for _, child := range children[i] {
            len := dfs(child) + 1 // 递归查找以孩子节点为起点,向下的路径长度,并累加上当前节点i
            if s[child] != s[i] { // 如果孩子节点颜色与当前节点不同
                ans = max(ans, maxLen+len) // 更新最长异色路径长度,将i到孩子节点+最长异色路径设置为新的最长路径长度
                maxLen = max(maxLen, len) // 记录最长异色路径的长度
            }
        }
        return maxLen // 返回从i向下最长的异色路径的长度
    }
    dfs(0) // 从根节点开始递归查找树中最长的异色路径长度
    return ans + 1 // 返回最长路径长度 +1,也就是路径上节点的数量 
}
func max(a,b int)int{if a>b{return a};return b}

算法示例三:LeetCode337. 打家劫舍 III

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。 除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。
如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
在这里插入图片描述

每个节点可选择偷或者不偷两种状态,根据题目意思,相连节点不能一起偷

  • 当前节点选择偷时,那么两个孩子节点就不能选择偷了
  • 当前节点选择不偷时,两个孩子节点只需要拿最多的钱出来就行(两个孩子节点偷不偷没关系)

故直接定义dfs,返回两个值,第一个为选当前结点的最高金额,第二个为不选当前结点的最高金额

class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        #返回两个值,第一个为选当前结点的最高金额,第二个为不选当前结点的最高金额
        def dfs(node :TreeNode)->(int,int):
            if node is None:
                return 0,0
            l_rob,l_not_rob=dfs(node.left)
            r_rob,r_not_rob=dfs(node.right)
            rob = l_not_rob + r_not_rob + node.val  # 选node结点
            not_rob = max(l_rob, l_not_rob) + max(r_rob, r_not_rob)  # 不选node结点
            return rob, not_rob
        return max(dfs(root))

算法练习一:LeetCode124. 二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
在这里插入图片描述

此题与上题基本一致,只是将所求值有直径改为路径上的结点和,故定义dfs返回当前结点左右子树的最长路径和,不过需要注意结点值可以为负数,则在dfs返回时需要判断路径和是否为负数,若是负数则返回0,表示该树不选择加入路径。

func maxPathSum(root *TreeNode) int {
    var dfs func(root *TreeNode) int
    // 将最大路径和初始化为负无穷
    ans := -math.MaxInt
    dfs = func(root *TreeNode) int {
        if root == nil {
            return 0
        }
        left := dfs(root.Left)
        right := dfs(root.Right)
        // 如果左右子树和当前节点的值之和大于当前最大路径和,则更新路径和ans
        ans = max(ans, left+right+root.Val)
        // 返回当前子树的最大路径和,若为负数则不能加入最大路径和,返回0
        return max(0, max(left, right)+root.Val)
    }
    dfs(root)
    // 返回遍历过程中找到的最大路径和
    return ans
}
func max(a,b int)int{if a>b{return a};return b}

算法练习二:LeetCode687. 最长同值路径

给定一个二叉树的 root ,返回 最长的路径的长度 ,这个路径中的 每个节点具有相同值 。 这条路径可以经过也可以不经过根节点。
两个节点之间的路径长度 由它们之间的边数表示。
在这里插入图片描述在这里插入图片描述

func longestUnivaluePath(root *TreeNode) (ans int) {
    var dfs func(*TreeNode) int
    dfs = func(node *TreeNode) int {
        if node == nil {
            return -1 // 下面 +1 后,对于叶子节点就刚好是 0
        }
        lLen := dfs(node.Left) + 1  // 左子树最大链长+1,在dfs内部可更新ans
        rLen := dfs(node.Right) + 1 // 右子树最大链长+1,在dfs内部可更新ans
        if node.Left != nil && node.Left.Val != node.Val {
            lLen = 0 // 链长视作 0
        }
        if node.Right != nil && node.Right.Val != node.Val {
            rLen = 0 // 链长视作 0
        }
        ans = max(ans, lLen+rLen) // 两条链拼成路径
        return max(lLen, rLen)    // 当前子树最大链长
    }
    dfs(root)
    return
}
func max(a, b int) int { if a < b { return b }; return a }

算法进阶一:LeetCode1617. 统计子树中城市之间最大距离

给你 n 个城市,编号为从 1 到 n 。同时给你一个大小为 n-1 的数组 edges ,其中 edges[i] = [ui, vi] 表示城市 ui 和 vi 之间有一条双向边。题目保证任意城市之间只有唯一的一条路径。换句话说,所有城市形成了一棵 树 。
一棵 子树 是城市的一个子集,且子集中任意城市之间可以通过子集中的其他城市和边到达。两个子树被认为不一样的条件是至少有一个城市在其中一棵子树中存在,但在另一棵子树中不存在。
对于 d 从 1 到 n-1 ,请你找到城市间 最大距离 恰好为 d 的所有子树数目。
请你返回一个大小为 n-1 的数组,其中第 d 个元素(下标从 1 开始)是城市间 最大距离 恰好等于 d 的子树数目。
请注意,两个城市间距离定义为它们之间需要经过的边的数目。2 <= n <= 15在这里插入图片描述

本题有较大难度,需要用到多种技巧,如下所示:

  1. 根据算法示例二LeetCode2246. 相邻字符不同的最长路径进行扩展,要根据给定的双向边edges数组得出neighbors邻居数组,即既要考虑子结点也要考虑父亲节点, neighbors[i]记录i节点的所有邻居节点,此时该所有的邻居数组组合起来,就能得到一颗树
  2. 根据示例一LeetCode543. 二叉树的直径继续扩展,本题给定选择的城市数组inSet,返回该数组组成的树的最长直径,并用visit记录所有遍历的节点
  3. 利用LeetCode78. 子集,采用回溯法选或不选的思路,从所有城市中选择出所有的城市子集
  4. 对比遍历城市节点visit与所选城市节点inSet,若两者长度不一样,则说明所选的城市并没有遍历完全,即inSet数组并不是一个连通树,因此不能加入ans中;若两者长度一样,则说明是连通树,才能加入ans中
func countSubgraphsForEachDiameter(n int, edges [][]int) []int {
    // neighbors[i]记录i节点的所有邻居节点
    neighbors := make([][]int, n)
    for _, e := range edges {
        x, y := e[0]-1, e[1]-1 // 编号改为从 0 开始
        neighbors[x] = append(neighbors[x], y)
        neighbors[y] = append(neighbors[y], x)
    }
    // dfs函数求以x为初始节点并深度优先遍历求树的直径,inSet数组记录目前选择的城市,vis数组记录节点是否被访问
    var inSet, vis [15]bool
    var diameter int
    var dfs func(int) int
    dfs = func(x int) (maxLen int) {
        vis[x] = true
        for _, neighbor := range neighbors[x] {
            if !vis[neighbor] && inSet[neighbor] {
                //深度优先遍历
                ml := dfs(neighbor) + 1
                diameter = max(diameter, maxLen+ml)
                maxLen = max(maxLen, ml)
            }
        }
        return
    }
    ans := make([]int, n-1)
    var f func(int)
    //递归函数f,采用回溯法,用ans记录从城市i到n间最大距离恰好为d的所有子树数目
    f = func(i int) {
        if i == n {
            for v, b := range inSet {
                if b {
                    //先初始化vis访问数组与选择的inSet数组组成的树的直径diameter
                    vis, diameter = [15]bool{}, 0
                    //以v为初始节点开始遍历求树的直径diameter,并直接break退出循环
                    dfs(v)
                    break
                }
            }
            //如果直径大于0,且所有选择的节点都可以被遍历到即是一个连通树,则将距离加入ans中
            if diameter > 0 && vis == inSet {
                //ans[0]表示长度为1的子树数目,故diameter-1
                ans[diameter-1]++
            }
            return
        }
        // 不选城市 i
        f(i + 1)
        // 选城市 i
        inSet[i] = true
        f(i + 1)
        inSet[i] = false // 恢复现场
    }
    f(0)
    return ans
}
func max(a, b int) int { if a < b { return b }; return a }

算法进阶二:LeetCode2538. 最大价值和与最小价值和的差值

给你一个 n 个节点的无向无根图,节点编号为 0 到 n - 1 。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。
每个节点都有一个价值。给你一个整数数组 price ,其中 price[i] 是第 i 个节点的价值。
一条路径的 价值和 是这条路径上所有节点的价值之和。
你可以选择树中任意一个节点作为根节点 root 。选择 root 为根的 开销 是以 root 为起点的所有路径中,价值和 最大的一条路径与最小的一条路径的差值。
请你返回所有节点作为根节点的选择中,最大 的 开销 为多少。
在这里插入图片描述

法一:

与进阶一类似,用neighbors[i] 记录第i个节点的邻居数组,dfs求以i为根节点的最大路径和,由于价值都是正数,因此价值和最小的一条路径一定只有一个点,「价值和最大的一条路径与最小的一条路径的差值」等价于「去掉路径的一个端点即叶子结点」,故ans直接用最大路径和dfs(i)-price[i],返回最大的ans即最大开销

func maxOutput(n int, edges [][]int, price []int) int64 {
    //建立邻居数组
    neighbors := make([][]int, n)
    for _, e := range edges {
        x, y := e[0], e[1] 
        neighbors[x] = append(neighbors[x], y)
        neighbors[y] = append(neighbors[y], x)
    }
    ans:=0
    vis:=[]int{}
    for i:=0;i<n;i++{
        vis=append(vis,0)
    }
    //dfs返回以i为根节点的最大路径和
    var dfs func(int)int
    dfs=func(i int)int{
        maxCost:=price[i]
        vis[i]=1
        for _,nxt:= range neighbors[i]{
            if vis[nxt]==0{
                vis[nxt]=1
                cost:=price[i]+dfs(nxt)
                maxCost=max(maxCost,cost)
                vis[nxt]=0
            }
        } 
        vis[i]=0
        return maxCost
    }
    for i:=0;i<n;i++{
        //如果i为叶子结点,则遍历i为根
        if len(neighbors[i])==1{
            ans=max(ans,dfs(i)-price[i])
        }
    }
    return int64(ans)
}
func max(a, b int) int { if a < b { return b }; return a }

法二:

上一种方法会提示超时,主要原因在于求ans时需要循环遍历以每个叶子结点为根的情况,但其实我们可以类比树的直径与算法示例三LeetCode337. 打家劫舍 III,对于每个结点,有两种情况为包括叶子结点与不包括叶子结点,而最终结果只能有一个包括叶子结点的情况,因此我们直接通过dfs返回 返回带叶子的最大路径和,不带叶子的最大路径和来一次求出ans,且上题中我们使用vis判断是否遍历结点,但其实我们只需要记录pre即前一个遍历的结点,遍历neighbors[i]时只需要不等于pre即可

func maxOutput(n int, edges [][]int, price []int) int64 {
	ans := 0
	neighbor := make([][]int, n)
	for _, e := range edges {
		x, y := e[0], e[1]
		neighbor[x] = append(neighbor[x], y)
		neighbor[y] = append(neighbor[y], x) // 建树
	}

	// 返回带叶子的最大路径和,不带叶子的最大路径和
	var dfs func(int, int) (int, int)
    //x表示当前结点,pre表示前一个遍历的结点
	dfs = func(x, pre int) (int, int) {
		p := price[x]
        //maxS1记录带叶子的最大路径和,maxS2记录不带叶子的最大路径和
		maxS1, maxS2 := p, 0
		for _, nxt := range neighbor[x] {
            //遍历x的邻居数组,如果nxt!=pre则更新maxS1, maxS2 
			if nxt != pre {
                //nxt作为当前结点,x作为pre前一个遍历结点
				s1, s2 := dfs(nxt, x)
				// 前面最大带叶子的路径和 + 当前不带叶子的路径和
				// 前面最大不带叶子的路径和 + 当前带叶子的路径和
				ans = max(ans, max(maxS1+s2, maxS2+s1))
				maxS1 = max(maxS1, s1+p)
				maxS2 = max(maxS2, s2+p) // 这里加上p是因为neighbor[x]包括pre外的元素,说明x 必不是叶子
			}
		}
		return maxS1, maxS2
	}
    //-1表示没有前一个遍历结点
	dfs(0, -1)
	return int64(ans)
}

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

算法进阶三:LeetCode1377. T 秒后青蛙的位置

给你一棵由 n 个顶点组成的无向树,顶点编号从 1 到 n。青蛙从 顶点 1 开始起跳。规则如下:
在一秒内,青蛙从它所在的当前顶点跳到另一个 未访问 过的顶点(如果它们直接相连)。
青蛙无法跳回已经访问过的顶点。
如果青蛙可以跳到多个不同顶点,那么它跳到其中任意一个顶点上的机率都相同。
如果青蛙不能跳到任何未访问过的顶点上,那么它每次跳跃都会停留在原地。
无向树的边用数组 edges 描述,其中 edges[i] = [ai, bi] 意味着存在一条直接连通 ai 和 bi 两个顶点的边。
返回青蛙在 t 秒后位于目标顶点 target 上的概率。与实际答案相差不超过 10-5 的结果将被视为正确答案。

本题与算法进阶一类似,不过更加简单,关键也在于利用edges建立邻居节点数组neighbors

func frogPosition(n int, edges [][]int, t int, target int) float64 {
    //邻居节点
    neighbors:=make([][]int,n+1)
    for _,e:=range edges{
        x,y:=e[0],e[1]
        neighbors[x]=append(neighbors[x],y)
        neighbors[y]=append(neighbors[y],x)
    }
    var dfs func(int,int,int)float64
    //dfs返回第j秒,从第i个节点到target的概率,pre则记录前一个遍历的结点
    dfs=func(i,j,pre int)float64{
        //判断边界条件
        if j==t&&i==target{
            return 1.0
        }else if j==t{
            return 0.0
        }
        //res记录返回的概率
        res:=float64(0)
        //cnt记录未被遍历的邻居点个数
        cnt:=0
        //循环遍历每个邻居节点
        for _,nxt:=range neighbors[i]{
            //判断是否被遍历过
            if nxt!=pre{
                cnt++
                res+=dfs(nxt,j+1,i)
            }
        }
        if cnt==0{
            return dfs(i,j+1,pre)
        }
        //除以可以跳到的点,由于青蛙从1开始跳,不能简单的除以len(neighbors[i])-1
        return res/float64(cnt)
    }
    return dfs(1,0,1)
}

算法进阶四:LeetCode2646. 最小化旅行的价格总和

现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组
edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。
每个节点都关联一个价格。给你一个整数数组 price ,其中 price[i] 是第 i 个节点的价格。给定路径的 价格总和
是该路径上所有节点的价格之和。 另给你一个二维整数数组 trips ,其中 trips[i] = [starti, endi] 表示您从节点
starti 开始第 i 次旅行,并通过任何你喜欢的路径前往节点 endi 。在执行第一次旅行之前,你可以选择一些 非相邻节点
并将价格减半。 返回执行所有旅行的最小价格总和。
在这里插入图片描述

利用cnt记录结点遍历次数,然后dfs返回每个结点减半与不减半两种情况的结果

func minimumTotalPrice(n int, edges [][]int, price []int, trips [][]int) int {
    neighbors := make([][]int, n)
    for _, e := range edges {
        x, y := e[0], e[1] 
        neighbors[x] = append(neighbors[x], y)
        neighbors[y] = append(neighbors[y], x)
    }
    cnt:=make([]int,n)
    for _,trip:=range trips{
        end:=trip[1]
        //for循环内部的dfs函数,用来记录路径进过结点的次数,且树只有唯一简单路径
        var dfs func(int,int)bool
        //i表示当前遍历结点,pre记录前一个遍历的结点
        dfs=func(i,pre int)bool{
            if i==end{
                cnt[i]++
                return true
            }
            for _,nxt:=range(neighbors[i]){
                if nxt!=pre&&dfs(nxt,i){
                    cnt[i]++
                    return true
                }
            }
            return false
        }
        dfs(trip[0],-1)
    }
    //dfs返回两个值,分别是当前结点折半与不折半的值
    var dfs func(int,int)(int,int)
    dfs=func(i,pre int)(int,int){
        mins1,mins2:=price[i]*cnt[i],price[i]*cnt[i]/2
        for _,nxt:=range(neighbors[i]){
            if nxt!=pre{
                s1,s2:=dfs(nxt,i)
                mins1+=min(s1,s2)// i未减半,那么nxt可以不减半,可以减半,取这两种情况的最小值
                mins2+=s1        //  i减半,那么nxt只能不减半
            }
        }
        return mins1,mins2
    }
    ans1, ans2 := dfs(0, -1)
	return min(ans1, ans2)
}
func min(a,b int)int{if a>b{return b};return a}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/608798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7使用docker compose部署ELK

说明&#xff1a;1、一定要先不要配置那么多配置文件&#xff0c;去除掉一些&#xff0c;先让docker compose启动相关服务能访问的时候&#xff0c;使用拷贝方法&#xff0c;把相关的配置文件拷贝出来在外面修改&#xff0c;这样保险一些&#xff0c;不然容易配置文件错误无法启…

90.构建 “工作流程 “第一部分

记得我们上次实现的页面的了么&#xff0c;如下图所示&#xff0c;这节我们接着来 记住我们之前的画的草图 现在我们就来构建Z字形的工作流程部分&#xff1b; ● 首先我们添加标题 工作流程 3个简单的步骤制作您每天的健康饮食 ● 接着就是添加Z字形的工作步骤 <div cl…

Visual Studio Code 下载安装教程(含必备插件)

文章目录 下载安装插件列表 下载 点击进入 VSCode 官网&#xff1a;https://code.visualstudio.com/&#xff0c;并点击右侧箭头&#xff0c;选择对应的版本&#xff0c;点击下载&#xff08;以Windows 版本为例&#xff09;&#xff1a; 等待下载完成&#xff1a; 安装 双击…

01人工智能导论概念总结

文章目录 第一章第六章第八章 第一章 人工智能&#xff1a;又称为机器智能&#xff08;Machine Intelligence&#xff0c;MI&#xff09;&#xff0c;主要研究用人工的方法和技术开发智能机器或智能系统&#xff0c;以模仿、延伸和扩展人类智能、生物智能、自然智能&#xff0…

Jenkins+RF持续集成测试(一) 环境搭建

通常在自动化测试中&#xff0c;我们需要对自动化测试用例定时构建&#xff0c;并生成报告并通过邮件发给指定的人。最佳工具选择莫过于Jenkins了。通过Jenkins集成robot framework插件&#xff0c;我们能非常方便的定时从git/svn上拉取最新的自动化测试用例&#xff0c;然后执…

MyBatis 学习笔记

MyBatis学习笔记 1、简介1.1、什么是MyBatis1.2、持久化1.3、持久层1.4、为什么需要MyBatis 2、MyBatis第一个程序2.1、搭建环境2.2、创建一个模块2.3、编写代码2.4、编写测试类 3、CRUD3.1、namespace3.2、select3.3、insert3.4、update3.5、delete3.6、分析错误3.7、万能Map3…

【内存操作函数】字符串函数不敢干的事,我干!

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C语言学习分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C语言知识   &#x1f51d;&#x1f51d; 内存操作函数 1. 前言&#x1f6a9…

操作系统复习2.4.0-死锁详解

什么是死锁 各进程互相竞争对手里的资源&#xff0c;导致各进程都阻塞&#xff0c;都无法向前推进 死锁、饥饿、死循环的区别 死锁&#xff1a;各进程互相持有对方想要的资源且不释放&#xff0c;导致各进程阻塞&#xff0c;无法向前推进 饥饿&#xff1a;由于长期得不到想要…

Chatbot + 知识库(Knowledge Base)

从 GPT-3 和 ChatGPT 等大型语言模型 (LLM) 的最新进展中可以看出&#xff0c;在技术行业引起了很大的关注。这些模型对内容生成非常强大&#xff0c;但它们也有一些缺点&#xff0c;例如偏差1 和幻象2。LLM在聊天机器人开发方面特别有用。 基于意图的聊天机器人​ 传统聊天机…

Redis中的压缩列表(ZipList)

前言 压缩列表的最大特点&#xff0c;就是它是一种内存紧凑型的数据结构&#xff0c;占用一块连续的内存空间&#xff0c;而且还会根据数据类型的不同&#xff0c;选择不同的编码方式来节省内存。 压缩列表的缺点也很明显 它查询节点只能一个一个查&#xff0c;所以时间复杂…

用springboot创建helloworld项目

目录 一、什么是springboot 二、使用idea构建springboot &#xff08;1&#xff09;下载idea &#xff08;2&#xff09;在idea配置maven &#xff08;3&#xff09;利用springboot构建1个helloworld的web项目​编辑​编辑 ​编辑 &#xff08;4&#xff09;启动springboot…

软考A计划-电子商务设计师-电子商务系统的测试

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

Zinx框架学习 - 消息队列及多任务

Zinx - V0.8 消息队列及多任务 之前zinxV0.7我们已经实现了读写分离&#xff0c;对应每个client&#xff0c;我们有3个go程&#xff0c;分别是reader、writer、DoMsgHandle假设服务器有10W个client请求&#xff0c;那么server就会有10W个reader的go、10W个writer的go程&#x…

python编程——环境搭建

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一 、安装python 1、进入官网下载python 2、打开安装…

【AI4DB】商用数据库-使用AI4DB技术并商用的数据库总结

目录 1.Amazon Redshift参考链接&#xff1a; 2.阿里云-DAS-Database Autonomy Service参考链接&#xff1a; 3.Oracle Autonomous Database参考链接&#xff1a; 4.阿里云-MaxCompute&#xff08;原ODPS&#xff09;参考文档&#xff1a; 5.腾讯云——DBbrain参考链接&#xf…

python 算符优先分析法的设计实现 编译原理

本文内容&#xff1a; 1、给出文法如下: G[E] E->T|ET; T->F|T*F; F->i|(E); 可以构造算符优先表如下: *()i><<><*>><><(<<<<)>>>i>>> 2、计算机中表示上述优先关系&#xff0c;优先关系的机内存放…

飞桨花滑骨骼点动作识别比赛——从 baseline 调优讲解

赛题介绍背景数据集 思路讲解backbone 模型文件结构 -- PaddleVideo 框架configs 文件夹paddlevideo 文件夹 模型介绍1. ST-GCN -- Baseline 模型整体结构GCN部分TCN部分 2. 2s-AGCN自适应图卷积双流网络 3. CTR-GCNCTR-GC 赛题介绍 背景 2021 CCF BDCI 基于飞桨实现花样滑冰…

初识JavaScript---(1)

初识JavaScript———&#xff08;1&#xff09;&#xff01;&#xff01;&#xff01; 一、初识JavaScript 1.什么是JavaScript&#xff1f; JavaScript是运行在浏览器上的脚本语言&#xff0c;简称JS。JavaScript程序不需要我们程序员手动编译&#xff0c;编写完源代码之后…

shell编程-02-变量作用域

作用域 局部变量&#xff1a;变量只能在函数内部使用 全局变量&#xff1a;变量可以在当前 Shell 进程中使用 环境变量&#xff1a;变量还可以在子进程中使用 局部变量 函数中定义的变量默认是全局变量&#xff0c;在定义时加上local命令&#xff0c;此时该变量就成了局部变…

Spring系列-10 事务机制

背景&#xff1a; 在 事务-1 事务隔离级别和Spring事务传播机制 中对事务的特性、隔离级别、Spring事务的传播机制结合案例进行了分析&#xff1b;在 事务-2 Spring与Mybatis事务实现原理 中对JDBC、Mybatis、Spring整合Mybatis实现事务的原理结合框架源码进行了介绍&#xff…