用GO语言和用C语言写的思路没有区别,都是利用递归来解决遍历难的问题。这里值得注意的是,在GO中用switch-case是完全可以代替if-else的,个人觉得在做多项判断时用switch-case来写不仅效率更高,思路也更清晰,也使得代码简洁。
前置各种参数说明:
package main
import "fmt"
const MAXSIZE=1000
const OK=1
const ERROR=0
type TElemType rune
type Status int
下面是二叉树结点的存储:
type BiNode struct{
data TElemType;
Lchild *BiNode;
Rchild *BiNode;
}
type BiTree = *BiNode;
1.初始化二叉树
即建立一个空二叉树,空间为一个结点大小
func InitBiTree(T *BiTree) Status {
*T = new(BiNode)
fmt.Println("分配地址成功")
(*T).Lchild=nil
(*T).Rchild=nil
(*T).data=0
return OK
}//初始化二叉树(给第一个节点分配空间)
2.创建二叉树.
对于创建二叉树来说,例如上图所示的二叉树,首先要自己对其进行前序遍历,值得注意的是:例如c结点的左孩子为空,我们用'#'来表示空结点。d,e,f结点的孩子也是这么表示。如此看来,这棵树的表示方法为 'abc#d##e##f##'。基于这个思路,创建二叉树的算法可以表示为:
func CreateBiTree(T *BiTree) {
var ch TElemType
fmt.Scanf("%c",&ch)
switch{
case ch=='#':
(*T)=nil //当输入#时判定为空结点
default: //其他情况分配空间,并且将ch赋给结点的data域
*T = new(BiNode)
(*T).data=ch
CreateBiTree(&((*T).Lchild))
CreateBiTree(&((*T).Rchild))
}
}
3.前序、中序、后续遍历二叉树.
在一对一的线性表中,只需要找到每个元素的后继结点就可以成功遍历出全部元素,环环相扣纵享丝滑。而二叉树的存储特点为数据是一对多(一对二)存储方式,用这种思路去遍历显然是行不通的,如果说有那么一种方法可以既有条理地去完成遍历,又能使代码简洁。那递归方法再合适不过了。
前序遍历方法思路就是从树根开始,先打印根结点的值,然后去找左子树。注意,这时左子树可以看作单独的一棵树,左子树的树根就是整颗树的左孩子。例如刚才的二叉树,树根为a,当打印出a的值后,以同样的方式访问a的左子树b,这时在访问以b为根结点的树,以此类推,直到当访问的树为空树时(比如访问到c的左子树),开始如此遍历右子树。
中序遍历即为先访问左子树,再访问根结点,最后访问右子树。同理后序遍历最后访问根结点 。
func PreorderTree(T *BiTree){
switch{
case *T==nil:
return;
default:
fmt.Printf("%c,",(*T).data)
PreorderTree(&(*T).Lchild)
PreorderTree(&(*T).Rchild)
}
}
func InorderTree(T *BiTree){
switch{
case *T==nil:
return;
default:
InorderTree(&(*T).Lchild)
fmt.Printf("%c,",(*T).data)
InorderTree(&(*T).Rchild)
}
}
4.求结点个数.
与遍历思路大同小异:前序遍历二叉树,当访问到空结点时:函数返回0,其余情况每访问一个结点,返回数+1。当二叉树完全遍历完成时,返回值就是二叉树结点的个数。
func CountNodes(T *BiTree) int{
switch{
case *T==nil:
return 0;
default:
return 1+CountNodes(&(*T).Lchild)+CountNodes(&(*T).Rchild) ;
}
}
5.求二叉树的深度.这个算法稍微麻烦一些,我们以函数本身的视角来审视这个算法:当函数接受(被输入)一个结点时,先判断是否为空,若结点为空结点,则返回0。若该结点不为空,则递归计算该节点的左子树深度,返回值赋给m。递归计算右子树深度,返回值赋给n。当m>n时,返回m+1。否则返回n+1。整棵树遍历完成后,返回值就是树的深度。
func Deep(T *BiTree) int{
var m int;
var n int;
switch{
case (*T)==nil:
return 0;
default:
m=Deep(&(*T).Lchild);
n=Deep(&(*T).Rchild);
switch{
case m>n:
return m+1;
default:
return n+1;
}
}
}
6.复制树.
方法很简单,以前序遍历为基础,遇到空结点则将新树的该结点置空,否则照抄旧树。遍历完成时,复制新树完成。
func copy(T *BiTree,Q *BiTree){
switch{
case *T==nil:
*Q=nil;
default:
*Q=new(BiNode);
(*Q).data=(*T).data;
(*Q).Lchild=(*T).Lchild;
(*Q).Rchild=(*T).Rchild;
copy(&(*T).Lchild,&(*Q).Lchild);
copy(&(*T).Rchild,&(*Q).Rchild);
}
}
7.测试.
下面给出main函数以及测试结果,由于本人设备不在身边,现在一台平板,只能用模拟环境测试,结果与pc端是一样的,后续会把pc端的结果更新。
func main(){
var T BiTree;
var Q BiTree;
InitBiTree(&T);
CreateBiTree(&T);
fmt.Printf("前序遍历:");
PreorderTree(&T);
fmt.Println("");
fmt.Printf("中序遍历:");
InorderTree(&T);
fmt.Println("");
fmt.Printf("节点的个数为:%d\n",CountNodes(&T));
n:=Deep(&T);
fmt.Printf("二叉树的深度为:%d\n",n);
copy(&T,&Q);
fmt.Printf("前序遍历复制后的新树:\n");
PreorderTree(&Q);
}
写在最后:目前本人还在学习阶段,这是第一次发文档,难免有不妥当不正确的地方,希望各位不吝赐教。如果这篇文章有一天能帮助到可能正在学习数据结构或者学习golang的你,我一定会很开心的。