数据结构 2树

树的一些术语与性质

在这里插入图片描述
,是一个没有环路的无向联通图;
,我们将树中一个不同的顶点视为树的根;
结点的度,结点的子树的数目,例如A的度为3,F的度为0;(与图的度不同?)
树的度,所有子树的度的最大值,例如上面树的度为3
父亲,儿子,兄弟;A是B的父节点,B是A的子节点,BC是兄弟节点
树叶:度为0的节点,例如上图树叶为K,L,F,G,M,I,J
树的高度:等于根节点的高度,等于最大的深度
高度:有的书将叶节点的高度定义为0,有的定义为1
二叉树:每个节点最多有两个儿子的树

性质:

  • 一棵树有N个顶点,则这棵树有N-1条边
  • 所有节点度的和 = 节点的个数-1 (这个1是由于根节点)

二叉树

二叉树的每个节点最多只有两个儿子,而且左儿子与右儿子不同
在这里插入图片描述
节点定义:

class TreeNode
{
	ElemType Val;
    TreeNode *Left;
    TreeNode *Right;
};

二叉树有左右两个指针,分别指向两个二叉树

二叉树的性质:

  1. 第 i 层最多有2i-1 个节点
  2. 深度为k的二叉树最多有2k -1个节点 (k>=1) …… 这个根节点的深度为定义为1
  3. 度为2节点的个数 = 度为0节点(树叶)的个数 -1 (利用所有节点度的和为节点的个数减1这个性质)

表达树

根据后缀表达式创建一个表达式树,
在这里插入图片描述
创建步骤如下:

  1. 依次读取字符
  2. 如果该字符是操作数,创建操作数节点并入栈;
  3. 如果该字符是操作符,将栈顶的两个节点T1,T2出栈,并创建操作符节点,将T1,T2分别作为操作符节点的左右儿子,最后将操作符节点入栈

有了表达式树以后,利用树的前序遍历,中序遍历,后序遍历可以分别获得前序表达式,中缀表达式和后缀表达式。

二叉树的遍历

前序遍历,中序遍历,后序遍历

使用递归的方法对二叉树进行遍历:
前序遍历,中序遍历,后序遍历说的是根节点的位置。前序遍历,先输出当前子树的根节点,然后一次对左右子树进行递归遍历。中序遍历,先对左子树进行递归遍历,再输出根节点的值,然后对右子树进行递归遍历。

class TreeNode 
{
public:
    ElemType Val;
    TreeNode *Left;
    TreeNode *Right;
public:
    TreeNode(ElemType X):Val(X),Left(nullptr),Right(nullptr){}

    //前序遍历 Root->Left->Right
    void traversalByPreOrder()
    {
        std::cout << Val << " ";
        if(Left != nullptr)
            Left->traversalByPreOrder();
        if(Right != nullptr)
            Right->traversalByPreOrder();
    }
    //中序遍历 Left -> Root -> Right
    void traversalByInOrder()
    {
        if(Left != nullptr)
            Left->traversalByInOrder();
        std::cout << Val << " ";        
        if(Right != nullptr)
            Right->traversalByInOrder();
    }

    //后续遍历 Left -> Right -> root
    void traversalByPostOrder()
    {
        if(Left != nullptr)
            Left->traversalByPostOrder();  
        if(Right != nullptr)
            Right->traversalByPostOrder();
        std::cout << Val << " ";  
    }
};

使用循环的方法进行中序遍历

    void iter_inorder()
    {
        stack<TreeNode*> S;
        TreeNode *p = root;
        for (;;)
        {
            for (; p; p=p->Left)
                S.push(p);   //将左子树的左子树的……全入栈
            if(S.size() == 0)
                break;  //栈为空表明遍历完成
            p = S.top();
            S.pop();
            if(!p)     //判断节点是否为null
                break;   
            cout << p->Val <<" ";
            p = p->Right; 指向其右子树做同样的操作
        }
    }
层序遍历

使用队列数据结构,算法步骤如下

  1. 首先将根节点入列,
  2. 将队列中的元素出列,打印输出,并将该元素的儿子节点依次入列
  3. 重复2,直到队列为空
    void LevelOrderTaversal()
    {
        queue<TreeNode *> T;
        T.push(root);
        while(T.size() != 0 )
        {
            TreeNode* p = T.front();
            T.pop();
            std::cout << p->Val << " ";
            if(p->Left != NULL) 
            {
                T.push(p->Left);
            }
            if(p->Right != NULL)
            {
                T.push(p->Right);
            }
        }
    }

