什么是二叉树?
引用自百度百科:
在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree),同样的左右子树也都是二叉树.
前言
本文代码实现为 C/C++,因为不是一个完整的讲解文章,只是个人思路,所以说思路讲解可能有不足之处,有错误请指出.
节点定义
使用单向链表的形式,只保存当前节点的子节点和权值,不保存父节点.
typedef struct Node{
int value;//权值,根据实际情况而定,这里用数字
Node *lChild;//左儿子节点
Node *rChild;//右儿子节点
}BNode;
二叉树的遍历
二叉树的遍历分为三种:
- 前序遍历
先遍历当前节点(根节点),然后遍历左儿子节点,再遍历右儿子节点 - 中序遍历
先遍历左儿子节点,然后遍历当前节点(根节点),再遍历右儿子节点 - 后序遍历
先遍历左儿子节点,然后遍历右儿子节点,再遍历当前节点(根节点)
我们创建二叉树的时候一般用的是递归的方法,但是二叉树的遍历可以有递归和非递归两种方式.下面会分别给出二叉树遍历递归和非递归的思路和代码.
PS:建议看懂思路后再看代码实现,然后手动模拟一下,效果更佳.
前序遍历
递归版:
先遍历根节点,然后遍历左子树,再遍历右子树
非常经典的递归思想,理解二叉树的性质和递归思想即可得出.
void preorderTraversal(BNode *rootNode) {
if(rootNode != NULL) {
printf("%d ", rootNode->value);
preorderTraversal(rootNode->lChild);
preorderTraversal(rootNode->rChild);
}
}
非递归版:
使用栈来实现,因为 C++ 中有现成的库,所以说就不手动模拟栈了,当遍历到一个节点的时候,先将此节点输出,然后一直遍历其左儿子,依次循环,直到碰到叶子节点,然后开始弹,也就是递归过程中的回溯。
对于任一结点node:
- 访问结点node,并将结点node入栈;
- 判断结点node的左孩子是否为空
- 若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点node,循环至1
- 若不为空,则将node的左孩子置为当前的结点node;
- 直到node为NULL并且栈为空,则遍历结束。
void preorderTraversalNonrecursive(BNode *rootNode) {
stack<BNode *>s;
if (rootNode == NULL ) {
return ;
}
BNode *tempNode = rootNode;
while(!s.empty() || tempNode != NULL) {
while(tempNode != NULL) {
printf("%d ", tempNode->value);
s.push(tempNode);
tempNode = tempNode->lChild;
}
if (!s.empty()) {
tempNode = s.top();
s.pop();
tempNode = tempNode->rChild;
}
}
}
中序遍历
递归版:
先遍历左子树,然后遍历根节点,再遍历右子树
void inorderTraversal(BNode *rootNode) {
if(rootNode != NULL) {
inorderTraversal(rootNode->lChild);
printf("%d ", rootNode->value);
inorderTraversal(rootNode->rChild);
}
}
非递归版:
和先序遍历一样的思想,因为要先访问左子树,然后再访问根节点(当前节点),那么在栈弹出的时候进行输出即为所求.
对于任一结点node:
- 若其左孩子不为空,则将node入栈并将node的左孩子置为当前的node,然后对当前结点P再进行相同的处理;
- 若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的node置为栈顶结点的右孩子;
- 直到node为NULL并且栈为空则遍历结束
void inorderTraversalNonrecursive(BNode *rootNode) {
stack<BNode *>s;
if (rootNode == NULL ) {
return ;
}
BNode *tempNode = rootNode;
while(!s.empty() || tempNode != NULL) {
while(tempNode != NULL) {
s.push(tempNode);
tempNode = tempNode->lChild;
}
if (!s.empty()) {
tempNode = s.top();
s.pop();
printf("%d ", tempNode->value);
tempNode = tempNode->rChild;
}
}
}
后序遍历
递归版:
先遍历左子树,然后遍历右子树,再遍历根节点
void postorderTraversal(BNode *rootNode) {
if(rootNode != NULL) {
postorderTraversal(rootNode->lChild);
postorderTraversal(rootNode->rChild);
printf("%d ", rootNode->value);
}
}
非递归版:
后序遍历的非递归版和先序遍历、中序遍历不一样,这里我用两个栈来实现