跳到主要内容

04Tree

tree

对于查找来说 分为两类。

​ 一、数据体量不变的,称为静态查找 (线性 和二分法)

​ 二、数据体量可以动态的扩容和缩减。需要动态查找。(二分查找搜索树)

学习路线:

​ 森林 ---> 树 ---> 二叉树 ---> 搜索二叉树 ---> 平衡搜索二叉树

​ 树(Tree) : n(n≥0)个结点构成的有限集合。当 n=0 时,称为空树;对于任一棵非空树(n≥0),它具备以下性质: ​ 树中有一个称为"根(Root)"的特殊结点,没有父节点; ​ 其余结点可分为 m(m>0)个互不相交的有限集 T1, T2, ... , Tm,其中每个集 ​ 合本身又是一棵树,称为原来树的"子树"(SubTree)。 注:树的定义具有递归性,即"树中还有树",仅有一个根结点的树是最小树。

​ 树的深度( Depth) :树中所有结点中的最大层次是这棵树的深度。

二叉树

二叉树是 n(n≥0)个结点的有限集合。n=0 的树称为空二叉树;n>0 的二叉树由一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成,左、右子树也是二叉树,所以子树也可以为空树。

基本特征:

​ ① 每个结点最多只有两棵子树(不存在度大于 2 的结点);( 度 就是 树叉) ​ ② 二叉树的子树,有左右之分,左子树和右子树次序不能颠倒。所以下面是两棵不同的树。

满二叉树: 在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称为满二叉树。

完全二叉树: 如果一棵深度为 k,有 n 个结点的二叉树中各结点能够与深度为 k 的顺序编号的满二叉树从 1 到 n 标号的结点相对应的二叉树称为完全二叉树。

二叉树的存储

​ 线性存储一般仅用于满二叉树和完全二叉树。

链式存储

1、递归实现

​ 前序遍历 中序遍历 后序遍历

​ 1.访问根节点 1.中序遍历左子树 1.后序遍历左子树

​ 2.前序遍历左子树 2.访问根节点 2.后序遍历右子树

​ 3.前序遍历右子树 3.中序遍历右子树 3.访问根节点

#include<stdio.h>

typedef struct TreeNode
{
public:
int _data;
struct TreeNode *_left;
struct TreeNode *_right;
}TreeNode;

void preOrderTraverase(TreeNode *r) //前序遍历
{
if (r)
{
printf("%d ",r->_data);
preOrderTraverase(r->_left);
preOrderTraverase(r->_right);
}
}

void midOrderTraverase(TreeNode *r) //中序遍历
{
if (r)
{
midOrderTraverase(r->_left);
printf("%d ", r->_data);
midOrderTraverase(r->_right);
}
}

void postOrderTraverase(TreeNode *r) //后序遍历
{
if (r)
{
postOrderTraverase(r->_left);
postOrderTraverase(r->_right);
printf("%d ", r->_data);
}
}

int getHeightByPostOrder(TreeNode *t)
{
int lH, rH, maxH;

if (t)
{
lH = getHeightByPostOrder(t->_left);
rH = getHeightByPostOrder(t->_right);
maxH = (lH > rH) ? lH : rH;
return maxH + 1;
}
else
return 0;
}

int main()
{
TreeNode a, b, c, d, e, f;
TreeNode * root = &a;

a._data = 1; b._data = 2; c._data = 3;
d._data = 4; e._data = 5; f._data = 6;

a._left = &b; a._right = &c;
b._left = &d; b._right = &e;
c._left = nullptr; c._right = &f;

d._left = d._right = e._left = e._right = f._left = f._right = nullptr;

// preOrderTraverase(root);
// putchar(10);
//midOrderTraverase(root);
//putchar(10);
postOrderTraverase(root);
putchar(10);

int high= getHeightByPostOrder(root);
printf("\n high = %d \n\n", high);
return 0;
}

2、循环实现

