一、 二叉树定义
一棵二叉树(binary tree)t是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个元素称为根,余下的元素被划分成两颗二叉树,分别称为t的左子树和右子树。
二叉树与树的根本区别:
- 二叉树的每颗子树都恰好有两颗子树(其中有一个或者两个可能为空)。而树的每个元素可由任意数量的子树
- 在二叉树中每个元素的子树都是有序的,有左子树和右子树之分。而树的子树是无序的。
- 二叉树可以为空,树不能为空
二、 二叉树的特性
- 二叉树有n个元素,n > 0,它有n-1条边。
- 一棵二叉树的高度为h,h≥0,最少有h个元素,最多有2的h次方减1个元素。
- 一棵二叉树有n个元素,n>0,它的高度最大为n,最小为一棵二叉树有n个元素,n>0,它的高度最大为n,最小为[log_2(n + 1)](表示向下取整)
- 设一完全二叉树的一元素编号为i,1≤i≤n。有以下关系成立:
- 如果i= 1,该元素为二叉树的根。若i>1,则父节点的编号为[i/2](向上取整)
- 如果2i>n,则该元素无左孩子。否则左孩子的编号为2i.
- 如果2i+1>n,则该元素无右孩子。否则右孩子的编号为2i+1。
三、二叉树的创建
1.#符号法创建二叉树
(1)二叉树的递归创建
根据输入的符号进行树的创建。当得到的字符是“#”符号时,表示该左子树已经是叶子节点,然后进行右子树的创建,以此类推,直到整个树创建完成。
//二叉树的创建“#”符号法,递归创建
template<class T>
void CreateBiTree(binaryTreeNode<T> **root)
{
char a;
cin >> a;
if ('#' == a)
{
root = NULL;
}
else
{
*root = new binaryTreeNode<T>;
(*root)->element = a;
CreateBiTree(&(*root)->leftChild);//递归创建左子树
CreateBiTree(&(*root)->rightChild);//递归创建右子树
}
}
(2)二叉树的非递归创建
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define STACKSIZE 50
#define bitree_size(tree) ((tree)->size)
#define bitree_root(tree) ((tree)->root)
#define bitree_left(node) ((node)->left)
#define bitree_right(node) ((node)->right)
/*
* 定义二叉树结点以及二叉树的结构
*
*/
typedef struct BiTreeNode_
{
char data;
struct BiTreeNode_ *left;
struct BiTreeNode_ *right;
}BiTreeNode;
typedef struct BiTree_//定义树的头结点
{
int size;
BiTreeNode *root;
}BiTree;
/*
* 定义栈的结构
*
*/
typedef struct Stack_
{
BiTreeNode * base[STACKSIZE];
int top;
int stacksize;
}Stack;
void stack_init(Stack *stack)
{
stack->top = -1;
stack->stacksize = 0;
}
void push(Stack *stack, BiTreeNode *node)
{
if (stack->stacksize == STACKSIZE)
exit(-1);
stack->base[++stack->top] = node;
stack->stacksize++;
}
int stack_empty(Stack *stack)//清空栈
{
if ((-1 == stack->top) && (0 == stack->stacksize))
return 1;
else
return 0;
}
BiTreeNode* pop(Stack *stack)//树的节点出栈
{
if (stack->top < 0)
exit(-1);
else
{
stack->stacksize--;
return stack->base[stack->top--];
}
}
BiTreeNode* get_stack_top(Stack *stack)//获取栈顶元素
{
if (stack->top < 0)
exit(-1);
return stack->base[stack->top];
}
void bitree_init(BiTree *tree)//初始化树
{
tree->size = 0;
tree->root = NULL;
}
//给树tree的某个结点node插入数据域为data的左子树
int bitree_ins_left(BiTree *tree, BiTreeNode *node, const char data)
{
BiTreeNode *new_node, **position;
if (NULL == node)
{
if (bitree_size(tree) > 0)//如果树的size>0返回-1
return -1;
position = &tree->root;//position
}
else
{
if (bitree_left(node) != NULL)
return -1;
position = &node->left;
}
if (NULL == (new_node = (BiTreeNode *)malloc(sizeof(BiTreeNode))))
return -1;
new_node->data = data;
new_node->left = NULL;
new_node->right = NULL;
*position = new_node;
tree->size++;
return 0;
}
//给树tree的某个结点node插入数据域为data的右子树
int bitree_ins_right(BiTree *tree, BiTreeNode *node, const char data)
{
BiTreeNode *new_node, **position;
if (NULL == node)
{
if (bitree_size(tree) > 0)
return -1;
position = &tree->root;
}
else
{
if (bitree_right(node) != NULL)
return -1;
position = &node->right;
}
if (NULL == (new_node = (BiTreeNode *)malloc(sizeof(BiTreeNode))))
return -1;
new_node->data = data;
new_node->left = NULL;
new_node->right = NULL;
*position = new_node;
tree->size++;
return 0;
}
// 先序遍历函数
void pre_order(const BiTreeNode *node)
{
if (node)
{
printf("%c", node->data);
pre_order(node->left);
pre_order(node->right);
}
}
// 中序遍历函数
void in_order(const BiTreeNode *node)
{
if (node)
{
in_order(node->left);
printf("%c", node->data);
in_order(node->right);
}
}
// 后序遍历函数
void end_order(const BiTreeNode *node)
{
if (node)
{
end_order(node->left);
end_order(node->right);
printf("%c", node->data);
}
}
int CreateBiTree()
{
char data;
Stack stack;
BiTree tree;
BiTreeNode *node;
stack_init(&stack);
bitree_init(&tree);
printf("请以先序顺序输入结点值: \n");
data = getchar();
if (0 == bitree_size(&tree))
{
if (' ' == data)
return -1;
bitree_ins_left(&tree, NULL, data);
push(&stack, tree.root);
}
while (!stack_empty(&stack))
{
data = getchar();
if (' ' == data)
{
node = pop(&stack);
if (NULL == node->left)
{
data = getchar();
if (' ' != data)
{
bitree_ins_right(&tree, node, data);
push(&stack, node->right);
}
}
}
else
{
node = get_stack_top(&stack);
if (NULL == node->left)
{
bitree_ins_left(&tree, node, data);
push(&stack, node->left);
}
else
{
node = pop(&stack);
bitree_ins_right(&tree, node, data);
push(&stack, node->right);
}
}
}
}
int main()
{
char data;
Stack stack;
BiTree tree;
BiTreeNode *node;
stack_init(&stack);
bitree_init(&tree);
printf("请以先序顺序输入结点值: \n");
data = getchar();//获取根节点
if (0 == bitree_size(&tree))//插入第一个节点
{
if ('#' == data)
return -1;
bitree_ins_left(&tree, NULL, data);//左子树的创建
push(&stack, tree.root);//将树压入栈
}
while (!stack_empty(&stack))
{
data = getchar();
if ('#' == data)//出栈插入右子树
{
node = pop(&stack);//出栈操作
if (NULL == node->left)
{
data = getchar();
if ('#' != data)
{
bitree_ins_right(&tree, node, data);
push(&stack, node->right);//入栈操作
}
}
}
else//进行左子树的插入
{
node = get_stack_top(&stack);//获取栈顶元素
if (NULL == node->left)
{
bitree_ins_left(&tree, node, data);
push(&stack, node->left);//入栈操作
}
else
{
node = pop(&stack);//出栈
bitree_ins_right(&tree, node, data);
push(&stack, node->right);//入栈操作
}
}
}
printf("-----先序遍历二叉树-----\n");
pre_order(tree.root);
putchar('\n');
printf("-----中序遍历二叉树-----\n");
in_order(tree.root);
putchar('\n');
printf("-----后序遍历二叉树-----\n");
end_order(tree.root);
putchar('\n');
return 0;
}
2 通过先序遍历和中序创建二叉树
BinaryTreeNode * CreateBinaryTree(ElemType * pre, ElemType *in, int length)//此种建立二叉树的方法本质上是先建立根结点 再建立左子树,再建立右子树
{
if (length <= 0 || pre == NULL || in == NULL)////递归出口长度为0时表示建树完毕
{
return NULL;
}
ElemType rootValue = pre[0];
BinaryTreeNode * root = new BinaryTreeNode();//建立根节点
root->data = rootValue;
root->left = root->right = NULL;
int leftLength, rightLength, i = 0;//左子树的长度,右子树的长度
while (i<length&&in[i] != rootValue)
{
i++;
}
leftLength = i;
rightLength = length - leftLength - 1;//注意对个一个遍历的序列一定满足rightLength+leftLength+1=length;左子树长度加上右子树长度加一个根结点的长度等于树所有结点的长度。
if (leftLength>0)//建立左子树
{
root->left = CreateBinaryTree(pre + 1, in, leftLength);////此处的关键在于寻找右子树后序序列的起始地址,和右子树的中序序列的起始地址
}
if (rightLength>0)//建立右子树
{
root->right = CreateBinaryTree(pre + length - rightLength, in + length - rightLength, rightLength);////此处的关键在于寻找右子树后序序列的起始地址,和右子树的中序序列的起始地址
// 上一句等价于root->right =CreateBinaryTree(pre+1+leftLength,in+1+leftLength,rightLength);
}
return root;
}
3 通过后序遍历和中序遍历创建树
BinaryTreeNode *CreateBinaryTree2(ElemType *post, ElemType * in, int length)//此种建立二叉树的方法本质上是先建立根节点再建立左子树,再建立右子树。
{
if (post == NULL || in == NULL || length <= 0)//递归出口长度为0时表示建树完毕
{
return NULL;
}
ElemType rootValue = post[length - 1];
BinaryTreeNode *root = new BinaryTreeNode();//建立根节点
root->data = rootValue;
root->left = root->right = NULL;
int leftLength, rightLength;//左子树的长度,右子树的长度
int i = 0;
while (i<length&&in[i] != rootValue)//遍历中序序列寻找根结点从而得到左子树的长度,和右子树的长度
{
i++;
}
leftLength = i;
rightLength = length - leftLength - 1;//右子树的长度为总长度减去左子树长度和一个根结点长度
if (leftLength>0)//建立左子树
{
root->left = CreateBinaryTree2(post, in, leftLength);//此处的关键在于寻找左子树后序序列的起始地址,和中序序列的起始地址
}
if (rightLength>0)//建立右子树
{
root->right = CreateBinaryTree2(post + leftLength, in + length - rightLength, rightLength);//此处的关键在于寻找右子树后序序列的起始地址,和右子树的中序序列的起始地址
//等价于root->right=CreateBinaryTree2(post+leftLength,in+leftLength+1,rightLength);
}
return root;//最后返回根节点
}
四、二叉树常用操作
(1)二叉树节点定义:
template<class T>
struct binaryTreeNode
{
T element;
binaryTreeNode *leftChild, *rightChild;//左子树,右子树
binaryTreeNode() { leftChild = rightChild = NULL; }
binaryTreeNode(const T &Telement):element(Telement), leftChild(NULL), rightChild(NULL){}
binaryTreeNode(const T &Telement, binaryTreeNode *TheLeftChild, binaryTreeNode *TheRightChild) :element(Telement), leftChild(TheLeftChild), rightChild(TheRightChild) {}
};
(2)确定高度
int DeepthOfTree(BinaryTreeNode* proot)
{
if (proot == NULL)
{
return 0;
}
int leftLength = DeepthOfTree(proot->left);
int rightLength = DeepthOfTree(proot->right);
return (leftLength>rightLength) ? leftLength + 1 : rightLength + 1;
}
(3)确定元素个数
//求所有结点的个数
int countAll(BinaryTreeNode *root)
{
if (root == NULL)
return 0;
return countAll(root->left) + countAll(root->right) + 1;
}
(4)复制二叉树
//复制二叉树
BinaryTreeNode *copyBTree(BinaryTreeNode *root)
{
BinaryTreeNode* p, *lchild, *rchild;
if (root == NULL)
return NULL;
lchild = copyBTree(root->left);
rchild = copyBTree(root->right);
p = (BinaryTreeNode *)malloc(sizeof(BinaryTreeNode));
p->data = root->data;
p->left = lchild;
p->left = rchild;
return p;
}
(5)显示打印二叉树(二叉树的遍历,下面)
(6)确定两棵树是否一样
bool Is_equal(BinaryTreeNode* T1, BinaryTreeNode* T2)
{
bool t = 0;
if (NULL == T1 && NULL == T2)
{
t = 1;
}
else
{
if (NULL != T1 &&NULL != T2)
{
if (T1->data == T2->data)
{
if (Is_equal(T1->left, T2->right))
{
t = Is_equal(T1->left, T2->right);
}
}
}
}
return t;
}
(7)删除整颗树
void clearTree(BinaryTreeNode** root)
{
BinaryTreeNode* pl, *pr;
if (*root == NULL)
return;
pl = (*root)->left;
pr = (*root)->right;
(*root)->left = NULL;
(*root)->right = NULL;
free((*root));
*root = NULL;
clearTree(&pl);
clearTree(&pr);
}
四、二叉树的遍历
1 先序遍历
(1)先序遍历的递归遍历
//先序遍历
template<class T>
void preOrder(binaryTreeNode<T>*t)
{
//前序遍历二叉树
if (t != NULL)
{
visit(t);//访问树根
preOrder(t->leftChild);
preOrder(t->rightChild);
}
}
(2)先序遍历的非递归遍历
以下为二叉树的先序的非递归遍历算法
对于任一结点pcur:
1)访问结点Pcur,并将结点pcur入栈,结点pcur一直左走
2)判断结点pcur的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当 前的结点pcur,循环至 1);若不为空,则将P的左孩子置为当前的结点pcur;
3)直到P为NULL并且栈为空,则遍历结束。
void PreOrderBinaryTree(BinaryTreeNode* proot)
{
if (proot == NULL)//此句其实是多余的。
{
return;
}
stack<BinaryTreeNode*> stackTreeNode;
BinaryTreeNode *pcur = proot;//指向当前要访问的结点
while (pcur != NULL || !stackTreeNode.empty())
{
while (pcur != NULL)//一直往左走
{
printf("%d ", pcur->data);
stackTreeNode.push(pcur);
pcur = pcur->left;
}
if (!stackTreeNode.empty())//在出栈和访问栈顶元素之前要先检查是否为空
{
pcur = stackTreeNode.top();//pcur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点
stackTreeNode.pop();
pcur = pcur->right;
}
}
}
2中序遍历(1)中序遍历递归遍历
//中序遍历
template<class T>
void inOrder(binaryTreeNode<T> *t)
{
inOrder(t->leftChild);
visit(t);
inOrder(t->rightChild);
}
(2)中序遍历的非递归遍历
// 以下为二叉树的中序的非递归遍历算法
void InOrderBinaryTree(BinaryTreeNode* proot)
{
if (proot == NULL)//此句其实是多余的。
{
return;
}
stack<BinaryTreeNode*> stackTreeNode;
BinaryTreeNode *pcur = proot;//指向当前要访问的结点
while (pcur != NULL || !stackTreeNode.empty())
{
while (pcur != NULL)//一直向左走
{
stackTreeNode.push(pcur);
pcur = pcur->left;
}
if (!stackTreeNode.empty())//在出栈和访问栈顶元素之前要先检查是否为空
{
pcur = stackTreeNode.top();//pcur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点
printf("%d ", pcur->data);
stackTreeNode.pop();
pcur = pcur->right;
}
}
}
3 后序遍历
(1)后序遍历递归遍历
//后序遍历
template<class T>
void postOrder(binaryTreeNode<T> *t)
{
postOrder(t->leftChild);
postOrder(t->rightChild);
visit(t);
}
(2)后续遍历的非递归遍历
以下为二叉树的后序的非递归遍历算法
思路:设立两个指针pcur ,和preVisted,,对于任一结点pcur,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,此时要判断此节点是否满是输出条件即:当前节点的右孩子如果为空或者已经被访问,则访问当前节点 否则则将pcur置为其右结点
void PostOrderBinaryTree(BinaryTreeNode* proot)
{
if (proot == NULL)//此句其实是多余的。
{
return;
}
stack<BinaryTreeNode*> stackTreeNode;
BinaryTreeNode *pcur = proot;//指向当前要访问的结点
BinaryTreeNode *preVisted = NULL;//指向前一结点被访问的结点
while (pcur != NULL || !stackTreeNode.empty())
{
while (pcur != NULL)//一直向左走
{
stackTreeNode.push(pcur);
pcur = pcur->left;
}
if (!stackTreeNode.empty())//在出栈和访问栈顶元素之前要先检查是否为空
{
pcur = stackTreeNode.top();//pcur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点
if (pcur->right == NULL || pcur->right == preVisted)//当前节点的右孩子如果为空或者已经被访问,则访问当前节点
{
printf("%d\t", pcur->data);
stackTreeNode.pop();
preVisted = pcur;//
pcur = NULL;//此句不要忘记
}
else//如果当前结点的右子结点存在且没被访问过
{
pcur = pcur->right;
}
}
}
}
4 层次遍历
//二叉树的层序遍历
void LevelOrderBinaryTree(BinaryTreeNode*proot)
{
if (proot == NULL)
{
return;
}
queue<BinaryTreeNode*>queueTreeNode;
queueTreeNode.push(proot);
while (!queueTreeNode.empty())
{
BinaryTreeNode *pNode = queueTreeNode.front();
queueTreeNode.pop();
printf("%d ", pNode->data);
if (pNode->left != NULL)
{
queueTreeNode.push(pNode->left);
}
if (pNode->right != NULL)
{
queueTreeNode.push(pNode->right);
}
}
}