经典算法-动态规划(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(路径, 选择列表)
    撤销选择

 

posted @ 2020-03-31 20:36  天之草  阅读(679)  评论(0编辑  收藏  举报