Algorithm4---BinaryTree

文章目录

    • BinarySearchTree
      • 二叉树遍历:
      • 平衡二叉树实现原理
      • 红黑树
    • 1、二叉树的锯齿形层次遍历(LeetCode 上第 103 号问题)
      • 题目描述
      • 题目分析
      • 代码实现
    • 2、完全平方数(LeetCode 第 279 号问题)
      • 题目描述
      • 题目分析
    • 3、员工的重要性( LeetCode 第 690 号问题)
      • 题目描述
      • 题目解析
      • 代码实现
    • 4、删除无效的括号(LeetCode 第 301 号问题)
      • 题目描述
      • 题目解析
      • 代码实现

  • 链表:链表在内存中的存储可以不是一段连续的区域
type ListNode struct {
	val int
	next ListNode
}
  • 树:二叉树,它其实是链表的一个延伸,树的遍历其实是有方向性的
type TreeNode struct {
	val int
	left TreeNode
	right TreeNode
}

图:图中的关系不再是 parent 和 children 的关系了,而是邻居的关系,这里也没有层级结构了,每个节点都是平等的。

type GraphNode struct {
	val int
	neithbors []int
}
  • 深度优先使用栈
  • 广度优先使用队列
  • 前、中、后序遍历

解决广度优先搜索问题,我们会使用队列这么一个FIFO的数据结构
用它解决哪些问题:

  • 层级遍历
  • 由点到面遍历图
  • 拓扑排序
  • 求最短路径

BinarySearchTree

  • 无序的顺序表,插入和删除效率还可以,查找慢;

  • 有序的顺序表,查找还行,插入和删除是个问题,那就就引入。。。动;

  • 动态查找表:在查找时插入或删除都比较高效。

  • BinarySearchTree 采取二叉链表作为二叉排序树(二叉搜索树)的存储结构。

  • 中序遍历二叉排序树可得到一个关键字的有序序列

  • 搜索,插入,删除的复杂度等于树高,O(log(n))

二叉树遍历:

  • 对于当前节点,先输出该节点,然后输出他的左孩子,最后输出他的右孩子
  • 对于当前结点,先输出它的左孩子,然后输出该结点,最后输出它的右孩子
  • 对于当前结点,先输出它的左孩子,然后输出它的右孩子,最后输出该结点
/*
[用list包实现栈、队列的push 和 pop](https://studygolang.com/articles/11932)

二叉树前序遍历(递归、非递归)
二叉树中序遍历(递归、非递归)
二叉树后序遍历(递归、非递归)
二叉树层次遍历(打印行号)
*/
package tree

import "fmt"

type Item interface {}

//节点
type Node struct {
	Value Item
	left *Node
	right *Node
}

//接下来实现以下方法
// 
type ItemBinarySearchTreer interface {
	Insert(key int, value Item)
	Min() *Item
	Max() *Item
	Search(key Item) bool
	InOrderTraverse(f func(item Item))
	PreOrderTraverse(f func(item Item))
	PostOrderTraverse(f func(item Item))
	String()
}
type ItermBinarySearchTree struct {
	Root *Node
}



// 先序
func (bst *ItermBinarySearchTree)PreOrderTraverse(f func(item Item)){
	preOrdertraver(bst.Root,f)
}
func preOrdertraver(n *Node, f func(item Item)){
	if n != nil{
		f(n.Value)
		preOrdertraver(n.left,f)
		preOrdertraver(n.right,f)
	}
}
//中序
func (bst *ItermBinarySearchTree)InOrderTraverse(f func(item Item)){
	inOrderTraverse(bst.Root, f)
}
func inOrderTraverse(n *Node, f func(item Item)){
	if n != nil{
		inOrderTraverse(n.left,f)
		f(n.Value)
		inOrderTraverse(n.right,f)
	}
}
//后序
func(bst *ItermBinarySearchTree)PostOrderTraverse(f func(item Item)){
	postOrdertraver(bst.Root,f)
}
func postOrdertraver(n *Node, f func(item Item)){
	if n != nil{
		postOrdertraver(n.left, f)
		postOrdertraver(n.right, f)
		f(n.Value)
	}
}

//非递归
type seqStack struct {
	data [100]*Node
	tag [100]int		 //后续 用
	top int				 //数组下标
}
//前序,非递归
func (bst *ItermBinarySearchTree)PreOrderTraverse1()(result []Item){
	return preOrdertraver1(bst.Root)
}
func preOrdertraver1(node *Node)(result []Item){
	var s seqStack
	s.top = -1
	if node == nil{
		panic("no data here")
	}else{
		for node != nil || s.top != -1{
			for node != nil{
				result = append(result,node.Value)
				s.top++
				s.data[s.top] = node
				node = node.left
			}
			s.top--
			node = s.data[s.top]
			node = node.right
		}
	}
}