非递归算法实现的基本思路:使用栈

递归的方式,每一个节点有 3 次访问的机会,但是对于压栈来说,每个压入的节 点,只有 2 次访问的机会。

mystack.h

#ifndef MYSTACK_H
#define MYSTACK_H

typedef struct _TreeNode
{
public:
int _data;
struct _TreeNode *_left;
struct _TreeNode *_right;
}TreeNode;

typedef struct _Stack
{
int _len;
int _top;
TreeNode **_space; //游标
}Stack;

void initStack(Stack *s,TreeNode* size);

int isStackFull(Stack *s);
int isStackEmpty(Stack *s);

void push(Stack *s, TreeNode* ch);

TreeNode* pop(Stack *s);
void clearStack(Stack *s);
void resetSatck(Stack *s);
#endif // MYSTACK_H

实现 stack.cpp

#include"myStack.h"
#include<stdlib.h>

void initStack(Stack *s,TreeNode* size)
{
s->_top = 0;
//s->_len = size;
s->_space = (TreeNode **)malloc(sizeof(TreeNode));
}

int isStackFull(Stack *s)
{
// if(s->_top==s->_len)
// return true;
// else
// return false;

return s->_top == s->_len;
}

int isStackEmpty(Stack *s)
{
return s->_top == 0;
}

void push(Stack *s, TreeNode* ch)
{
s->_space[s->_top++] = ch;
}

TreeNode* pop(Stack *s)
{
return s->_space[--s->_top];
}

void clearStack(Stack *s) //清空 销毁栈
{
free(s->_space);
}

void resetSatck(Stack *s)
{
s->_top = 0;
}

main.cpp

#include<stdio.h>
#include"myStack.h"


void preOrderTraverase(TreeNode *r) //前序遍历
{
if(r)
{
Stack s;
initStack(&s,r);
while (r || !isStackEmpty(&s))//t 为空,但是栈内不空
{
while (r)
{
printf("%d ", r->_data);/*(访问)结点*/
push(&s, r);
r = r->_left; /*一直向左并将沿途结点压入堆栈*/
}
if (!isStackEmpty(&s))
{
r = pop(&s); /*结点弹出堆栈*/
r = r->_right; /*转向右子树*/
}
}
}
}

void midOrderTraverase(TreeNode *r) //中序遍历
{
if (r)
{
Stack s;
initStack(&s,r);
while (r || !isStackEmpty(&s))
{
while (r)
{
push(&s, r);
r = r->_left;
}
if (!isStackEmpty(&s))
{
r = pop(&s);
printf("%d ", r->_data);
r = r->_right;
}
}
}
}

void postOrderTraverase(TreeNode *r) //后序遍历
{
Stack s;
initStack(&s, r);
TreeNode *cur;
TreeNode *pre = nullptr;
push(&s, r);
while (!isStackEmpty(&s))
{
cur = pop(&s);
push(&s, cur);

if ((cur->_left == nullptr && cur->_right == nullptr) ||
(pre != nullptr && (pre == cur->_left || pre == cur->_right)))
{
//如果当前结点没有孩子结点或者孩子节点都已被访问过
printf("%d ", cur->_data);
pop(&s);
pre = cur;
}
else
{
if (cur->_right != nullptr)
push(&s, cur->_right);
if (cur->_left != nullptr)
push(&s, cur->_left);
}
}
}


int main()
{
TreeNode a, b, c, d, e, f;
TreeNode * root = &a;

a._data = 1; b._data = 2; c._data = 3;
d._data = 4; e._data = 5; f._data = 6;

a._left = &b; a._right = &c;
b._left = &d; b._right = &e;
c._left = nullptr; c._right = &f;

d._left = d._right = e._left = e._right = f._left = f._right = nullptr;

preOrderTraverase(root);
putchar(10);
midOrderTraverase(root);
putchar(10);

postOrderTraverase(root);
putchar(10);

return 0;
}