递归——前奏练习

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"))
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值