//中序遍历,非递归
func (bst *ItermBinarySearchTree)InOrderTraverse1()(result []Item){
	return inOrderTraverse1(bst.Root)
}
func inOrderTraverse1(node *Node)(result []Item){
	var s seqStack
	s.top = -1
	if node == nil{
		panic("no data here")
	}else{
		for node != nil && s.top != -1{
			for node != nil {
				s.top++
				s.data[s.top] = node
				node = node.left
			}
			s.top--
			node = s.data[s.top]
			result = append(result,node.Value)
			node = node.right
		}
	}
	return
}

//后续,非递归
func (bst *ItermBinarySearchTree)PostOrderTraverse1()(result []Item){
	return postOrdertraver1(bst.Root)
}
func postOrdertraver1(node *Node)(result []Item){
	var s seqStack
	s.top = -1
	if node == nil{
		panic("no data here")
	}else{
		for node != nil || s.top != -1{
			for node != nil{
				s.top++
				s.data[s.top] = node
				s.tag[s.top] = 0
				node = node.left
			}
			if s.tag[s.top] == 0{
				node = s.data[s.top]
				s.tag[s.top] = 1
				node = node.right
			}else{
				for s.tag[s.top] == 1{
					node = s.data[s.top]
					s.top--
					result = append(result, node.Value)
					if s.top < 0{break}
				}
				node = nil
			}
		}
	}
	return
}

//广度优先排序  从左到右 从右到左都可以
import "fmt"
import "container/list"

type MyQueue struct {
	List *list.List
}

type BinaryTree struct {
	Value interface{}
	Left *BinaryTree
	Right *BinaryTree
}

func (queue *MyQueue) pop() interface{} {
	if elem := queue.List.Front(); elem != nil {
		queue.List.Remove(elem)
		return elem.Value
	}
	return nil
}

func (queue *MyQueue) push(elem interface{}) {
	queue.List.PushBack(elem)
}
func levelOrder(node *BinaryTree) {
	var nlast *BinaryTree
	last := node
	level := 1
	queue := MyQueue{List: list.New()}

	fmt.Println(fmt.Sprintf("-----this is %d level-----", level))
	queue.push(node)
	for queue.List.Len() > 0 {
		node := queue.pop().(*BinaryTree)

		if node.Left != nil {
			queue.push(node.Left)
			nlast = node.Left
		}

		if node.Right != nil {
			queue.push(node.Right)
			nlast = node.Right
		}

		fmt.Println(node.Value)
		if last == node && (node.Left != nil || node.Right != nil) {
			last = nlast
			level++
			fmt.Println()
			fmt.Println(fmt.Sprintf("-----this is %d level-----", level))
		}
	}
}

//最小值,根据二叉搜索树的特性,最小值在树的最左边
func (bst *ItermBinarySearchTree)Min()*Item{
	n := bst.Root
	if n == nil{return nil}
	for {
		if n.left == nil{return &n.Value}
		n = n.left
	}
}
//最大值,根据二叉搜索树的特性,最大值在树的最右边
func (bst *ItermBinarySearchTree)Max()*Item{
	n := bst.Root
	if n == nil{return nil}
	for{
		if n.right == nil{return &n.Value}
		n = n.right
	}
}
/*
二叉搜索树,查找元素
指针f指向n的双亲,其初始值为nil
若查找成功,p指向该数据元素节点,并返回True
否则p指向查找路径上访问的最后一个节点并返回False
*/
func (bst *ItermBinarySearchTree)Search(key Item)bool{
	return search(bst.Root,key)
}
func search(n *Node, key Item)bool{
	if n == nil{
		return false
	} else if key == n.Value{
		return true
	} else if key < n.Value{
		return search(n.left,key)
	} else if key > n.Value{
		return search(n.right,key)
	}
}
// 二叉搜索树,插入元素
func (bst *ItermBinarySearchTree)Insert(key int,value Item){
	n := &Node{key,value,nil,nil}

	if bst.Root == nil{
		bst.Root = n
	}else {
		insertNode(bst.Root,n)
	}
}
// 递归插入
func insertNode(node ,newNode *Node){
	if newNode.Value < node.Value{
		if node.left == nil{
			node.left = newNode
		}else {
			insertNode(node.left,newNode)
		}
	}else if newNode.Value > node.Value{
		if node.right == nil{
			node.right = newNode
		}else {
			insertNode(node.right,newNode)
		}
	}
}
// 非递归 插入
func insertNode1(node ,key Item)bool{
	var s,p *Node 
	if (!search(node,key,nil,p)){
		s.Value = key
		s.left = s.right =nil
		if (!p){
			node = s				//插入的s为新的根节点
		}else if key < p.Value{
			p.left = s				
		}else {
			p.right = s
		}
		return true
	}else{
		return false		      	//树中已有关键字相同的节点,不再插入
	}
}
//二叉搜索树,删除元素
func (bst *ItermBinarySearchTree)Delete(key Item)bool{
	return delete(bst.Root,key)
}
func deleteBST(n *Node, key Item)bool{
	if n == nil{
		return false
	}else if key == n.Key{
		return delete(n,key)
	}else if key < n.Key{
		return deleteBST(n.left,key)
	}else if key > n.Key{
		return deleteBST(n.right,key)
	}
}
/*
 删除节点p,并重接它的左或右子树
 1、叶子节点
 2、仅左或右子树的节点
 3、左右节点都有,递归方式对二叉排序树T查找key,找到时删除
 */
