树的搜索
树的搜索 · SharingSource/LogicStack-LeetCode Wiki (github.com)
173. 二叉搜索树迭代器 (leetcode-cn.com)
其实我就是先中序遍历一遍…(偷懒了)
type BSTIterator struct {
result []*TreeNode
}
func Constructor(root *TreeNode) BSTIterator {
result := make([]*TreeNode, 0, 100000)
var dfs func(root *TreeNode)
dfs = func(root *TreeNode) {
if root == nil {
return
}
dfs(root.Left)
result = append(result, root)
dfs(root.Right)
}
dfs(root)
return BSTIterator{result: result}
}
func (this *BSTIterator) Next() (ret int) {
ret = this.result[0].Val
this.result = this.result[1:]
return
}
func (this *BSTIterator) HasNext() bool {
return len(this.result) > 0
}
331. 验证二叉树的前序序列化 (leetcode-cn.com)
就是先往左找再往右找边界,如果最后边界刚好是总长就ok
//获取边界的终点
func isOK(nums []string, index int) int {
if index >= len(nums) || index == -1 {
return -1
}
if nums[index] == "#" { //遇到终止就返回下一个起始点
return index + 1
}
return isOK(nums, isOK(nums, index+1))
}
func isValidSerialization(preorder string) bool {
nums := strings.Split(preorder, ",")
return isOK(nums, 0) == len(nums) //看看整个的终点是不是刚好是总长度
}
671. 二叉树中第二小的节点 - 力扣(LeetCode) (leetcode-cn.com)
找到一个第二小的点🤣(大小比最开始的点大,比其他点小)
func findSecondMinimumValue(root *TreeNode) int {
ans := -1
rootVal := root.Val
var dfs func(*TreeNode)
dfs = func(node *TreeNode) {
if node == nil || ans != -1 && node.Val >= ans {
return
}
if node.Val > rootVal {
ans = node.Val
}
dfs(node.Left)
dfs(node.Right)
}
dfs(root)
return ans
}
993. 二叉树的堂兄弟节点 (leetcode-cn.com)
用bfs即可
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
type Node struct{
parent *TreeNode
node *TreeNode
level int
}
func isCousins(root *TreeNode, x int, y int) bool {
queue := make([]*Node,0,101)
queue = append(queue,&Node{parent:nil,node:root,level:0})
isOk := 0
var x1,y1 *Node
for len(queue)>0 {
p := queue[0]
queue = queue[1:]
if p.node.Val == x{
x1 = p
isOk++
}
if p.node.Val == y{
y1 = p
isOk++
}
if isOk == 2{
break
}
if p.node.Left!=nil{
queue = append(queue,&Node{parent:p.node,node:p.node.Left,level:p.level+1})
}
if p.node.Right!=nil{
queue = append(queue,&Node{parent:p.node,node:p.node.Right,level:p.level+1})
}
}
return x1.level == y1.level && x1.parent != y1.parent
}
回溯算法
回溯算法 · SharingSource/LogicStack-LeetCode Wiki (github.com)
dfs模板
17. 电话号码的字母组合 - 力扣(LeetCode) (leetcode-cn.com)
这个就是把数字的每一位所代表的的字母进行枚举
func letterCombinations(digits string) (ret []string) {
if digits == ""{
return
}
letterMap := []string{
" ", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz", //9
}
result := make([]byte,len(digits))
var dfs func(step int)
dfs = func(step int){
if step >= len(digits){ //循环终止条件->数字每一位都枚举完了
ret = append(ret,string(result))
return
}
for _,v:=range letterMap[digits[step]-'0']{
result[step] = byte(v)
dfs(step+1)
}
}
dfs(0)
return
}
797. 所有可能的路径 (leetcode-cn.com)
这个有点像图的邻接表下的dfs
func allPathsSourceTarget(graph [][]int) (ret [][]int) {
n := len(graph)
res := make([]int,0,n)
res = append(res,0)
var dfs func(idx int)
dfs = func(idx int){
if idx == n-1 { //到达目的地终止
ret = append(ret,append([]int{},res...))
return
}
for _,v:=range graph[idx]{ //遍历连接的点
res = append(res,v)
dfs(v)
res = res[:len(res)-1]
}
}
dfs(0)
return
}
这个题有点像是迷宫问题的解法(dfs遍历就对了)
var next = [4][2]int{{0,1},{0,-1},{-1,0},{1,0}}
func exist(board [][]byte, word string) bool {
var dfs func(x,y,idx int,isOK [][]bool)bool
dfs = func(x,y,idx int,isOK [][]bool)bool{
if idx == len(word){
return true
}
for i:=0;i<4;i++{
nx := x+next[i][0]
ny := y+next[i][1]
if nx<0||ny<0||nx>=len(board)||ny>=len(board[nx])||isOK[nx][ny]||board[nx][ny]!=word[idx]{
continue
}
isOK[nx][ny] = true
if dfs(nx,ny,idx+1,isOK){
return true
}
isOK[nx][ny] = false
}
return false
}
for i,row := range board{ //遍历棋盘,从每个可行的点开始进行dfs
for j,v :=range row{
if v == word[0] {
isOK := make([][]bool,len(board)) //标记数组
for i:=range isOK{
isOK[i] = make([]bool,len(board[i]))
}
isOK[i][j] = true
if dfs(i,j,1,isOK){
return true
}
}
}
}
return false
}
排列组合
剑指 Offer 38. 字符串的排列 (leetcode-cn.com)
这个主要考察的是排列组合+去重
排列组合有点像啊哈算法上计算n张牌的全排列问题,有n个桶,每个桶放一张牌,遍历n个桶,在每个桶中遍历每张牌,如果有某张牌没有被使用就放到当前的桶中,知道所有的桶都被装满了就算一种放法.
而去重则一般对这种数组进行排序,然后进行判断
//如果前一个字符没有被遍历,且和此时的字符相等,则会出现重复(因为上一次已经用过这个组合了)
//ps:比如3个c(c0c1c2) 第一次:c0c1c2 第二次c0c2c1重复 第三次c1c0c2重复...
if b || j>0 && !vis[j-1] && t[j] == t[j-1] {
continue
}
func permutation(s string) (ans []string) {
n := len(s)
t := []byte(s)
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] }) //排序
vis := make([]bool, n) //标记数组
perm := make([]byte, 0, n)
var dfs func(int)
dfs = func(i int){
if i == n { //都放满了就放到结果李
ans = append(ans,string(perm))
return
}
for j,b := range vis {
if b || j>0 && !vis[j-1] && t[j] == t[j-1] { //去重
continue
}
vis[j] = true
perm = append(perm,t[j])
dfs(i+1)
perm = perm[:len(perm)-1]
vis[j] = false
}
}
dfs(0)
return
}
从n个数里挑几个使其满足条件的种类数
在面对每一个数的时候要么选,要么跳过.维持一个当前剩余值和当前面对的数,要么选择这个数(如果可以选,然后把剩余值减掉响应的部分),要么直接跳过.(因为都是不重复的数,不存在去重问题)
func combinationSum(candidates []int, target int) (ret [][]int) {
result := make([]int, 0, 510)
var dfs func(target, idx int)
dfs = func(target, idx int) {
if idx == len(candidates) { //到达边界,直接返回
return
}
if target == 0 { //凑出目标值,就加入结果
ret = append(ret, append([]int{}, result...))
return
}
if target-candidates[idx] >= 0 { //不跳过,看看可以选择不
result = append(result, candidates[idx])
dfs(target-candidates[idx], idx)
result = result[:len(result)-1]
}
dfs(target, idx+1) //跳过这个位置的数
}
dfs(target, 0)
return
}
另一种写法(和下面那个题保持一致)
func combinationSum(candidates []int, target int) (ret [][]int) {
result := make([]int, 0, 510)
var dfs func(target, idx int)
dfs = func(target, idx int) {
if target == 0 { //凑出目标值,就加入结果
ret = append(ret, append([]int{}, result...))
return
}
for i:=idx;i<len(candidates);i++ {
if target-candidates[i] >= 0 { //不跳过,看看可以选择不
result = append(result, candidates[i])
dfs(target-candidates[i], i)
result = result[:len(result)-1]
}
}
}
dfs(target, 0)
return
}
这个既要考虑去重,还得考虑组合
先排序来防止重复(感觉和前面的去重还不太一样,这个只要保证一个数使用一次就行),
func combinationSum2(candidates []int, target int) (ret [][]int) {
sort.Ints(candidates)
results := make([]int,0,len(candidates))
var dfs func(target,index int)
dfs = func(target,index int) {
if target == 0 {
ret = append(ret,append([]int{},results...))
return
}
for i:=index;i<len(candidates);i++ {
if i>index && candidates[i]==candidates[i-1] { //保证一个数只用一次
continue
}
if target-candidates[i]>=0 { //试探下可以用不
results = append(results,candidates[i])
dfs(target-candidates[i],i+1)
results = results[:len(results)-1]
}else { //因为从小到大排序,在这里可以剪枝下
break
}
}
}
dfs(target,0)
return
}
子集
类似上面第三个类,可以选,也可以不选,没有额外限制条件
func subsets(nums []int) (ret [][]int) {
result := make([]int, 0, len(nums))
var dfs func(index int)
dfs = func(index int) {
if index == len(nums) {
ret = append(ret, append([]int{}, result...)) //这种写法防止切片被后续修改
return
}
result = append(result, nums[index])
dfs(index + 1)
result = result[:len(result)-1]
dfs(index + 1)
}
dfs(0)
return
}
这个在上面一个的基础上,增加了去重
同样要先排序,然后去重,和排列组合那个挺像的
if !isChoose && index > 0 && nums[index] == nums[index-1] {
return
}
考虑数组 [1,2,2][1,2,2],选择前两个数,或者第一、三个数,都会得到相同的子集。
也就是说,对于当前选择的数 xx,若前面有与其相同的数 yy,且没有选择 yy,此时包含 xx 的子集,必然会出现在包含 yy 的所有子集中。
我们可以通过判断这种情况,来避免生成重复的子集。代码实现时,可以先将数组排序;迭代时,若发现没有选择上一个数,且当前数字与上一个数相同,则可以跳过当前生成的子集。
func subsetsWithDup(nums []int) (ret [][]int) {
sort.Ints(nums)
result := make([]int, 0, len(nums))
var dfs func(isChoose bool, index int)
dfs = func(isChoose bool, index int) {
if index == len(nums) {
ret = append(ret, append([]int{}, result...))
return
}
dfs(false, index+1)
if !isChoose && index > 0 && nums[index] == nums[index-1] {
return
}
result = append(result, nums[index])
dfs(true, index+1)
result = result[:len(result)-1]
}
dfs(false, 0)
return
}
分隔子串
先判断在哪个位置可以切,然后dfs下去,
func partition(s string) (ret [][]string) {
//先用dp计算回文子串用于后续判断用
isOK := make([][]bool,len(s))
for i:=range isOK{
isOK[i] = make([]bool,len(s))
isOK[i][i] = true
}
for i := len(isOK)-2;i>=0;i--{
for j:=i+1;j<len(isOK[i]);j++{
if s[i] == s[j] && ((j-1<i+1)||isOK[i+1][j-1]) {
isOK[i][j] = true
}
}
}
//dfs进行搜索
result := make([]string,0,len(s))
var dfs func(idx int)
dfs = func(idx int){
if idx == len(s) {
ret = append(ret,append([]string{},result...))
return
}
for j:=idx;j<len(s);j++ {
if isOK[idx][j] { //如果在这个idx位置可以分割,就dfs下去
result = append(result,s[idx:j+1])
dfs(j+1)
result = result[:len(result)-1]
}
}
}
dfs(0)
return
}
未完待续…