Threaded Binary Trees(线索二叉树)

线索二叉树可以让二叉树以一种既定的方式进行遍历。
如果只给我们一个树叶节点,我们无法知道它的父节点是什么,而且next指向了null。从而也就无法依靠一个叶节点进行对树的遍历。而线索二叉树可以,它给一些节点添加了额外的信息,不用递归,甚至不用额外的存储空间就可以实现对线索二叉树的遍历。
线索二叉树的定义
对于一个二叉树,如果T->Right == null,则使T->Right指向以中序遍历为顺序的T节点的后一个节点;如果T->Left == null ,则使T->Left,指向以中序遍历为顺序的T节点的前一个节点。(如果存在的话)例如C的左右儿子的指向怎么判断?中序遍历的顺序为根节点中间输出,即A B C D E F G H I, 所以,C的左儿子指向B,右儿子指向D
这种定义方式,规定了线索二叉树是以中序方式进行遍历的。
在这里插入图片描述

Algorithm traverse(t):
Input: a pointer t to a node (or nil)
If t = nil, return.
Else:
	traverse(left-child(t))
	Visit t
	traverse(right-child(t))

例如对节点D进行遍历,
会先对C进行遍历,而由于有了线索,并不会直接输出C,反而是对C左节点指向的B进行遍历,再会先对A先进行遍历。最终遍历输出的就是对F进行中序遍历输出的序列

二叉搜索树

定义

二叉搜索树的每个节点都包含一个key值,key值是不同的;
对于任意一个节点,若它的左儿子不为null,则左儿子的key值比它小;若它的右儿子不为null,则右儿子的key值比它大
左子树与右子树也一定是二叉搜索树

操作

Find

在一棵二叉树中找到key为X的节点。
递归查找,如果所找的元素比当前节点小,则对当前节点的左子树进行递归,如果所找的元素比当前节点大,则对当前节点的右子树进行递归,否则的话就是找到了该元素,直接返回。
不用递归的方法,T = root从根元素开始。如果,要找的元素比T->Element大,则将T移动到T->Right;小的话,就移动到T->Left.不断循环,知道移动到恰好相等的那个节点。

Position Find(int X,BSTree T)
{
	if(T == null) return;    //递归出口不要忘记
	if(T->Element > X) return Find(X,T->Left);
	if(T->Element < X) return Find(X,T->Right);
		else return T;
}
Position Iter_Find( int X, BSTree T )
{
 	while ( T ) {
 		if ( X == T->Element ) return T ;
 		if ( X < T->Element )
 			T = T->Left ;
		 else
			T = T-> Right ; 
	 } 
	 return NULL ; 
} 
FindMin与FindMax

找到一个二叉树中key值最大或最小的元素。
根据二叉搜索树定义,最小的元素一定在左子树上,不断的向左子树进行查找就可以找到。

//递归版本
Position FindMin(BSTree T)
{
	if(T == null) return null;  //空树的情况
	if(T->Left == null) return T;  //找到的最左边的元素
		else return FindMin(T->Left);
}
//非递归版本
Position FindMin(BSTree T)
{
	if(T)
		while(T->Left != null)
			T=T->Left;
	return T;
}
插入
SearchTree Insert( ElementType X, SearchTree T ) 
{
	if(T == null) T = TreeNode(X); //如果为空树,则直接新建一个节点
	if(X < T->Element) T->Left = Insert(X, T->Left); //如果比T的元素值小,就向左子树插入
	if(X > T->Element) T->Right = Insert(X, T->Right); 
	return T;
}
删除

情形1:左右儿子都存在的情况
在这里插入图片描述
从右子树中找到一个最小的节点,将值复制给要删除的节点,最后转变为删除右子树最小元素的那个节点

TmpCell = FindMin(T->Right);
T->Element = TmpCell->Element;
T->Right = DeleteNode(T->Element, T->Right);

情形二:只有一个儿子或没有儿子的情况
在这里插入图片描述
可以直接这样写:

Temp = T;
if(T->Left == null) T = T->Right;
	else if(T->Right == null) T = T->Left;
free(Temp);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值