二叉树
二又树(Binary Tree)是n(n=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树特点
- 每个结点最多有两棵子树,所以二又树中不存在度大于2的结点。注意不是只有两棵子树,而是最多有。没有子树或者有一棵子树都是可以的。
- 左子树和右子树是有顺序的,次序不能任意颠倒。
- 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
二叉树遍历
二又树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
- 中心思想:访问和次序。
二叉树遍历方法
- 前序遍历
- 中序遍历
- 后序遍历
- 层序遍历
前序遍历
规则是若二又树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。如下图所示,遍历的顺序为:ABDGHCEIF。
前序遍历算法
/*前序遍历算法——递归版*/
void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
printf("%c",T->data);/*显示节点数据*/
PreorderTraverse(T->lchild);/*再先序遍历左子树*/
PreorderTraverse(t->rchild);/*最后遍历右子树*/
}
/*前序遍历算法——非递归版*/
#include<bits/stdc++.h>
using namespace std;
struct node
{
node left;
node right;
int value;
};
void preorder(node head)//先序遍历
{
if(head!=null)//头节点不为空时才执行下列操作
{
stack < node >q;
q.push(head);//将头节点放入栈中去
while (!q.empty())
{
head=q.top();
q.pop();
cout<<head.value<<" ";//打印头节点
if(head.right!=null)//如果右孩子不为空,先放右孩子.
q.push(head.right);
if(head.left!=null)//如果左孩子不为空,后放左孩子,这样才能保证打印顺序为:头节点-->左孩子-->右孩子
q.push(head.left);
}
}
}
中序遍历
规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。如下图所示,遍历的顺序为:GDHBAEICF。
中序遍历算法
/*中序遍历算法——递归版*/
void InOrderTraverse(BiTree T)
{
if(T==NULL)
return;
PreorderTraverse(T->lchild);/*中序遍历左子树*/
printf("%c",T->data);/*显显示结点数据,可以更改为其他对结点操作*/
PreorderTraverse(t->rchild);/*最后中序遍历右子树*/
}
/*中序遍历算法——非递归版*/
#include<bits/stdc++.h>
using namespace std;
struct node
{
node left;
node right;
int value;
};
void inorder(node head)//中序遍历
{
if(head!=null)//头节点不为空时才执行下列操作
{
stack < node >q;
while (!q.empty()||head!=null)//这两个条件只要满足一个,就执行下面过程
{
if(head!=null)
//如果当前节点不为空,就把当前节点放入栈中去,然后让head指向当前节点的左节点,没有打印过程
{
q.push(head);
head=head.left;
}
else if(head==null)
//如果当前节点为空,那么就弹出栈顶的元素,打印。然后让head指向栈顶节点的右节点
{
head=q.top();
q.pop();
cout<<head.value<<" ";
head=head.right;
}
}
}
}
后序遍历
规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。如下图所示,遍历的顺序为:GHDBIEFCA。
后序遍历算法
/*后序遍历算法——递归版*/
void PostOrderTraverse(BiTree T)
{
if(T==NULL)
return;
PreorderTraverse(T->lchild);/*先遍历左子树*/
PreorderTraverse(t->rchild);/*后遍历右子树*/
printf("%c",T->data);/*显显示结点数据,可以更改为其他对结点操作*/
}
/*后序遍历算法——非递归版*/
#include<bits/stdc++.h>
using namespace std;
struct node
{
node left;
node right;
int value;
};
void postorder(node head)//后序遍历,利用栈能够使一段序列逆序的性质
{
if(head!=null)//和上面一样
{
stack < node >q;
stack < node >p;//辅助栈,让q栈中的弹出的节点逆序
q.push(head);
while (!q.empty())
{
head=q.top();
//弹出栈顶节点,但是先不着急打印,而是把该节点放入p栈中去,来实现原先打印顺序的逆序
p.push(head);
q.pop();
if(head.left!=null)//如果该节点的左孩子不为空,先放左孩子
q.push(head.right);
if(head.right!=null)//如果该节点的右孩子不为空,先放右孩子
q.push(head.left);
}
while (!p.empty())//打印辅助栈中的节点
cout<<p.value<<" ";
}
}
总结
- 应该说,三种有序表的查找本质上是分隔点的选择不同,各有优劣,实际开发时可根据数据的特点综合考虑再做出选择。
- 未知的数据首先考虑二分查找
- 均匀的数据适合插值查找
- 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当中的大部分数据,斐波那契查找工作效率要高一些。
参考文献
[1] 程杰. 大话数据结构[M]. 清华大学出版社, 2011.
[2] 啊哈磊. 啊哈! 算法[M]. 人民邮电出版社, 2014.
[3] 齐鲁工业大学–栾琪_二叉树的3种遍历方法 6种实现_2018_5_19
[4] z_ryan_详解二叉树的非递归遍历_2018_6_29