【说明】:
本文是左程云老师所著的《程序员面试代码指南》第三章中“打印二叉树的边界节点”这一题目的C++复现。
本文只包含问题描述、C++代码的实现以及简单的思路,不包含解析说明,具体的问题解析请参考原书。
感谢左程云老师的支持。
【题目】:
给定一颗二叉树的头节点 head,按照如下两种标准分别实现二叉树边界节点的逆时针打印。
标准一:
1、头节点为边界节点;
2、叶节点为边界节点;
3、如果节点在其所在的层中是最左或者最右的,那么也是边界节点。
标准二:
1、头节点为边界节点;
2、叶节点为边界节点;
3、树左边界延伸下去的路径为边界节点;
4、树右边界延伸下去的路径为边界节点。
例如,如下图所示的树。
按标准一打印的结果为:1,2,4,7,11,13,14,15,16,12,10,6,3
按标准一打印的结果为:1,2,4,7,13,14,15,16,10,6,3
【要求】:
1、如果节点数为 N,两种标准实现的时间复杂度要求都为 O(N),额外的空间复杂度要求为 O(h),h 为二叉树的高度;
2、两种标准都要求逆时针顺序不重复的打印所有边界节点。
【思路】:
标准一解法:打印每一层的最左节点->打印非最左最右的叶子节点->打印每一层的最右节点
标准二解法:第一个既有左孩子又有右孩子的节点及其这个节点前的节点全部打印->打印从这个节点开始的左边界延伸路径以及叶子节点->打印从这个节点开始的右边界的延伸路径以及叶子节点。
【编译环境】:
CentOS6.7(x86_64)
gcc 4.4.7
【实现及测试】:
声明代码:
1 /* 2 *文件名:bt_print.h 3 *作者: 4 *摘要:按一定规则打印二叉树的边界 5 */ 6 #ifndef _BT_PRINT 7 #define _BT_PRINT 8 9 class Node 10 { 11 public: 12 Node(int data) 13 { 14 value = data; 15 left = NULL; 16 right = NULL; 17 } 18 public: 19 int value; 20 Node *left; 21 Node *right; 22 }; 23 24 //规则一所涉及的接口 25 26 //获得二叉树的高度 27 int getHeight(Node *root,int l=0); 28 //获得每一层的最左边和最右边节点 29 void setEdgeMap(Node *root,Node *edgeMap[][2],int l=0); 30 //打印既不是左边界也不是右边界的叶子节点 31 void printLeafNotInMap(Node *root,Node *edgeMap[][2],int l=0); 32 //打印规则一所要求的边界节点 33 void printEdge1(Node *root); 34 35 //规则二所涉及的接口 36 void printLeftEdge(Node *root,bool print);//打印左边界的延伸路径 37 void printRightEdge(Node *root,bool print);//打印右边界的延伸路径 38 void printEdge2(Node *root); 39 40 #endif
标准一的实现代码:
1 /* 2 *文件名:bt_print1.cpp 3 *作者: 4 *摘要:规则一的实现 5 */ 6 7 #include <iostream> 8 #include <algorithm> 9 #include <string.h> 10 #include "bt_print.h" 11 12 using namespace std; 13 14 int getHeight(Node *root,int l) 15 { 16 if(NULL == root) 17 return l; 18 return max(getHeight(root->left,l+1),getHeight(root->right,l+1)); 19 } 20 21 void setEdgeMap(Node *root,Node *edgeMap[][2],int l) 22 { 23 if(NULL == root) 24 return ; 25 edgeMap[l][0] = edgeMap[l][0] == NULL ? root : edgeMap[l][0];//第一列保存左边界 26 edgeMap[l][1] = root;//第二列保存右边界 27 setEdgeMap(root->left,edgeMap,l+1); 28 setEdgeMap(root->right,edgeMap,l+1); 29 return ; 30 } 31 32 void printLeafNotInMap(Node *root,Node *edgeMap[][2],int l) 33 { 34 if(NULL == root) 35 return ; 36 if(NULL == root->left && NULL == root->right && root != edgeMap[l][0] && root != edgeMap[l][1]) 37 cout << root->value << " "; 38 printLeafNotInMap(root->left,edgeMap,l+1); 39 printLeafNotInMap(root->right,edgeMap,l+1); 40 return ; 41 } 42 43 void printEdge1(Node *root) 44 { 45 if(NULL == root) 46 return; 47 const int height = getHeight(root); 48 49 //Node *edgeMap[height][2] = {NULL}; //错误:可变大小的对象‘edgeMap’不能被初始化 50 Node *edgeMap[height][2]; 51 memset(edgeMap,0,height*2*sizeof(Node*)); //将edgeMap相关的内存空间置为0(NULL) 52 setEdgeMap(root,edgeMap); 53 54 for(int i=0; i <height; i++) //打印左边界 55 cout << edgeMap[i][0]->value << " "; 56 57 printLeafNotInMap(root,edgeMap); //打印既不属于左边界也不属于右边界的叶子节点 58 59 for(int i=height-1;i>=0;i--) 60 { 61 if(edgeMap[i][0] != edgeMap[i][1]) //防止头节点那种情况 62 cout << edgeMap[i][1]->value << " "; 63 } 64 65 cout << endl; 66 return; 67 }
标准二的实现代码:
1 /* 2 *文件名:bt_print2.cpp 3 *作者: 4 *摘要:规则二的实现 5 */ 6 7 #include <iostream> 8 #include "bt_print.h" 9 10 using namespace std; 11 12 /* 13 *函数介绍:打印左边界的延伸路径及叶子节点 14 *输入参数:root为根节点;print为打印标志 15 *输出参数:无 16 *返回值:无 17 */ 18 19 20 void printLeftEdge(Node *root,bool print) 21 { 22 if(NULL == root) 23 return ; 24 if( print || (NULL == root->left && NULL == root->right) ) 25 cout << root->value << " "; 26 printLeftEdge(root->left,print);//左边界左延伸必定打印 27 printLeftEdge(root->right, print && root->left == NULL ? true : false);//若左延伸为NULL,则打印右分支 28 return ; 29 } 30 31 void printRightEdge(Node *root,bool print) 32 { 33 if(NULL == root) 34 return ; 35 //运算符等级:== 大于 && 大于 ?: 36 printRightEdge(root->left, print && root->right == NULL ? true : false);//若右延伸为NULL,则打印左分支 37 printRightEdge(root->right,print);//右边界的右延伸必定打印 38 if( print || (NULL == root->left && NULL == root->right) ) 39 cout << root->value << " "; 40 return ; 41 } 42 43 void printEdge2(Node *root) 44 { 45 if(NULL == root) 46 return ; 47 cout << root->value << " "; 48 if(NULL != root->left && NULL != root->right) 49 { 50 printLeftEdge(root->left,true); 51 printRightEdge(root->right,true); 52 } 53 else 54 { 55 printEdge2(NULL != root->left ? root->left : root->right); 56 } 57 cout << endl; 58 return ; 59 }
测试代码:
1 /* 2 *文件名:main.cpp 3 *作者: 4 *摘要:测试代码 5 */ 6 7 #include <iostream> 8 #include "bt_print.h" 9 10 using namespace std; 11 12 //由数组创建二叉树,数组中的-1值意味此节点为空节点 13 void createBT(Node **head,int arr[],int len,int index=0) 14 { 15 if(index > len-1 || -1 == arr[index] ) 16 return; 17 (*head) = new Node(arr[index]); 18 createBT(&((*head)->left),arr,len,2*index+1); 19 createBT(&((*head)->right),arr,len,2*index+2); 20 } 21 22 //前序遍历 23 void preOrderRecur(Node *head) 24 { 25 if(NULL == head) 26 return ; 27 cout << head->value << " "; 28 preOrderRecur(head->left); 29 preOrderRecur(head->right); 30 } 31 32 int main() 33 { 34 int arr[63] = {1,2,3,-1,4,5,6,-1,-1,7,8,9,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; 35 Node *root = NULL; 36 createBT(&root,arr,63); 37 cout << "Using recursive method to achieve pre-order traversal:" << endl; 38 preOrderRecur(root); 39 cout << endl; 40 41 cout << "Using the Fir method to print the edge of the bt:"<< endl; 42 printEdge1(root); 43 44 cout << "Using the Sec method to print the edge of the bt:"<< endl; 45 printEdge2(root); 46 47 return 0; 48 }
【说明】:
1、标准一是比较好理解的,即将二叉树的外围打印出来;
2、标准二不太好理解(我也有点迷糊),大家可以看看左老师的原书;
3、为了构造左老师书中所举的例子,我写了那么个数组,不知到小伙伴们有木有更好的方法?
注:
转载请注明出处;
转载请注明源思路来自于左程云老师的《程序员代码面试指南》。