遍历二叉树
因为树的遍历要对每个节点访问一次,所以一棵有N个节点的二叉树遍历的时间复杂度是O(n),不能再降了
遍历方案:1.遍历方案
从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
(1)访问结点本身(N),
(2)遍历该结点的左子树(L),
(3)遍历该结点的右子树(R)。
以上三种操作有六种执行次序:
NLR、LNR、LRN、NRL、RNL、RLN。
注意:
前三种次序与后三种次序对称,故只讨论先左后右的前三种次序。
2.三种遍历的命名
根据访问结点操作发生位置命名:
① NLR:前序遍历(PreorderTraversal亦称(先序遍历))
——访问结点的操作发生在遍历其左右子树之前。
② LNR:中序遍历(InorderTraversal)
——访问结点的操作发生在遍历其左右子树之中(间)。
③ LRN:后序遍历(PostorderTraversal)
——访问结点的操作发生在遍历其左右子树之后。
注意:
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
遍历算法
1.中序遍历的递归算法定义:
若二叉树非空,则依次执行如下操作:
(1)遍历左子树;
(2)访问根结点;
(3)遍历右子树。
2.先序遍历的递归算法定义:
若二叉树非空,则依次执行如下操作:
(1) 访问根结点;
(2) 遍历左子树;
(3) 遍历右子树。
3.后序遍历得递归算法定义:
若二叉树非空,则依次执行如下操作:
(1)遍历左子树;
(2)遍历右子树;
(3)访问根结点。
4.中序遍历的算法实现
用二叉链表做为存储结构,中序遍历算法可描述为:
void InOrder(BinTree T)
{ //算法里①~⑥是为了说明执行过程加入的标号
① if(T) { // 如果二叉树非空
② InOrder(T->lchild);
③ printf("%c",T->data); // 访问结点
④ InOrder(T->rchild);
⑤ }
⑥ } // InOrder
遍历序列
1.遍历二叉树的执行踪迹
三种递归遍历算法的搜索路线相同(如下图虚线所示)。
具体线路为:
从根结点出发,逆时针沿着二叉树外缘移动,对每个结点均途径三次,最后回到根结点。
2.遍历序列
A
/ \
B C
/ / \
D E F
图
(1) 中序序列(inorder traversal)
中序遍历二叉树时,对结点的访问次序为中序序列
【例】中序遍历上图所示的二叉树时,得到的中序序列为:
D B A E C F
(2) 先序序列(preorder traversal)
先序遍历二叉树时,对结点的访问次序为先序序列
【例】先序遍历上图所示的二叉树时,得到的先序序列为:
A B D C E F
(3) 后序序列(postorder traversal)
后序遍历二叉树时,对结点的访问次序为后序序列
【例】后序遍历上图所示的二叉树时,得到的后序序列为:
D B E F C A
(4)层次遍历(level traversal)二叉树的操作定义为:若二叉树为空,则退出,否则,按照树的结构,从根开始自上而下,自左而右访问每一个结点,从而实现对每一个结点的遍历
注意:
(1)在搜索路线中,若访问结点均是第一次经过结点时进行的,则是前序遍历;若访问结点均是在第二次(或第三次)经过结点时进行的,则是中序遍历(或后序遍历)。只要将搜索路线上所有在第一次、第二次和第三次经过的结点分别列表,即可分别得到该二叉树的前序序列、中序序列和后序序列。
(2)上述三种序列都是线性序列,有且仅有一个开始结点和一个终端结点,其余结点都有且仅有一个前趋结点和一个后继结点。为了区别于树形结构中前趋(即双亲)结点和后继(即孩子)结点的概念,对上述三种线性序列,要在某结点的前趋和后继之前冠以其遍历次序名称。
【例】上图所示的二叉树中结点C,其前序前趋结点是D,前序后继结点是E;中序前趋结点是E,中序后继结点是F;后序前趋结点是F,后序后继结点是A。但是就该树的逻辑结构而言,C的前趋结点是A,后继结点是E和F。
二叉链表的构造
1. 基本思想
基于先序遍历的构造,即以二叉树的先序序列为输入构造。
注意:
先序序列中必须加入虚结点以示空指针的位置。
【例】
建立上图所示二叉树,其输入的先序序列是:ABD∮∮∮CE∮∮F∮∮。
2. 构造算法
假设虚结点输入时以空格字符表示,相应的构造算法为:
void CreateBinTree (BinTree *T)
{ //构造二叉链表。T是指向根指针的指针,故修改*T就修改了实参(根指针)本身
char ch;
if((ch=getchar())=='') *T=NULL; //读人空格,将相应指针置空
else{ //读人非空格
*T=(BinTNode *)malloc(sizeof(BinTNode)); //生成结点
(*T)->data=ch;
CreateBinTree(&(*T)->lchild); //构造左子树
CreateBinTree(&(*T)->rchild); //构造右子树
}
}
注意:
调用该算法时,应将待建立的二叉链表的根指针的地址作为实参。
【例】
设root是一根指针(即它的类型是BinTree),则调用CreateBinTree(&root)后root就指向了已构造好的二叉链表的根结点。
二叉树建立过程见http://student.zjzk.cn/course_ware/data_structure/web/flashhtml/erchashujianli.htm
下面是关于二叉树的遍历、查找、删除、更新数据的代码(递归算法):
[code]
#include <iostream>
using namespace std;
typedef int T;
class bst{
struct Node{
T data;
Node* L;
Node* R;
Node(const T& d, Node* lp=NULL, Node* rp=NULL):data(d),L(lp),R(rp){}
};
Node* root;
int num;
public:
bst():root(NULL),num(0){}
void clear(Node* t){
if(t==NULL) return;
clear(t->L);
clear(t->R);
delete t;
}
~bst(){clear(root);}
void clear(){
clear(root);
num = 0;
root = NULL;
}
bool empty(){return root==NULL;}
int size(){return num;}
T getRoot(){
if(empty()) throw "empty tree";
return root->data;
}
void travel(Node* tree){
if(tree==NULL) return;
travel(tree->L);
cout << tree->data << ' ';
travel(tree->R);
}
void travel(){
travel(root);
cout << endl;
}
int height(Node* tree){
if(tree==NULL) return 0;
int lh = height(tree->L);
int rh = height(tree->R);
return 1+(lh>rh?lh:rh);
}
int height(){
return height(root);
}
void insert(Node*& tree, const T& d){
if(tree==NULL)
tree = new Node(d);
else if(ddata)
insert(tree->L, d);
else
insert(tree->R, d);
}
void insert(const T& d){
insert(root, d);
num++;
}
Node*& find(Node*& tree, const T& d){
if(tree==NULL) return tree;
if(tree->data==d) return tree;
if(ddata)
return find(tree->L, d);
else
return find(tree->R, d);
}
bool find(const T& d){
return find(root, d)!=NULL;
}
bool erase(const T& d){
Node*& pt = find(root, d);
if(pt==NULL) return false;
combine(pt->L, pt->R);
Node* p = pt;
pt = pt->R;
delete p;
num--;
return true;
}
void combine(Node* lc, Node*& rc){
if(lc==NULL) return;
if(rc==NULL) rc = lc;
else combine(lc, rc->L);
}
bool update(const T& od, const T& nd){
Node* p = find(root, od);
if(p==NULL) return false;
erase(od);
insert(nd);
return true;
}
};
int main()
{
bst b;
cout << "input some integers:";
for(;;){
int n;
cin >> n;
b.insert(n);
if(cin.peek()=='\n') break;
}
b.travel();
for(;;){
cout << "input data pair:";
int od, nd;
cin >> od >> nd;
if(od==-1&&nd==-1) break;
b.update(od, nd);
b.travel();
}
}
[/code]
同学,你们老师和我们老师留的作业是一模一样的阿,我有现成的做好了的程序,调试成功。这个程序的难点就在于这种很别扭的输入形式,所以我为它设计了一个结构体形式存放输入内容,再将它转化成了线性结构。
#include <iostream.h>
#include <stdlib.h>
struct inform /*建立输入信息结构体inform*/
{ char data;
int l;
int r;
int signl; /*作为标记的signl,signr*/
int signr;
};
struct leafnode /*建立叶子节点结构体*/
{
char leaf;
leafnode* lchild;
leafnode* rchild;
};
void print(inform* ps, int n);
void judge ( inform* ps );
leafnode* creatree(); /*声明二叉树的建立函数*/
void preorder (leafnode* T); /*声明先序遍历函数*/
void inorder (leafnode* T); /*声明中序遍历函数*/
void postorder (leafnode* T); /*声明后序遍历函数*/
char a[100];
int k=1;
int s=0;
inform *p;
void main()
{
/*-------------------------------按格式输入信息-----------------------------------*/
int n;
cout<<"请输入二叉树内容:第一行为节点总数n ,后面的n行是节点的具体形式:"<<endl;
cout<<"n= ";
cin>>n;
p=(inform* )malloc( n*sizeof(inform) ); /*开辟的一个叶子结构体型的指针数组*/
inform *p1; p1=p;
for(int i=0; i<n; i++)
{
cin>>(p+i)->data>>(p+i)->l>>(p+i)->r;
if((p+i)->l != -1) (p+i)->signl=1; /*用signl signr 的0,1标示输入的信息中是否有左或右孩子*/
else (p+i)->signl= 0;
if((p+i)->r !=-1) (p+i)->signr=1;
else (p+i)->signr= 0;
}
/*--------------------------------------------------------------------------------------------*/
a[0]= p->data;
judge ( p1 ); /*用递归算法将输入数据信息转为线性字符串*/
cout<<endl<<"输出转换的线性字符串: "<<endl;
cout<<a<<endl<<endl;
/*------------------------------------------遍历-----------------------------------*/
leafnode* T;
T= creatree();
/*先续遍历二叉树*/
cout<<"先序遍历二叉树: "<<endl;
preorder( T );
cout<<endl<<"中序遍历二叉树: "<<endl;
inorder ( T );
cout<<endl<<"后序遍历二叉树: "<<endl;
postorder( T );
cout<<endl<<endl;
}
/*------------------------------------------函数定义-------------------------------*/
void judge( inform* ps ) /*用函数的递归来将输入的信息转化为线性的数组*/
{
inform* b;
if (ps->signl==0)
{
a[k]='@';
k++;
}
else
{
b = p+(ps->l);
a[k] = b->data;
k++;
judge(b);
}
if ((ps->signr) == 0)
{
a[k]='@';
k++;
}
else
{
b = p+(ps->r );
a[k] = b->data;
k++;
judge(b);
}
}
leafnode* creatree() /*建立二叉树函数*/
{
char ch;
leafnode *t;
ch= a[s];
s++;
if(ch=='@')
{
t=NULL;
}
else
{
t=(leafnode* )malloc(sizeof(leafnode));
t->leaf=ch;
t->lchild=creatree();
t->rchild=creatree();
}
return t;
}
/*先序遍历的递归函数*/
void preorder (leafnode* T)
{
if(T)
{
cout<<T->leaf;
preorder(T->lchild);
preorder(T->rchild);
}
}
/*中序遍历的递归函数*/
void inorder (leafnode* T)
{
if(T)
{
inorder(T->lchild);
cout<<T->leaf;
inorder(T->rchild);
}
}
/*后序遍历的递归函数*/
void postorder (leafnode* T)
{
if(T)
{
postorder(T->lchild);
postorder(T->rchild);
cout<<T->leaf;
}
}
按你的描述,应该采用顺序表来存储数据。
楼上用的是动态链表来存储,显然不合要求。
但是按你描述的结构操作不方便,可不可以在后面的n行中再加一个整数表示其父结点,当父节点值为-1时表示根结点?
这样数据结构为:
typedef struct BTreeNode{
char ch;
int parent;
int lchild;
int rchild;
}BTreeNode;
struct BTree{
BTreeNode *body;/*用于存储二叉树的所有节点*/
int root;/*根节点序号*/
int count;/*节点数*/
}
待续。。。。
源代码:
typedef struct BTreeNode{
char ch;
int parent;
int lchild;
int rchild;
}BTreeNode;
struct BTree{
BTreeNode *body;/*用于存储二叉树的所有节点*/
int root;/*根节点序号*/
int count;/*节点数*/
}
/*创建二叉树*/
BTree createBTree(){
int i,n;
BTree bTree;
printf("请输入节点数:");
scanf("%d",&n);
bTree.body = (BTreeNode) malloc(sizeof(BTreeNode)*n);
bTree.root = -1;
bTree.count = n;
do{
for( i=0; i<n; i++ ){
printf("请输入:节点字符 父节点序号 左节点序号 右节点序号:\n");
scanf("%c %d %d %d",bTree.body[i]->ch,
bTree.body[i]->parent,
bTree.body[i]->lchild,
bTree.body[i]->rchild);
if( bTree.body[i]->parent == -1 ){
bTree.root = i;
break;
}
}
}while( bTree.root == -1 );
return bTree;
}
/*以输出遍历序列为例的遍历方法*/
void travel(BTree *bTree, int i, int pattern ){
/**
* pattern=0表示先序遍历,1表示中序,2表示后序;
*
*/
if( pattern == 0 )
printf("%c",bTree->body[i]->ch);
if( bTree->body[i]->lchild != -1 ){
travel(bTree, bTree->body[i]->lchild, pattern);
if( pattern == 1 )
printf("%c",bTree->body[i]->ch);
}
if( bTree->body[i]->rchild != -1 ){
travel(bTree, bTree->body[i]->rchild, pattern);
if( pattern == 2 )
printf("%c",bTree->body[i]->ch);
}
}
void travelBTree(BTree *bTree , int pattern ){
if( pattern == 0) printf("先序遍历:");
if( pattern == 1) printf("中序遍历:");
if( pattern == 2) printf("后序遍历:");
travel( bTree, bTree->root, pattern );
printf("\n");
}
/*通用遍历方法*/
void travel2(BTree *bTree, int i, int pattern, void (*func)(BTree *bTree,int i) ){
/**
* pattern=0表示先序遍历,1表示中序,2表示后序;
* void (*func)(BTree *bTree,int i)中func为函数指针,用于指定遍历进行的操作。
*/
if( pattern == 0 )
(*func)( bTree, i );
if( bTree->body[i]->lchild != -1 ){
travel2(bTree, bTree->body[i]->lchild, pattern, func);
if( pattern == 1 )
(*func)( bTree, i)
}
if( bTree->body[i]->rchild != -1 ){
travel2(bTree, bTree->body[i]->rchild, pattern, func);
if( pattern == 2 )
(*func)( bTree, i);
}
}
/*测试,数据自定*/
void print( BTree *bTree, int i ){
printf("%c",bTree->body[i]->ch );
}
void main(){
BTree bTree;
void (*func)( BTree *bTree, int i );
bTree = createBTree();
travelBTree( &bTree, 0 );
travelBTree( &bTree, 1 );
travelBTree( &bTree, 2 );
/*通用遍历算法测试*/
func = print;
travel2( &bTree, bTree.root, 0, func);
printf("\n");
travel2( &bTree, bTree.root, 1, func);
printf("\n");
travel2( &bTree, bTree.root, 2, func);
printf("\n");
}
运行时可能会报错,因为我没有调试,你自己调试一下。
我来了
O(∩_∩)O~
二叉树遍历结合例子具体讲解例子不能太简单
遍历的方法有:层序遍历、先序遍历、中序遍历、后序遍历等,以下面的二叉树为例介绍遍历 E \/ \\ B F \/ \\ \\ A D H \/ \/ \\ C G I \\ K \/ J 1.层序遍历 即从上到下按层次访问该树,每一层单独输出一行,每一层要求访问的顺序为从左到右。例子中层序遍历...
二叉树的遍历
对任意给定的二叉树(顶点数自定)建立它的二叉链表存储结构,并利用栈的五种基本运算(置空栈、进栈、出栈、取栈顶元素、判栈空)实现二叉树的先序、中序、后序三种遍历,输出三种遍... 对任意给定的二叉树(顶点数自定)建立它的二叉链表存储结构,并利用栈的五种基本运算(置空栈、进栈、出栈、取栈顶元素、判...
如何从后序遍历求原二叉树?
1、先求原始二叉树,后序遍历中最后出现的是根,所以A是整棵树的根,在结合中序遍历来看 BDCE是A的左子树,而FHG是A的右子树;2、BDCE序列中B是整个序列根,因为后序遍历中B最后出现。此时再看中序中根B左端没有左子 树,右端有DCE,所以DCE是B的右子树 ;3、再看D、C、E在后序遍历中C...
树的先序遍历与二叉树的先序遍历是相同的吗?
树的先根遍历和二叉树的先序遍历相同,后根遍历与二叉树的中序遍历相同。二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点...
二叉树的后续遍历是什么意思啊?
树的后序遍历是指先依次后序遍历每棵子树,然后访问根结点。当树用二叉树表示法(也叫孩子兄弟表示法)存储时,可以找到唯一的一棵二叉树与之对应,我们称这棵二叉树为该树对应的二叉树。那么根据这个法则可知,树的后序遍历序列等同于该树对应的二叉树的中序遍历。从二叉树的递归定义可知,一棵非空...
什么叫遍历算法(最好有例子)
遍历算法:所谓遍历(Traversal),是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。当然遍历的概念也适合于多元素集合的情况,如数组。遍历算法概念延伸:图遍历:图遍历又称图的...
二叉树有哪几种形式?
二叉树的五种形态:1、 空二叉树(什么都没有,nothing)2、 只有一个根节点的二叉树(左右子树为空)3、 右子树为空的二叉树(右腿断了)4、 左子树为空的二叉树(左腿断了)5、 左右子树都非空的的二叉树(既有左子树又有右子树,)...
二叉树的后序遍历序列为?
详解为:前序序列的顺序是根、左、右,序列ABCD第一个一定是根结点,A是根节点。中序序列顺序是左、根、右,因为A是根节点,所以DCB位于A左侧,A右侧没有结点,B是DCB三个结点中的根。前序序列是中左右,根结点为A;中序序列是左中右,左子树BCD;遵循遍历序列的规则排列出二叉树,得出后序遍历...
数据结构(树和二叉树)
树是n (n≥0) 个结点的有限集。 n=0 时称为空树。在任意一棵非空树中:二叉树是n个结点所构成的集合,它或为空树(n=0),或为非空树,对于非空树T:二叉树和树的区别:* 二叉树每个结点至多只有两颗子树。* 二叉树的子树有左右之分,其次序不能任意颠倒。1.顺序存储结构:使用一组...
数据结构题目二叉树遍历,哪位大神帮忙解答下,谢谢!
本题考察二叉树的遍历 二叉树的遍历一共有4中 前序遍历 中序遍历 后序遍历 层序遍历 略
容畏尼索: 二叉树的遍历有3种方式: a/ \/ \b e/ \ \/ \ \c d f(先序)先根遍历:(根左右)先访问根,再访问左子树,最后访问右子树,则可得如下的序列:abcdef(中序)中根遍历:(左根右)先访问左子树,再访问根,最后访问右子树,则可得...
凯里市18529526777: 二叉树的遍历? - ?
容畏尼索: 遍历方案从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成.因此,在任一给定结点上,可以按某种次序执行三个操作:(1)访问结点本身(N),(2)遍历该结点的左子树(L),(3)遍历该结点的右子树(R)...
凯里市18529526777: 数据结构二叉树怎么遍历啊?? - ?
容畏尼索: 拿先序遍历举例: 先序遍历 是根左右 先遍历根A,然后遍历A的左子树(是左面那一群),然后遍历A的右子树(为空). 在A的左子树中,先遍历根也就是B,在遍历B的左子树也就是C,在遍历B的右子树,是右边的一群. 在B的右子树中继续…………
凯里市18529526777: 何谓二叉树的遍历? - ?
容畏尼索: 就是按照一定的顺序访问二叉树中的每一个节点.顺序一般有先序遍历,中序遍历和后序遍历 1.中序遍历的递归算法定义: 若二叉树非空,则依次执行如下操作: (1)遍历左子树; (2)访问根结点; (3)遍历右子树.2.先序遍历的递归算...
凯里市18529526777: 二叉树遍历结合例子具体讲解例子不能太简单 - ?
容畏尼索: 遍历的方法有:层序遍历、先序遍历、中序遍历、后序遍历等,以下面的二叉树为例介绍遍历E/ \B F/ \ \A D H/ / \C G I\K/J 1.层序遍历即从上到下按层次访问该树,每一层单独输出一行,每一层要求访问的顺序为从左到右.例子中...
凯里市18529526777: 什么是二叉树数的遍历 - ?
容畏尼索: 二叉树遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问.访问结点所做的操作依赖于具体的应用问题.遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础. 遍历方案 从二叉树的递归定...
凯里市18529526777: 二叉树遍历 - ?
容畏尼索: 首先求二叉树中序遍历是关键.中序遍历(左中右)它可以区分一个树的左右子树,所以它可以跟先序遍历和后序遍历或者层序遍历结合.这道题主要根据层序遍历求出根节点位置,用中序求出左右子树的位置.这样可以画出整个二叉树.
凯里市18529526777: 遍历二叉树 - ?
容畏尼索: 同学,你们老师和我们老师留的作业是一模一样的阿,我有现成的做好了的程序,调试成功.这个程序的难点就在于这种很别扭的输入形式,所以我为它设计了一个结构体形式存放输入内容,再将它转化成了线性结构.#include <iostream.h>#...
凯里市18529526777: 二叉树的遍历怎么理解?如何理解遍历? - ?
容畏尼索: 你有图像没有,不然我就把遍历全过程告诉你了.一般先序遍历 :先遍历根节点,左子树,右子树.对于每个节点都那样.(大哥,你咋不上个图片,这样才好解释) 后续遍历:左子树 右子树 根节点http://zhidao.baidu.com/question/2074156498982637948.html?fr=uc_push&push=core&rpSampling=&entry=uhome_new&oldq=1
凯里市18529526777: 二叉树的遍历算法 - ?
容畏尼索: 怎么又来问了,不是回答过你了吗?很简单,就是一个递归过程.在函数中以先序遍历的第一个结点在中序遍历中为界把中序遍历分为两半,再分别把左一半和右一半作为这个结点的左子树和右子树进行递归.完成递归之后再打印该结点即可....