func delete(p *Node)bool{
	var q,s *Node{}
	if *p.right == nil{     			//右子树为空只需重接它的左子树
		*p = *p.left
	}else if *p.left == nil{			//左子树为空只需重接它的右子树
		*p = *p.right
	}else{								//左右子树均为空
		q = p
		s = *p.left
		for s.right{					//左转,然后向右到尽头
			q = s
			s = s.right
		}
		*p.Value = *s.Value
		if q != p{
			q.right = s.left			//重接q的右子树
		}else{
			q.left = s.left				//重接p的左子树
		}
	}
	return true
}
// stringfy
func(bst *ItermBinarySearchTree)Sting(){
	fmt.Println("----------------")
	stringfy(bst.Root,0)
	fmt.Println("----------------")
}

func stringfy(n *Node, level int){
	if n != nil{
		format := ""
		for i:=0;i<level;i++{
			format += "          "
		}
		format += "-----["
		level ++
		stringfy(n.left,level)
		fmt.Println(n.Value)
		stringfy(n.right,level)
	}
}

平衡二叉树实现原理

package main

//Blance Factor 平衡因子=节点的左子树深度-节点的右子树深度
/*
平衡二叉树实现原理:在构建二叉排序树中,每当有一个节点插入时,
检查平衡性,找出最小不平衡树
保证二叉排序特性,调整不平衡树中各节点间的链接关系
 */

/*
旋转
最小不平衡树的根节点与它的子节点符号都是相同的、
不统一就把它们先转到符号统一,再旋转
 */
type Status bool
type Item interface {
}
type BiTNode struct{
	data Item
	bf int
	lchild *BiTNode
	rchild *BiTNode
}

/*
对以p为根的二叉排序树做右旋转处理
处理之后p指向新的树根节点,即旋转处理之前的左子树的根节点
*/
func RRotate(p *BiTNode){
	var L BiTNode
	L = *p.lchild
	*p.lchild = L.rchild
	L.rchild = *p
	*p = L
}
/*
对以p为根的二叉排序树做左旋转处理
处理之后p指向新的树根节点,即旋转处理之前的右子树的根节点
*/
func LRotate(p *BiTNode){
	var R BiTNode
	R = *p.rchild
	*p.rchild = R.lchild
	R.lchild = *p
	*p = R
}
/*
平衡旋转
 */
const LH  = 1
const EH  = 0
const RH  = -1

/*
对以p为根的二叉排序树做左旋转处理
处理之后p指向新的树根节点,即旋转处理之前左子树的右子树的根节点
 */
func LBalanceRotate(p *BiTNode){
	var L,Lr BiTNode
	L = *p.lchild
	switch L.bf {
		case LH:
			*p.bf = L.bf = EH
			RRotate(p)
			break
		case RH: //新节点插在p左子树的右子树上,要做双旋转处理
			Lr = L.rchild //Lr表示p左子树的右子树
			switch Lr.bf { //修改p及其左孩子的平衡因子
			case LH:
				*p.bf = RH
				L.bf = EH
				break
			case EH:
				*p.bf = L.bf = EH
				break
			case LH:
				*p.bf = EH
				L.bf = LH
				break
			}
			Lr.bf = EH
			LRotate(*p.lchild)
			RRotate(p)
	}

}
/*
对以p为根的二叉排序树做右旋转处理
处理之后p指向新的树根节点,即旋转处理之前的右子树的左子树根节点
*/
func RBalanceRotate(p *BiTNode){
	var R,Rl BiTNode
	R = *p.lchild
	switch R.bf {
	case LH:
		*p.bf = R.bf = EH
		RRotate(p)
		break
	case RH:
		Rl = R.rchild
		switch Rl.bf {
		case RH:
			*p.bf = RH
			R.bf = EH
			break
		case EH:
			*p.bf = R.bf = EH
			break
		case LH:
			*p.bf = EH
			R.bf = LH
			break
		}
		Rl.bf = EH
		RRotate(*p.rchild)
		LRotate(p)
	}

}
/*
若插入不存在和e有相同关键字的节点,则插入
成功返回1 失败返回0
若导致失衡,则做平衡处理,布尔变量taller反应p长高与否
 */

