经典算法-动态规划(golang)
package main import ( "fmt" ) type Tree struct { Data int LeftNode *Tree RightNode *Tree } func main() { //tree := []int{4, 2, 6, 1, 3, 5, 7} root := createTree() fmt.Println("pre:") preOrderTraverseTree(root) fmt.Printf("\nin: \n") inOrderTraverseTree(root) fmt.Printf("\npost: \n") postOrderTraverseTree(root) fmt.Println() fmt.Printf("\nbfs: \n") bfs(root) fmt.Printf("\ndfs: \n") dfs(root) fmt.Println() } func createTree() *Tree { var root *Tree = &Tree{4, nil, nil} root.LeftNode = &Tree{2, nil, nil} root.LeftNode.LeftNode = &Tree{1, nil, nil} root.LeftNode.RightNode = &Tree{3, nil, nil} root.RightNode = &Tree{6, nil, nil} root.RightNode.LeftNode = &Tree{5, nil, nil} root.RightNode.RightNode = &Tree{7, nil, nil} return root } // 先序 func preOrderTraverseTree(root *Tree) { if root == nil { return } fmt.Printf("--%d--", root.Data) preOrderTraverseTree(root.LeftNode) preOrderTraverseTree(root.RightNode) } // 中序 func inOrderTraverseTree(root *Tree) { if root == nil { return } inOrderTraverseTree(root.LeftNode) fmt.Printf("--%d--", root.Data) inOrderTraverseTree(root.RightNode) }
// 一直遍历到左子树最下边,边遍历边保存根节点到栈中 // cur为nil时,说明已经到达左子树最下边,这时需要出栈了 func inorderTraversal(root *TreeNode) []int { stack := make([]*TreeNode, 0) cur := root result := make([]int, 0) for len(stack) > 0 || cur != nil { for cur != nil { stack = append(stack, cur) cur = cur.Left } if len(stack) > 0 { node := stack[len(stack)-1] result = append(result, node.Val) stack = stack[:len(stack)-1] cur = node.Right } } return result }
// 后序 func postOrderTraverseTree(root *Tree) { if root == nil { return } postOrderTraverseTree(root.LeftNode) postOrderTraverseTree(root.RightNode) fmt.Printf("--%d--", root.Data) } // 队列实现bfs func bfs(root *Tree) { if root == nil { return } // for root 需要借助队列 var queue []*Tree queue = append(queue, root) for len(queue) > 0 { node := queue[0] fmt.Printf("--%d--", node.Data) if node.LeftNode != nil { queue = append(queue, node.LeftNode) } if node.RightNode != nil { queue = append(queue, node.RightNode) } queue = queue[1:] // 通过这样的方式达到出队列 } }
// 从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行 // 例如: 给定二叉树: [3,9,20,null,null,15,7], // 返回其层次遍历结果: // [ // [3], // [9,20], // [15,7] //] func levelOrder(root *TreeNode) [][]int { if root == nil { return [][]int{} } var nodeList [][]int var queue []*TreeNode queue = append(queue, root) for len(queue) > 0 { var temp []int length := len(queue) for i := 0; i < length; i++ { head := queue[0] temp = append(temp, head.Val) if head.Left != nil { queue = append(queue, head.Left) } if head.Right != nil { queue = append(queue, head.Right) } queue = queue[1:] } nodeList = append(nodeList, temp) } return nodeList }
func dfs(root *Tree) { if root == nil { return } dfs(root.LeftNode) fmt.Printf("--%d--", root.Data) dfs(root.RightNode) } // 求数组的最大连续子序列之和
// 最大和是dp[i-1]+a[i],即a[p]+...+a[i-1]+a[i]=dp[i-1]+a[i];
// 对于这两种情况可以得到状态转移方程dp[i]=max{a[i],dp[i-1]+a[i]}
func maxSum() int {
nums := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4}
sum, maxNum := nums[0], nums[0]
for i := 1; i < len(nums); i++ {
sum = max(sum+nums[i], nums[i])
maxNum = max(sum, maxNum)
}
return maxNum
}
// 最长递增(上升)子序列(Longest Increasing Subsequence,简写 LIS) // 示例 1: // // 输入:[10,9,2,5,3,7,101,18] // 输出:4 // 解释:最长的上升子序列是[2,3,7,101], 它的长度是4 // 状态转移方程:nums[j] < nums[i] -> dp[i] = max(dp[i], dp[j]+1)
// 有点抽象,需要将dp[j]理解成之前满足条件的上升序列 func longestCommonSubsequence3(nums []int) int { dp := make([]int, len(nums))
maxNum := 0 for i := 0; i < len(nums); i++ {
dp[i] = 1 for j := 0; j < i; j++ { if nums[j] < nums[i] { dp[i] = max(dp[i], dp[j]+1) } }
maxNum = max(dp[i], maxNum) }
return maxNum }
// 给出一个数组 A 求A[i] - A[j]最大,并且满足i>j func maxNum(data []int) int { maxnum := math.MinInt32 currentMin := math.MaxInt32 for _, v := range data { currentMin = min(currentMin, v) maxnum = max(maxnum, v-currentMin) } return maxnum }
func maxSum() int { nums := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4} var sum, max int for _, v := range nums { if sum + v > v { sum = sum + v } else { sum = v } if sum > max { max = sum } } return max } // 有n步台阶,一次只能上1步或2步,共有多少种走法 // 递归方式实现:分析高二数列递推方程: f(n) = f(n-2) + f(n-1) func ladderSum(num int) int {
if num <= 2 { return num } return ladderSum(num-2) + ladderSum(num-1)
} // 迭代方式实现:效率更高, a1,a2,a3为临时变量 func ladderSum(num int) int {
if num <= 2 { return num } a1, a2, sum := 1, 2, 0 for i := 3; i <= num; i++ { sum = a1 + a2 a1, a2 = a2, sum } return sum }
//for i in [1..N]:
// for w in [1..W]:
// dp[i][w] = max(
// 把物品 i 装进背包,
// 不把物品 i 装进背包
// )
//return dp[N][W]
//for 状态1 in 状态1的所有取值:
// for 状态2 in 状态2的所有取值:
// for ...
// dp[状态1][状态2][...] = 择优(选择1,选择2...)
// 硬币问题:若只使用coins中的前i个硬币的面值,若想凑出金额j,有dp[i][j]种凑法。
// var maxW, maxV int
// var weight = []int{2, 2, 4, 6, 3}
// var value = []int{3, 4, 8, 9, 6}
// var n = 5 // 物品个数
// var w = 9 // 背包承受的最大重量
// 0-1背包,求最大价值:动态规划
// n为物品个数 ,w为背包承受的最大重量
// 0-1背包,求最大价值:动态规划
func dpbeibao(weight []int, n, w int) int { dp := make([][]int, n) for i := 0; i < n; i++ { dp[i] = make([]int, w+1) } // i代表个数,j代表重量 for i := 1; i <= n; i++ { for j := 0; j < w; j++ { if weight[i-1] > j { dp[i][j] = dp[i-1][j] } else { dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]) } } } return dp[n][w] //// 简化版 //dp := make([]int, w+1) //for i := 0; i < n; i++ { // 正序会重复选择物品,相当于物品数量无限(例如找零钱) // 倒序不会重复拿物品,相当于每个物品只有一个。 // for j := w; j >= 0; j-- { // if j-weight[i] >= 0 { // dp[j] = max(dp[j], dp[j-weight[i]] + value[i]) // } // } //} //return dp[w] } // 示例 1: // 输入: coins = [1, 2, 5], amount = 11 //输出: 3 //解释: 11 = 5 + 5 + 1 // 示例 2: // 输入: coins = [2], amount = 3 //输出: -1 // 说明: //你可以认为每种硬币的数量是无限的。 // 零钱兑换 (leetcode322) func coinChange(coins []int, amount int) int { dp := make([]int, amount+1) dp[0] = 0 for i := 1; i <= amount; i++ { dp[i] = math.MaxInt32 } for i := 1; i <= amount; i++ { for _, coinValue := range coins { if coinValue <= i { dp[i] = min(dp[i], dp[i-coinValue]+1) // } } } if dp[amount] == math.MaxInt32 { return -1 } return dp[amount] } // 股票买卖(只能操作一次) func maxProfit(prices []int) int { //// 暴力破解法 //max := 0 //for i := 1; i < len(prices); i++ { // for j := 0; j < i; j++ { // value := prices[i] - prices[j] // if value > max { // max = value // } // } //} //return max //if len(prices) == 0 { // return 0 //} // //// 二维数组dp //dp := make([][]int, len(prices)) //for i := 0; i < len(prices); i++ { // dp[i] = make([]int, 2) //} //dp[0][0], dp[0][1] = 0, -prices[0] //// 转移方程 //for i := 1; i < len(prices); i++ { // dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) // 不持有股票 // dp[i][1] = max(dp[i-1][1], -prices[i]) // 持有股票 //} //return dp[len(prices)-1][0] // 再次简化(最优) dp_i_0, dp_i_1 := 0, -prices[0] for i := 0; i < len(prices); i++ { //dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]) //dp[i][1] = max(dp[i-1][1], -prices[i]) dp_i_1 = max(dp_i_1, -prices[i]) } return dp_i_0 } // 股票买卖(可以操作无数次) func maxProfit(prices []int) int { if len(prices) < 2 { return 0 } sum := 0 for i := 1; i < len(prices); i++ { if prices[i] > prices[i-1] { sum += prices[i] - prices[i-1] } } return sum }
// 编辑距离 func minDistance(word1 string, word2 string) int { //// 递归法 //var dp func(i,j int) int //dp = func (i, j int) int { // if i == -1 { // return j + 1 // } // if j == -1 { // return i + 1 // } // if word1[i] == word2[j] { // return dp(i - 1, j - 1) // } else { // return min( // dp(i, j-1) + 1, // dp(i-1, j) + 1, // dp(i-1, j-1) + 1, // ) // } // //} //return dp(len(word1)-1, len(word2)-1) // 动态规划 //i指向word1,j指向word2 //若当前字母相同,则dp[i][j] = dp[i - 1][j - 1]; //否则取增删替三个操作的最小值 + 1, 即: //dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 dp := make([][]int, len(word1)+1) for i := 0; i < len(dp); i++ { dp[i] = make([]int, len(word2)+1) } for i := 0; i <= len(word1); i++ { dp[i][0] = i } for j := 0; j <= len(word2); j++ { dp[0][j] = j } for i := 1; i <= len(word1); i++ { for j := 1; j <= len(word2); j++ { if word1[i-1] == word2[j-1] { dp[i][j] = dp[i-1][j-1] } else { dp[i][j] = min3( dp[i-1][j], dp[i][j-1], dp[i-1][j-1], ) + 1 } } } return dp[len(word1)][len(word2)] }
// 最小路径 n*n的矩阵(从左上角1到右下角3) // 0 1 2 3 //0 1 3 5 9 //1 2 1 3 4 //2 5 2 6 7 //3 6 8 4 3 func minDistBT(matrix [][]int, n int) int { states := [n][n]int{} sum := 0 for j := 0; j < n; j++ { sum += matrix[0][j] states[0][j] = sum // 初始化最上边横排递增累加数值 } sum = 0 for i := 0; i < n; i++ { sum += matrix[i][0] states[i][0] = sum // 初始化最左边竖排递增累加数值 } for i := 1; i < n; i++ { for j := 1; j < n; j++ { states[i][j] = matrix[i][j] + min(states[i][j-1], states[i-1][j]) } } return states[n-1][n-1] }
// 1、最长公共子串 // 例如 str1 = "helloworld" str2 = "loop" 最长公共子串是"lo" // 状态转移方程:dp[i][j] = // 1、 0 , a[i] != b[j] // 2、 dp[i-1][j-1] + 1, a[i] == b[j] func longestCommonSubsequence1(str1, str2 string) int { dp := make([][]int, len(str1)+1) for i := 0; i < len(dp); i++ { dp[i] = make([]int, len(str2)+1) } res := 0 for i := 1; i <= len(str1); i++ { for j := 1; j <= len(str2); j++ { if str1[i-1] == str2[j-1] { dp[i][j] = dp[i-1][j-1] + 1 res = max(res, dp[i][j]) } } } return res }
// 给定一个数组arr,返回arr的最长无的重复子串的长度 // [2,2,3,4,3,9] 结果为3 [2,3,4] func maxLength( arr []int ) int { // write code here m := make(map[int]int) start, maxNum := 0, 0 for k, v := range arr { if _, ok := m[v]; ok { start = max(start, m[v]+1) // 找到起始点 } m[v] = k maxNum = max(maxNum, k-start+1) //每次截取不重复的区间 } return maxNum }
// Leetcode 340. 至多包含 K 个不同字符的最长子串 典型的滑动窗口双指针技巧 // 示例1: 输入: s = "eceba", k = 2 输出:3 即"ece" func longestStr(s string, k int) int { m := make(map[byte]int) start, count, maxLen := 0, 0, 0 for i := 0; i < len(s); i++ { if _, ok := m[s[i]]; !ok { // 右指针后移一直找不重复的 count++ } m[s[i]]++ // 记录字符出现次数 for count > k { m[s[start]]-- if m[s[start]] == 0 { count-- } start++ // 记录字符出现次数 } maxLen = max(maxLen, i-start+1) } return maxLen }
// 求最短路径dijkstra
// https://www.jianshu.com/p/ff6db00ad866
func dijkstra() {
}
// A*算法 f(n) = g(n) + h(n) 当h(n)=0的时候,相当于dijkstra算法,当g(n)=0的时候,相当于贪心算法
// 高中数学通项式:
// F(1) = 0;
// F(2) = 1;
// F(n) = 4*F(n-1) - 5*F(n-2)
############################################### 回溯法 ##########################################################
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择