zoukankan      html  css  js  c++  java
  • 树算法的GO语言实现

    树的遍历

    递推公式

    X序遍历,就先打印X,再打印左右
    前序遍历的递推公式:
    preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)
    中序遍历的递推公式(有序):
    inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)
    后序遍历的递推公式:
    postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r

    package algo
    
    import (
    	"fmt"
    	"testing"
    )
    
    type Node struct {
    	data int
    	left *Node
    	right *Node
    }
    
    func PreOrder(node *Node){
    	if node == nil{
    		return
    	}
    	fmt.Println(node.data)
    	PreOrder(node.left)
    	PreOrder(node.right)
    }
    
    //从小到大有序
    func InOrder(node *Node){
    	if node == nil {
    		return
    	}
    	//递归找到最左(最小的点)
    	InOrder(node.left)
    	fmt.Println(node.data)
    	InOrder(node.right)
    }
    
    func PostOrder(node *Node){
    	if node == nil{
    		return
    	}
    	PostOrder(node.left)
    	PostOrder(node.right)
    	fmt.Println(node.data)
    }
    
    func TestTree(t *testing.T){
    	tree := &Node{data:5}
    	tree.left = &Node{data:4}
    	tree.right = &Node{data:7}
    	tree.left.left = &Node{data:3}
    	PostOrder(tree)
    }
    
    func FindValue(root *Node,value int) *Node{
    	//以根节点为起点
    	currentNode := root
    	//叶节点指针为空
    	for currentNode != nil{
    		//数值相同,直接返回
    		if currentNode.data == value{
    			return currentNode
    			//数值比当前节点大,节点偏移到当前节点的右节点
    		}else if value > currentNode.data {
    			currentNode = currentNode.right
    		}else if value < currentNode.data {
    			currentNode = currentNode.left
    		}
    	}
    	//没有找到返回空指针
    	return nil
    }
    
    
    /**
    关键:
    1. 树是空的判断
    2. 插入位置节点是空的判断
     */
    func insert(tree *Node,value int){
    	//树是空的,直接把根节点指向新插入数据
    	if tree == nil{
    		tree = &Node{data:value}
    		return
    	}
    	currentNode := tree
    
    	//遍历到叶子节点
    	for currentNode != nil{
    		//如果新数据比当前节点大,并且右节点为空,则插入到右节点
    		if value > currentNode.data{
    			if currentNode.right == nil{
    				currentNode.right = &Node{data:value}
    				return
    			}
    			//右节点不为空,继续从右节点找插入位置
    			currentNode = currentNode.right
    		}else{
    			if currentNode.left == nil{
    				currentNode.left = &Node{data:value}
    				return
    			}
    			currentNode = currentNode.left
    		}
    	}
    }
    
    

    BFS

    • 图其实是树的一个变种
    • 从A出发,一层一层从左到右,从上往下
    1. 先遍历与A相邻的节点(B & C,顺序无要求,可以多种结果)
    2. 从左到右看和B & C相邻的节点
    3. 由于先遍历的是B,先求B相邻的节点(A C D),由于A C已经遍历过,只需要遍历D

    数据结构

    • 队列(A入队,A出队,并且把A相邻节点B C塞入队列。把B拿出来同时把B的相邻节点D塞入队列...可以保证先遍历B再遍历C的节点),用队列保证下一个遍历的元素是前面元素的顺序

    DFS

    • 从A出发,一条路走到底,再往回走
    1. 先找一个与A相邻的点(B or C)B
    2. 再找与B相邻的点(C or D)D
    3. 找与D相邻的点(C or E or F)F
    4. 发现F没有路,往回挑到前面一个点(D)
    5. 找D没有走过的点(C or E)E
    6. 从E找没有做过的点C
    7. C发现所有点已经走完(回跳)

    数据结构

    • 栈(A入栈,把栈里清空一个元素并把邻节点放入,C和B入栈,B出栈,把B的邻节点,D入栈...F没有邻节点,也要从栈拿出一个元素),用栈保证下一个拿的是上一个元素的邻节点
    package algo
    
    import (
    	"testing"
    )
    
    
    func BFS(graph map[string][]string,start string)[]string{
    	//初始化队列,起点入队
    	queue := []string{}
    	queue = append(queue, start)
    	//初始化访问集合,起点加入集合
    	visitedSet := map[string]bool{}
    	visitedSet[start] = true
    	//用一个切片记录走过的路径
    	bfs := []string{}
    	//循环,直到队列无元素(最后一个点)
    	for len(queue) > 0 {
    		//取出队头
    		vertex := queue[0]
    		queue = queue[1:]
    		
    		//记录路径
    		bfs = append(bfs, vertex)
    		
    		//取出队头的邻节点
    		neighbor := graph[vertex]
    		//队头的未访问的邻节点全部入队,并且放到访问集合
    		for _,node := range(neighbor){
    			if !visitedSet[node]{
    				queue = append(queue,node)
    				visitedSet[node] = true
    			}
    		}
    	}
    	return bfs
    }
    
    
    func DFS(graph map[string][]string,start string){
    	//建立栈,并且起点入栈
    	stack := []string{}
    	stack = append(stack,start)
    	//建立已访问集合,并且标记起点已访问
    	visitedSet := map[string]bool{}
    	visitedSet[start] = true
    	//循环,结束点是栈中没有元素
    	for len(stack) > 0 {
    		//取出栈顶(数组最后一个元素)
    		vertex := stack[len(stack)-1]
    		stack = stack[:len(stack)-1]
    		//取出栈顶元素的邻节点
    		neighbors := graph[vertex]
    		fmt.Println(vertex)
    		//循环邻节点的所有元素
    		for _,node := range(neighbors){
    			//判断元素,如果没有被访问,则入栈并标记为已访问
    			if !visitedSet[node]{
    				stack = append(stack,node)
    				visitedSet[node] = true
    
    			}
    		}
    	}
    }
    
    func TestBFS(t *testing.T){
    	graph := map[string][]string{}
    	graph["A"] = []string{"B","C"}
    	graph["B"] = []string{"A","C","D"}
    	graph["C"] = []string{"A","B","D","E"}
    	graph["D"] = []string{"B","C","E","F"}
    	graph["E"] = []string{"C","D"}
    	graph["F"] = []string{"D"}
    	bfs := BFS(graph,"A")
    	t.Log(bfs)
    }
    

    特点

    • 是完全二叉树,除了最后一层,数据都是满的,最后一层靠左
    • 节点的值大于或小于左右节点
    • 最大值在顶点的是大顶堆,最小值在顶点的是最小堆
    • 数组存储,节点i,左节点i2,右节点i2+1

    建堆和移除堆顶

    建堆:把节点插入到末尾,从下向上,每个节点与父节点比较,不满足大小关系就交换(从下往上是指节点主动与父节点比较),直到节点循环到1为止
    移除堆顶:用节点最后一个值覆盖堆顶的值,然后进行堆化,从堆顶开始,从上往下,每个父节点与左边节点比较,如果不满足大小关系,则把最大标记置为左节点,右节点同理。通过两个比较后,如果最大标记仍然是父节点,表示满足了大小关系,循环结束,否则父节点与最大标记交换。父节点更新为最大标记的位置,开始下一轮比较。

    type Heap struct {
    	arr []int
    	count int //已使用的容量
    	length int //总容量
    }
    
    //模拟构造函数
    func NewHeap(length int)*Heap{
    	heap := &Heap{}
    	heap.arr = make([]int,length+1) //初始化,从arr[1]开始使用
    	heap.count = 0 //默认已使用容量为0
    	heap.length = length+1
    	return heap
    }
    
    func (this *Heap)insert(value int){
    	//已使用容量 >= 总容量 表示容量已满
    	if this.count >= this.length{
    		return
    	}
    	//标记已使用容量+1
    	this.count++
    	//最后一个元素放入value
    	this.arr[this.count] = value
    	//从下往上堆化(子节点主动跟父节点比较)
    	i := this.count
    	//循环结束条件:i/2>0表示最后一直比较到1
    	//子节点与父节点比较
    	for i/2 > 0 && this.arr[i] > this.arr[i/2]{
    			this.arr[i], this.arr[i/2] = this.arr[i/2], this.arr[i]
    			//i偏移到父节点,如果比较不成立,表示不需要移动数据,数据已经到了合适的位置
    			i = i / 2
    	}
    }
    
    func (this *Heap)removeMax(){
    	if this.count == 0{
    		return
    	}
    	//用最后一个值覆盖第一个值
    	this.arr[1] = this.arr[this.count]
    	//总数减少
    	this.count--
    	//堆化
    	this.heapify()
    }
    
    func (this *Heap)heapify(){
    	//从第一个节点开始比较
    	i := 1
    	for {
    		//假设父节点是最大的
    		maxPos := i
    		//当左节点存在(小于和等于都是存在的),比较大小,如果左节点更大,maxPos更新为左节点的值
    		if i*2 <= this.count && this.arr[i] < this.arr[i*2]{
    			maxPos = i*2
    		}
    		//同理,如果右节点更大,更新为右节点的值
    		if i*2+1 <= this.count && this.arr[i] < this.arr[i*2+1]{
    			maxPos = i*2+1
    		}
    		//如果左右比价后,父节点仍然是最大的,表示已经不需要比较,数据都是正常的
    		if maxPos == i {
    			break
    		}
    		//否则,用父节点跟左右节点中最大的值交换
    		this.arr[i],this.arr[maxPos] = this.arr[maxPos],this.arr[i]
    		//父节点更新为最大标记的位置,以进行下一轮比较的起点
    		i = maxPos
    	}
    }
    
    func TestHeap(t *testing.T){
    	head := NewHeap(5)
    	head.insert(4)
    	head.insert(6)
    	head.insert(1)
    	head.insert(3)
    	head.insert(7)
    	head.removeMax()
    	head.removeMax()
    	for i:=1; i<=head.count;i++{
    		t.Log(head.arr[i])
    	}
    }
    

    堆排序

    步骤

    1. 建堆:从n/2开始到1(非叶子节点的范围),从后往前推进,逐渐建堆(假设开始堆只有3个元素,逐渐往上加)
    2. 堆化:传入一个父节点以及数组结束位置,父节点与左右节点对比,如果没有交换,则父节点是最大的,否则,标记左/右节点为下一个父节点,并交换元素,并继续向下堆化
    3. 排序:先进行第一次建堆,建完后最大/小值在堆顶,把堆顶放到数组末尾,并且砍掉(i--),重复此过程,直到堆顶(i)
    package algo
    
    import (
    	"testing"
    )
    
    //堆化
    func heapify(arr []int,start int,end int){
    	i := start
    	//用一个变量记录最大的位置
    	maxPos := i
    	for{
    		//找出父左右中最大的节点
    		//当左节点没有超过数组的边界并且左节点更大,标记最大位置
    		if i*2 <= end && arr[i*2] > arr[i]{
    			maxPos = i*2
    		}
    		if i*2+1 <= end && arr[i*2+1] > arr[maxPos]{
    			maxPos = i*2+1
    		}
    		//父节点已经是最大的时候,结束循环
    		if maxPos == i {
    			break
    		}
    		//父节点不是最大的时候,跟最大的节点交换,并且标记下一轮循环的父节点为该节点
    		//如果不用循环继续往下堆化,依然可找出最大的元素在父节点,但是无法保证子节点的堆性质成立
    		arr[maxPos],arr[i] = arr[i],arr[maxPos]
    		i = maxPos
    	}
    }
    
    func buildHead(arr []int,start int,end int){
    	//从最后一个非叶子节点开始,逐渐加入元素,建堆
    	for i:=end/2; i>=start; i--{
    		heapify(arr,i,end)
    	}
    }
    
    func heapSort(arr []int){
    	//从后往前堆化,完成后堆顶是最大的元素
    	//把堆顶的元素跟最后一个元素交换,并且往前推移,直到推进到堆顶
    	for i := len(arr)-1;i>=1;i--{
    		buildHead(arr,1,i)
    		arr[i],arr[1] = arr[1],arr[i]
    	}
    }
    
    func TestHeadSort(t *testing.T){
    	arr := []int{0,5,7,2,4,1}
    	heapSort(arr)
    	for i:=1; i<len(arr);i++{
    		t.Log(arr[i])
    	}
    }
    
  • 相关阅读:
    Python环境搭建
    接口测试工具Jmeter
    接口测试工具postman
    fiddler -- 一个强大的抓包工具
    Pychram中使用reduce()函数报错:Unresolved reference 'reduce'
    Mac下用命令行获取苹果手机的UDID
    python 的 lambda使用笔记
    appium报错:An unknown server-side error occurred while processing the command. Original error: Could not proxy command to remote server. Original error: Error: read ECONNRESET
    Pycharm中使用from appium import webdriver时报错:ModuleNotFoundError: No module named 'appium'
    如何升级pip3
  • 原文地址:https://www.cnblogs.com/jaychan/p/12782744.html
Copyright © 2011-2022 走看看