func InsertAVL(p *BiTNode,e Item, taller *Status)bool{
	if (!*p){
		//树中存在和e相同关键字的节点
		p := BiTNode{e,EH,nil,nil}
		*taller = true
	}else{
		if (e == *p.data){
			*taller = false
			return false
		}
		if (e < *p.data){
			//需在p的左子树中进行搜索
			if (!InsertAVL(*p.lchild,e,taller)){
				return false
			}
			if taller{//已插入到p的左子树
				switch *p.bf {
				case LH: //原本左子树比右子树高
					LBalanceRotate(p)
					*taller = false
					break
				case EH: //原本左右子树等高
					*p.bf = LH
					*taller = true
					break
				case RH: //原本右子树比左子树高
					*p.bf = EH
					*taller = true
					break
				}
			}
		}else{
			//需在p的右子树中进行搜索
			if (!InsertAVL(*p.rchild,e,taller)){
				return false
			}
			if taller{//已插入到p的右子树
				switch *p.bf {
				case LH: //原本左子树比右子树高
					*p.bf = EH
					*taller = true
					break

				case EH: //原本左右子树等高
					*p.bf = RH
					*taller = true
					break
				case RH: //原本右子树比左子树高
					LBalanceRotate(p)
					*taller = false
					break
				}
			}
		}
	}
	return true
}

红黑树

【一篇看懂红黑树】

1、二叉树的锯齿形层次遍历(LeetCode 上第 103 号问题)

题目描述

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

题目分析

我们需要记录每一层的信息,但是记录的顺序有区分
tricky,都从左向右记录,但是记录方式不一样,一种记录方式是从列表的尾部加入,另一种是从链表的头部加入

代码实现

type TreeNode struct {
	Val int
	Left *TreeNode
	Right *TreeNode
}
//递归
func ZigzagLevelOrder(root *TreeNode)[][]int{
	if root == nil{
		return nil
	}
	res := make([][]int,0,10)
	var dfs func(root *TreeNode,level int)
	dfs = func(root *TreeNode,level int){
		if root == nil{return}
		if level >= len(res){
			res = append(res,[]int{})
		}
		if level&1 == 0{
			res[level] = append(res[level],root.Val)
		}else{
			res[level] = append([]int{root.Val},res[level]...)
		}
		dfs(root.Left,level+1)
		dfs(root.Right,level+1)
	}
	dfs(root,0)
	return  res
}	
//非递归
func ZigzagLevelOrder(root *TreeNode)[][]int{
	result := make([][]int,0,10)
	if root == nil{
		return result
	}
	queue := list.New()
	queue.PushBack(root)
	isReverse := false
	for queue.Len() != 0{
		size := queue.Len()
		curLevel := []int{}
		for i:=0; i<size; i++{
			cur := queue.Front().Value
			curr := cur.(TreeNode)
			if curr.Left != nil{
				queue.PushBack(curr.Left)
			}
			if curr.Right != nil{
				queue.PushBack(curr.Right)
			}
			if isReverse{
				curLevel = append([]int{curr.Val},curLevel...)
			}else{
				curLevel = append(curLevel,curr.Val)
			}
		}
		isReverse = !isReverse
		result = append(result,curLevel)
	}
	return result
}

2、完全平方数(LeetCode 第 279 号问题)

题目描述

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

题目分析

使用广度优先搜索方法,将 n 依次减去比 n 小的所有平方数,直至 n = 0 ,此时的层数即为最后的结果。

3、员工的重要性( LeetCode 第 690 号问题)

题目描述

输入: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1
输出: 11
解释:
员工 1 自身的重要度是 5,他有两个直系下属 23 ,而且 23 的重要度均为 3 。因此员工 1 的总重要度是 5 + 3 + 3
= 11。

•一个员工最多有一个直系领导,但是可以有多个直系下属
•员工数量不超过 2000

题目解析

利用哈希表来存储员工的信息,找到指定 id 的员工后,采用广度优先遍历算法来遍历编号为 id 的员工及其下属员工

代码实现

4、删除无效的括号(LeetCode 第 301 号问题)

题目描述

删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果。

输入: "(a)())()"
输出: ["(a)()()", "(a())()"]

题目解析

所谓有效的括号,那么字符串中的左右括号数应该相同,而且每个右括号左边一定有其对应的左括号。这里很容易想到使用一个栈来模拟匹配过程,若栈为空说明该串是符合题意的。

  • 从前往后遍历可以删除不符合的’)'括号
  • 从后往前历可以删除不符合的’('括号
    通过BFS不断的对队列的字符串进行checkLeft 和 checkRight 操作,若遇到true,则说明当前的字符串已经是删除最小无效的括号的最优解了,接着就对队列中的其它字符串进行check即可

代码实现

你可能感兴趣的:(arithmetic)