package dynamicProgram
import (
"fmt"
"testing"
"time"
)
/*
暴力递归
暴力递归的过程就是尝试
1.把问题转化为规模缩小了的同类问题的子问题
2.有明确的不需要继续进行递归的条件(base case)
3.有当得到了子问题的结果之后的决策过程
4.不记录每一个子问题的解 记录则为动态规划
*/
/*
熟悉什么叫尝试
。打印n层汉诺塔从最左边移动到最右边的全部过程
。打印一个字符串的全部子序列
。打印一个字符串的全部子序列,要求不要出现重复字面值的子序列
。打印一个字符串的全部排列
。打印一个字符串的全部排列,要求不要出现重复排列
*/
/*
| | |
--- | |
----- | |
------- | |
左 中 右
主视图
小压大的次序将左侧柱子上的盘子移动的右侧的柱子上去,可以借助中间的柱子
O O
O
俯视图
1. 1 ~ N-1 左 --> 中
2. N 左 --> 右
3. 1 ~ N-1 中 --> 右
*/
func hanoi1(n int) {
leftToRight(n)
}
// 把1~N层圆盘,从左 -> 右
func leftToRight(n int) { //1-n层全部圆盘
if n == 1 { //base case 只剩一个
fmt.Println("Move 1 from left to right")
return
}
leftToMid(n - 1)
fmt.Println("Move", n , "from left to right")
midToRight(n - 1)
}
// 把1~N层圆盘,从左 -> 中
func leftToMid( n int) {
if n == 1 {
fmt.Println("Move 1 from left to mid")
return
}
leftToRight(n - 1)
fmt.Println("Move",n,"from left to mid")
rightToMid(n - 1 )
}
// 把1~N层圆盘,从 右 -> 中
func rightToMid(n int){
if n == 1 {
fmt.Println("Move 1 from right to mid")
return
}
rightToLeft(n - 1)
fmt.Println("Move",n,"from right to mid")
leftToMid(n - 1)
}
func midToRight(n int) {
if n == 1 {
fmt.Println("Move 1 fromo mid to right")
return
}
midToLeft(n - 1)
fmt.Println("Move",n,"from mid to right")
leftToRight(n - 1)
}
func rightToLeft(n int) {
if n == 1 {
fmt.Println("Move 1 from right to left")
return
}
rightToMid(n - 1)
fmt.Println("Move",n,"from right to left")
midToLeft(n - 1)
}
func midToLeft(n int) {
if n == 1 {
fmt.Println("Move 1 from mid to left")
return
}
midToRight(n - 1)
fmt.Println("Move",n,"from mid to left")
rightToLeft(n - 1)
}
func TestHanoi1(t *testing.T) {
hanoi1(3)
}
/*
1. 1~N-1 from -> other
2. N from -> to
3. 1~N-1 other -> to
*/
func hanoi2(n int) {
if n > 0 {
hanoi(n,"left","right","mid")
}
}
func hanoi(N int, from, to, other string) {
if N == 1 {
fmt.Println("Move 1 from",from,"to",to)
return
}else {
hanoi(N - 1,from , other,to)
fmt.Println("Move",N,"from",from,"to",to)
hanoi(N-1,other,to,from)
}
}
func TestHanoi2(t *testing.T) {
hanoi2(10)
}
/*
3层为例
1. 1,2 左 --> 中 | 1 左 --> 右
| 2 左 --> 中
| 1 右 --> 中
2. 3 左 --> 右
3. 1,2 中 --> 右 | 1 中 --> 左
| 2 中 --> 右
| 1 左 --> 右
N层汉诺塔最优解复杂度O(2^N-1)
*/
type Record struct {
finish bool
base int
from string
to string
other string
}
type RecordStack struct {
Elem []Record
}
func NewRecordStack() *RecordStack {
return &RecordStack{Elem: make([]Record,0)}
}
func (s *RecordStack)Push(val Record) {
s.Elem = append(s.Elem, val)
}
func (s *RecordStack)Pop() Record {
res := s.Elem[len(s.Elem)-1]
s.Elem = s.Elem[:len(s.Elem)-1]
return res
}
func (s *RecordStack)Peek() *Record {
return &s.Elem[len(s.Elem)-1]
}
func (s *RecordStack)IsEmpty() bool {
return len(s.Elem) == 0
}
func hanoi3( N int) {
if N < 1 {
return
}
stack := NewRecordStack()
stack.Push(Record{
finish: false,
base: N,
from: "left",
to: "right",
other: "mid",
})
for !stack.IsEmpty() {
cur := stack.Pop()
if cur.base == 1 {
fmt.Println("Move 1 from",cur.from,"to",cur.to)
if !stack.IsEmpty() {
stack.Peek().finish = true
}
}else {
if !cur.finish {
stack.Push(cur)
stack.Push(Record{
finish: false,
base: cur.base-1,
from: cur.from,
to: cur.other,
other: cur.to,
})
}else {
fmt.Println("Move",cur.base,"from",cur.from,"to",cur.to)
stack.Push(Record{
finish: false,
base: cur.base-1,
from: cur.other,
to: cur.to,
other: cur.from,
})
}
}
}
}
func TestHanoi3(t *testing.T) {
hanoi3(3)
}
/*
仰望好的尝试?
给你一个栈,请你逆序这个栈,
不能申请额外的数据结构
只能使用递归函数。如何实现?
*/
/*
第一步 实现去掉栈底,并返回的函数 int f(stack)
| 1 | | |
| 2 | | 1 |
| 3 | | 2 |
----- -----
f r = 1 , last = f2(stack)
f2 r = 2, last = f3(stack)
f3 r = 3 return
*/
type Stack []int
func (s *Stack)IsEmpty() bool {
return len(*s) == 0
}
func (s *Stack)Pop() int {
res := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
return res
}
func (s *Stack)Push(val int) {
*s = append(*s,val)
}
func reverse(stack *Stack) {
if stack.IsEmpty() {
return
}
i := f(stack)
reverse(stack)
stack.Push(i)
}
func f(stack *Stack) int {
result := stack.Pop()
if stack.IsEmpty() {
return result
}else {
last := f(stack)
stack.Push(result)
return last
}
}
func TestReverse(t *testing.T) {
stack := &Stack{}
stack.Push(100)
stack.Push(200)
stack.Push(300)
reverse(stack)
for !stack.IsEmpty() {
fmt.Println(stack.Pop())
}
}
func Test(t *testing.T) {
b := false
go func() {
for !b {
fmt.Println("1")
}
fmt.Println("2")
}()
go func() {
b = true
}()
time.Sleep(time.Second *100)
}
/*
二叉树可以通过先序,后序或者按层遍历的方式序列化和反序列化
二叉树无法通过中序遍历的方式实现序列化和反序列化
因为不同的两棵树,可能得到同样的中序序列,几遍补了空位置也可能一样
比如如下两棵树
2
---
/
1
和
1
---
\
2
补足位置的中序遍历结果都是{nil,1,nil,2,nil}
*/
/*
打印一个字符串的全部子序列
子串:必须连续的
abcd
0: a, ab, abc, abcd
1: b, bc, bcd
2: c, cd
3: d
for
for
子序列
原始序列中从左往右拿字符,可以不连续,要求相对次序不能变
abc
------------0------------
要a/ \ 不要a
-----1----- -----1-----
要b/ \不要b b要/ \b 不要
-2- -2- -2- -2-
c要 / \c不要 c要 / \c不要 c要 / \c不要 c要 / \c不要
abc ab ac a bc b c ""
*/
func subs(str string) []string {
var path string
path = ""
ans := &[]string{}
process1(str,0,ans,path)
return *ans
}
// str 固定不变
// index 此时来到的位置,要 or 不要
// ans 如果index 来到了 str 的终止位置,我要把我沿途路径所形成的的答案,放到ans里去
// 之前做出的选择,就是path
func process1(str string,index int, ans *[]string,path string) {
if index == len(str) {
*ans = append(*ans,path)
return
}
no := path
process1(str, index + 1, ans, no)
yes := path + string(str[index])
process1(str, index + 1, ans, yes)
}
func TestSubStr(t *testing.T) {
fmt.Println(subs("abcdefg")) //也是深度优先遍历
}
/*
打印一个字符串的全部子序列,要求不要出现重复字面值的子序列
所有的字符都不是一样的
如aaaa
*/
func process2(str string,index int, ans map[string]bool,path string) {
if index == len(str) {
ans[path] = true
return
}
no := path
process2(str, index + 1, ans, no)
yes := path + string(str[index])
process2(str, index + 1, ans, yes)
}
func subs2(str string) []string {
var path string
path = ""
ans := map[string]bool{}
process2(str,0,ans,path)
result := []string{}
for key, _ := range ans{
result = append(result, key)
}
return result
}
func TestSubs2(t *testing.T) {
fmt.Println(subs2("aaaaa"))
}
/*
所有不同字面值的子序列的个数,动态规划
*/
/*
打印一个字符串的全部排列
[a,b,c]
[0,1,2]
0a ->0 1b -> 0 2c ->0 谁来到0 位置 做决策
abc bac cba
1b ->1 2c ->1 谁来到1 位置 做决策,不要把已经放到0位置的 再调回来
abc acb
2c ->2 2b ->2
abc acb
1a->1 2c->1 1b ->b 2a ->1
bac bca cba cab
*/
func premution(str string) []string {
var res []string
if str == "" || len(str) == 0 {
return res
}
process([]byte(str),0,&res)
return res
}
//str[0...i-1]已经做好决定的
//str[i...]都有机会来到i位置
//i终止位置,str当前的样子就是一种结果 -> ans
func process(str []byte, i int,res *[]string) {
if i == len(str) {
*res = append(*res, string(str))
return //可加可不加
}
// 若果i 没到终止位置,从i 往后的所有位置都可以来到i位置(j,可以尝试来到i位置)
for j := i; j < len(str); j++ {
swap(str,i,j)
process(str,i+1,res)
swap(str,i,j) // 恢复现场,才能正确执行
}
}
func swap(str []byte,i,j int) {
str[i] , str[j] = str[j], str[i]
}
func TestPermutations(t *testing.T) {
fmt.Println(premution("abac"))
}
/*
打印一个字符串的全部排列 要求不出现重复的排列
分支限界
*/
func TestPremution2(t *testing.T) {
fmt.Println(premution2("abac"))
}
func premution2(str string) []string {
var res []string
if str == "" || len(str) == 0 {
return res
}
process_2([]byte(str),0,&res)
return res
}
//str[0...i-1]已经做好决定的
//str[i...]都有机会来到i位置
//i终止位置,str当前的样子就是一种结果 -> ans
func process_2(str []byte, i int,res *[]string) {
if i == len(str) {
*res = append(*res, string(str))
return // 可加可不加
}
visit := [26]bool{} //非共享,剪枝 --> 分支限定
// 若果i 没到终止位置,从i 往后的所有位置都可以来到i位置(j,可以尝试来到i位置)
for j := i; j < len(str); j++ {
if !visit[str[j]-'a'] {
visit[str[j]-'a'] = true
swap(str, i, j)
process_2(str, i+1, res)
swap(str, i, j) // 恢复现场,才能正确执行
}
}
}
// 用位图解决问题
func premution3(str string) []string {
var res []string
if str == "" || len(str) == 0 {
return res
}
process_3([]byte(str),0,&res)
return res
}
func process_3(str []byte, i int,res *[]string) {
if i == len(str) {
*res = append(*res, string(str))
return // 可加可不加
}
visit := 0 //非共享,剪枝 --> 分支限定
// 若果i 没到终止位置,从i 往后的所有位置都可以来到i位置(j,可以尝试来到i位置)
for j := i; j < len(str); j++ {
if visit >> (str[j]-'a') &1 == 0 {
visit |= 1 << (str[j]-'a')
swap(str, i, j)
process_3(str, i+1, res)
swap(str, i, j) // 恢复现场,才能正确执行
}
}
}
func TestPremution3(t *testing.T) {
fmt.Println(premution3("aaaa"))
}
递归——前奏练习
最新推荐文章于 2024-01-28 22:37:20 发布