前些天看数据结构,看到了二叉树的相关操作,我觉得,链表搞透彻了,这些东西都不是问题,还有栈、队列之类的,都是基于结构体和指针,其原理和链表相差无几,接下来来分享一下二叉树的创建以及各种遍历方法:
二叉树简介
二叉树(英语:Binary tree)是每个节点最多只有两个分支(不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”和“右子树”。二叉树的分支具有左右次序,不能颠倒。
二叉树的第层至多拥有
个节点数;深度为
的二叉树至多总共有
个节点数(定义根节点所在深度
),而总计拥有节点数匹配的,称为“满二叉树”;
深度为有
个节点的二叉树,当且仅当其中的每一节点,都可以和同样深度
的满二叉树,序号为1到
的节点一对一对应时,称为“完全二叉树”;
二叉树创建
首先定义结构体:
1 typedef struct Tree 2 { 3 int data; // 数据域 4 struct Tree *left, *right; // 左孩子、右孩子 5 }Tree;
接着采用递归的方法进行创建、以先序遍历的方法,附上代码:
1 void create ( Tree* &T) 2 int n; 3 scanf ( "%d", &n ); // 输入节点数据 4 if ( n == 0 ) // 也可以改成其他的,这里仅作为空节点的标志 5 { 6 T = NULL; 7 } 8 else 9 { 10 T = new Tree; 11 T -> data = n; 12 create ( T -> left ); // 递归创建左孩子 13 create ( T -> right ); // 递归创建右孩子 14 } 15 }
这样一个先序遍历创建的二叉树就建立好了,紧接着就是遍历了,通过遍历可以处理一些基本的操作(增、删、改、查);
二叉树的遍历分为先序遍历、中序遍历、后序遍历以及层次遍历:
![来源于维基百科](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Binary_tree.svg/300px-Binary_tree.svg.png)
先序遍历:
先访问根节点、如果有左孩子,继续访问左孩子,然后将左孩子当作下一个根节点继续重复访问,遇到无左孩子(左孩子为空节点)时开始访问其根节点的右孩子;直到所有的否访问完,退出(图中顺序:2->7->2->6->5->11->5->9->4)。中序遍历:
首先判断是否为空二叉树,如果不是,判断是否有左孩子,如果有访问左孩子,如何继续循环判断,如果没有,开始访问相对根节点(即该节点无孩子,访问该节点的父节点),如何访问右孩子;直到遍历结束退出(途中顺序:2->7->5->6->11->2->5->9->4)。后序遍历:
首先判断是否为空,然后判断是否有左孩子,如果有,访问左孩子,并将左孩子当作根节点继续重复,直到无左孩子时,访问其根节点的右孩子,最后访问根节点,直到遍历结束退出(图中顺序:2->5->11->6->7->4->9->5->2)层次遍历:
层次遍历相对来说好理解一点,就是按行访问,第一行访问结束开始访问下一行,图中的顺序为2->7->5->2->6->9->5->11->4 我相信把访问顺序列出来就应该很容易就理解了。虽然好理解,但是实现起来却不容易;其实先序中序后续听起来很复杂,弄懂了之后会发现特别简单,先序:跟、左、右;中序:左、根、右;后序:左、右、根;你可以这样理解:按什么顺序,就怎么访问根节点,先序就先访问根节点,中序的话,根节点就放到中间访问,后序就最后访问根节点;理解之后三种顺序搞出来一个,其他两个就So easy 了。
接下来放代码:
1 void out1 ( Tree* T ) // 先序遍历输出 2 { 3 if ( T ) 4 { 5 printf ( "%d ", T -> data ); // 先访问根节点 6 out1 ( T -> left ); 7 out1 ( T -> right ); 8 } 9 } 10 void out2 ( Tree* T ) // 中序遍历输出 11 { 12 if ( T ) 13 { 14 out2 ( T -> left ); 15 printf ( "%d ", T -> data ); // 中间访问根节点 16 out2 ( T -> right ); 17 } 18 } 19 void out3 ( Tree* T ) // 后序遍历输出 20 { 21 if ( T ) 22 { 23 out3 ( T -> left ); 24 out3 ( T -> right ); 25 printf ( "%d ", T -> data ); // 最后访问根节点 26 } 27 }
仔细观察不难发现:这三种顺序的代码仅仅是换了位置,代码本身并没有变化,这里的遍历以输出为例,增删改查都是同样的操作。
1 void out4 ( Tree* T ) // 层次遍历创建 2 { 3 int rear = -1, front = 0; 4 Tree *nums[100]; 5 if ( !T ) 6 { 7 return; 8 } 9 nums[front] = T; 10 while ( front != rear ) 11 { 12 printf ( "%d ",nums[++rear] -> data ); 13 if ( T -> left ) 14 { 15 nums[++front] = T -> left; 16 } 17 if ( T -> right ) 18 { 19 nums[++front] = T -> right; 20 } 21 T = nums[rear+1]; 22 } 23 }
怎么样,层次遍历还像你想的那么简单了吗?
参差遍历我觉得不用递归的简单些,而且层次遍历相对于先序中序后序来说不是重点,大家选择性的看一下,这里的代码中运用到栈的相关思想,我相信看到二叉树的情况下,栈应该都不成问题了。
最后主函数调用一下即可:
1 #include "stdio.h" 2 3 int main ( ) 4 { 5 printf ( "int 型,0 表示空节点:" ); 6 T = new Tree; 7 create ( T ); 8 printf ( "先序遍历输出:" ); 9 out1 ( T ); 10 printf ( " 中序遍历输出:" ); 11 out2 ( T ); 12 printf ( " 后序遍历输出:" ); 13 out3 ( T ); 14 printf ( " 层次遍历输出:" ); 15 out4 ( T ); 16 printf ( " " ); 17 return 0; 18 }