树的周边(二)树的基本操作:深度、宽度与遍历
来源:互联网 发布:1.0铝合金门窗价格算法 编辑:程序博客网 时间:2024/05/07 08:08
1.树的存储
树的数据结构中除了要存储本节点内的值之外,还要存储子结点的指针。可以定义为:
struct TreeNode{int data;TreeNode *left,*right;};
根据具体的需求情况,也可以添加其他的成员,比如可以添加一个指针指向其父结点。因为树的存储特点,一般情况下,各种树的操作都可以有递归与非递归两种方式,非递归的方法一般需要用到栈进行各层结点的逐层访问。
2.求树的深度。
树的深度是指树的层数。其中根节点位于树的第1层,依次累加。
参考:
http://blog.csdn.net/linraise/article/details/9878859
http://blog.csdn.net/walkinginthewind/article/details/7518888
递归的方式:
int treeDepth(TreeNode *p){if (!p){return 0;}int left = treeDepth(p->m_pLeft);int right = treeDepth(p->m_pRight);return left>right?left+1:right+1;}
非递归的方式:
int Depth(TreeNode *T){TreeNode * store[50];memset(store,0,50);int length=0;int depth=0;stack<TreeNode *> st;if (T){st.push(T);while (!st.empty()){depth++;length=0;while (!st.empty()){TreeNode * p=st.top();st.pop();if (p->m_pLeft)store[length++]=p->m_pLeft;if (p->m_pRight)store[length++]=p->m_pRight;}for (int i=0;i<length;i++)st.push(store[i]);}return depth;}elsereturn 0;}
3.树的宽度
根据上述非递归方式中获得最大的length即可得到树的‘宽度’,如此即可以获得树的面积=深度×宽度。
4.树的三种遍历
树有三种顺序遍历方式:中序遍历、前序遍历及后序遍历。中序遍历:左结点->当前结点->右结点 前序遍历:当前结点->左结点->右结点 后序遍历:左结点->右结点->当前结点。根据中序遍历和前序遍历或后序遍历可以唯一确定一棵树,但仅有一种遍历方式,或根据前后遍历不能唯一确定一棵树。
前序遍历:
递归方式:
void preorder(TreeNode *root){ if(root!=NULL){ cout<<root->data<<endl; preorder(root->left); preorder(root->right);}}
非递归方式:
void preorder(TreeNode*root){treeNode *p=root;stack<TreeNode*>s;while(p!=NULL || !s.empty()){while (p!=NULL){s.push(p);cout<<p->data<<endl;p=p->left;}if (!s.empty()){p=s.top();s.pop();p=p->right;}}}
中序遍历:
递归方式:
void inorder(TreeNode *root) //递归中序遍历{if(root!=NULL){inorder(root->lchild);cout<<root->data<<" ";inorder(root->rchild);}}
非递归方式:
void inorder(TreeNode *root) //非递归中序遍历{stack<TreeNode*> s;TreeNode *p=root;while(p!=NULL||!s.empty()){while(p!=NULL){s.push(p);p=p->lchild;}if(!s.empty()){p=s.top();cout<<p->data<<" ";s.pop();p=p->rchild;}} }
后序遍历:
递归方式:
void postorder(TreeNode *root) //递归后序遍历{if(root!=NULL){postorder(root->lchild);postorder(root->rchild);cout<<root->data<<" ";} }
非递归方式:
后序遍历的非递归实现是三种遍历方式中最难的一种。
因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
void postorder(TreeNode *root) //非递归后序遍历{stack<TreeNode*> s;TreeNode *cur; //当前结点 TreeNode *pre=NULL; //前一次访问的结点 s.push(root);while(!s.empty()){cur=s.top();if((cur->lchild==NULL&&cur->rchild==NULL)||(pre!=NULL&&(pre==cur->lchild||pre==cur->rchild))){cout<<cur->data<<" "; //如果当前结点无孩子或者孩子都已访问过 s.pop();pre=cur; }else{if(cur->rchild!=NULL)s.push(cur->rchild);if(cur->lchild!=NULL) s.push(cur->lchild);}} }
5.遍历重构与互换
根据前序/后序和中序遍历重构树或推知后序/前序遍历
void buildTree(char* pre,char* mid,TreeNode* &root){int len=strlen(pre); //实际下先序序列和后序序列的长度是一样的//序列长度为0,说明上一级已经是叶子节点,返回if(len==0){root=NULL;return;}//返回先序的第一个元素在中序中出现的位置char* p=strchr(mid,pre[0]);int pos=(int)(p-mid);//建设子树的根节点//先序中第一个元素作为本子树的根节点root->data=pre[0];if(pos!=0){ //当前节点的左子树是存在的TreeNode* left=(TreeNode*)malloc(sizeof(TreeNode));root->left=left;//左子树根节点上的元素就是先序中的第2个元素left->data=pre[1]; char* left_pre=(char*)malloc((pos+1)*sizeof(char));char* left_mid=(char*)malloc((pos+1)*sizeof(char));//找到左子树的先序和中序strncpy(left_pre,pre+1,pos);left_pre[pos]='\0';strncpy(left_mid,mid,pos);left_mid[pos]='\0';//递归建立左子树buildTree(left_pre,left_mid,left);}else{root->left=NULL;}if(pos!=len-1){ //当前节点的右子树是存在的TreeNode* right=(TreeNode*)malloc(sizeof(TreeNode));root->right=right;//右子树根节点上的元素就是先序中的第pos+2个元素right->data=pre[pos+1];char* right_pre=(char*)malloc((len-pos)*sizeof(char));char* right_mid=(char*)malloc((len-pos)*sizeof(char));//找到右子树的先序和中序strncpy(right_pre,pre+pos+1,len-1-pos);right_pre[len-pos-1]='\0';strncpy(right_mid,mid+pos+1,len-1-pos);right_mid[len-pos-1]='\0';//递归建立右子树buildTree(right_pre,right_mid,right);}else{root->right=NULL;}}
void postfrompremid(char *pre,char *mid,char*post){int length=strlen(pre);if (length==0){return;}post[length-1]=pre[0];char *midnode=strchr(mid,pre[0]);if (!midnode){cout<<"wrong input";return;}int midpos=(int)(midnode-mid);if (midpos!=0)//有左子树{char *leftpre=new char[midpos+1];char *leftmid=new char[midpos+1];strncpy(leftpre,pre+1,midpos);leftpre[midpos]='\0';strncpy(leftmid,mid,midpos);leftmid[midpos]='\0';postfrompremid(leftpre,leftmid,post);delete [] leftpre;delete [] leftmid;}if (midpos!=length-1)//有右子树{char *rightpre = new char[length-midpos];char *rightmid = new char[length-midpos];strncpy(rightpre,pre+midpos+1,length-midpos-1);strncpy(rightmid,mid+midpos+1,length-midpos-1);rightpre[length-midpos-1]='\0';rightmid[length-midpos-1]='\0';postfrompremid(rightpre,rightmid,post+midpos);delete [] rightpre;delete [] rightmid;}}
- 树的周边(二)树的基本操作:深度、宽度与遍历
- 线索二叉树的遍历与基本操作(史上最全)
- 二叉树的一些基本操作(括号表示法,宽度,深度,结点个数,叶子节点个数)
- 二叉树的建立(非递归建立与定义建立)与基本操作(广度和深度遍历,求叶子树高)实现
- 7.4 7.5 二叉树的基本操作与遍历
- C语言实现二叉树的基本操作---创建、遍历、求深度、求叶子结点
- C二叉树的基本操作---创建、遍历、求深度、求叶子结点
- C语言实现二叉树的基本操作---创建、遍历、求深度、求叶子结点
- 树的宽度优先遍历
- C语言基本数据结构之二(二叉树的三种遍历,节点数以及深度算法)
- 二叉树的各种操作(遍历/深度/距离/转换)
- 二叉树的基本操作(创建、递归和非递归遍历、求深度、求叶子数)
- 二叉树的基本操作精集(创建、遍历、求深度结点以及叶子结点个数)
- 二叉链表的存储结构和基本操作(各种遍历、求树深度、求树叶个数)
- 二叉树的建立和基础操作<二> —— (层次遍历和计算二叉树的宽度)
- [数据结构]求二叉树的深度与宽度
- 求二叉树的最大深度与最大宽度
- codevs1501 二叉树的最大宽度与深度
- c#判断点是否在直线上
- 暂停想想
- asp.net+c#图片压缩后上传
- 自己看看
- MYSQL 数据表中行存在时更新,不存在时插入的SQL语句
- 树的周边(二)树的基本操作:深度、宽度与遍历
- Android.mk的用法和基础
- java实现socket多线程代码
- Computer Science Vocabulary
- JS中window.open全屏
- centos 下安装qtcreator 之路
- linux 各版本介绍
- 使用dmbs_stats.gather_table_stats做分析后执行计划不准的解决方法
- dojo中的事件处理