树是一种数据结构,是由n (n>=1)个有限点组成一个具有层次关系的集合
而二叉树是每个结点最多有两个子树的树结构
下面是关于二叉树的一些简单操作
在之前写过的链表中,是用链表的头节点的指针来表示一个链表,那么在二叉树中,同样是使用根节点的指针来表示一个二叉树
4 typedef struct TreeNode 5 { 6 TreeNodeType data; 7 struct TreeNode* lchild; 8 struct TreeNode* rchild; 9 }TreeNode; 10
1.二叉树的初始化
8 void TreeInit(TreeNode** pRoot) 9 { 10 if(pRoot == NULL) 11 { 12 //非法输入 13 return ; 14 } 15 *pRoot = NULL; 16 return; 17 }
2.二叉树的先序、中序、后续、层序
(1)二叉树的先序
二叉树的先序的顺序为先遍历树的根节点,再遍历根节点的左子树,最后再遍历根节点的右子树
上面这张图的先序遍历结果为ABDEGCF
下面是使用递归的方法来遍历地得出二叉树的先序遍历,首先先访问根节点(即打印根节点),再分别遍历左右子树
34 //先序遍历树 35 void TreePreOrder(TreeNode* root) 36 { 37 if(root == NULL) 38 { 39 //空树 40 return ; 41 } 42 //先访问根节点 43 printf("%c",root->data); 44 //遍历左子树 45 TreePreOrder(root->lchild); 46 //遍历右子树 47 TreePreOrder(root->rchild); 48 return ; 49 }
(2)二叉树的中序
二叉树的中序遍历顺序为先遍历二叉树的左子树,再遍历二叉树的根节点,最后再遍历右子树
上面这张图的中序遍历结果为DBGEACF
下面是使用递归的方法来遍历地得出二叉树的中序遍历,首先先遍历左子树,再访问根节点(即打印根节点),最后再遍历右子树
51 //中序遍历树 52 void TreeInOrder(TreeNode* root) 53 { 54 if(root == NULL) 55 { 56 //空树 57 return ; 58 } 59 //遍历左子树 60 TreeInOrder(root->lchild); 61 //先访问根节点 62 printf("%c",root->data); 63 //遍历右子树 64 TreeInOrder(root->rchild); 65 return ; 66 }(3)二叉树的后序
二叉树的后序遍历顺序为先遍历二叉树的左子树,再遍历二叉树的右子树,最后再遍历二叉树的根节点
上面这张图的后序遍历结果为DGEBFCA
下面是使用递归的方法来遍历地得出二叉树的后序遍历,首先先遍历左子树,再遍历右子树,最后再访问根节点(即打印根节点)
69 //后序遍历树 70 void TreePostOrder(TreeNode* root) 71 { 72 if(root == NULL) 73 { 74 //空树 75 return ; 76 } 77 //遍历左子树 78 TreePostOrder(root->lchild); 79 //遍历右子树 80 TreePostOrder(root->rchild); 81 //先访问根节点 82 printf("%c",root->data); 83 return ; 84 }
扫描二维码关注公众号,回复:
699385 查看本文章
(4)二叉树的层序
二叉树的层序就是按照二叉树的层来进行打印
下面是通过使用一个数组来实现这个操作,首先先创建一个数组,将树的根节点插入到链表的头部,然后取队首,进行打印,并出队列,然后将刚出队列的元素的左右孩子入队列,然后再取队首元素,重复上面的操作,详细过程如图所示
86 //层序遍历树 87 void TreeLevelOrder(TreeNode* root) 88 { 89 if(root == NULL) 90 { 91 //空树 92 return ; 93 } 94 SeqQueue queue; 95 SeqQueueInit(&queue); 96 SeqQueuePush(&queue,root); 97 TreeNode* cur=NULL; 98 while(SeqQueueFront(&queue,&cur)) 99 { 100 SeqQueueType front; 101 int ret=SeqQueueFront(&queue,&front); 102 if(ret = 0) 103 { 104 //取队首元素失败,说明队列为空 105 break; 106 } 107 //打印当前值,把当前对手元素出队列 108 printf("%c",front->data); 109 SeqQueuePop(&queue); 110 //把当前左右子树入队列 111 if(front->lchild!=NULL) 112 { 113 SeqQueuePush(&queue,front->lchild); 114 } 115 if(front->rchild!=NULL) 116 { 117 SeqQueuePush(&queue,front->rchild); 118 } 119 } 120 printf("\n"); 121 } 122
(5)还原一棵树
还原一棵树就是给出一个数组,根据数组的内容,构建出一棵树
这个操作还是要使用递归来完成,详细过程见程序
123 //还原一棵树 124 //输入一个数组(数组对每个元素就是树上对节点) 125 //根据数组内容,构建出一棵树,数组中元素内容符合树对先序遍历结果 126 127 //辅助递归函数 128 TreeNode* _TreeCreate(TreeNodeType data[],size_t size,size_t* index,TreeNodeType null_node) 129 { 130 if(index == NULL) 131 { 132 //非法输入 133 return NULL; 134 } 135 if(*index >= size) 136 { 137 //数组遍历结束 138 return NULL; 139 } 140 if(data[*index] == null_node) 141 { 142 //空树 143 return NULL; 144 } 145 TreeNode* new_node=CreateTreeNode(data[*index]); 146 //先 147 ++(*index); 148 new_node->lchild=_TreeCreate(data,size,index,null_node); 149 //后 150 ++(*index); 151 new_node->rchild=_TreeCreate(data,size,index,null_node); 152 return new_node; 153 } 154 TreeNode* TreeCreate(TreeNodeType data[] ,size_t size,char null_node) 155 { 156 //1.根据index指向对元素内容创建一个节点 157 //2.先++index后递归的构架新的节点的左子树 158 //3.再++index后递归的构架新的节点的右子树 159 size_t index=0;//表示当前取数组中对那个元素 160 return _TreeCreate(data,size,&index,null_node);//辅助完成递归 161 } 162
(6)二叉树的拷贝
树的拷贝有两种,一种是浅拷贝,一种是深拷贝
浅拷贝(copy)是指内存是同一块内存,两个对象,相当于是指针赋值,两个对象共享同一块内存
深拷贝(clone)是再申请一块内存,
浅拷贝的优点在于,执行效率高,但是缺点是若改变一处则对所有的对象都生效
深拷贝的优点在于,改变所有的对象都不会受影响,但缺点在于执行效率低
164 //树的拷贝 165 TreeNode* TreeClone(TreeNode* root) 166 { 167 if(root == NULL) 168 { 169 //空树 170 return NULL; 171 } 172 //按照先序方式遍历 173 TreeNode* new_node=CreateTreeNode(root->data); 174 new_node->lchild=TreeClone(root->lchild); 175 new_node->rchild=TreeClone(root->rchild); 176 return new_node; 177 } 178
(7)二叉树的销毁
是按照二叉树的后序遍历的方式来进行销毁的
179 //二叉树的销毁 180 void TreeDestroy(TreeNode* root) 181 { 182 if(root == NULL) 183 { 184 //空树 185 return; 186 } 187 //按照后序遍历的方式销毁树 188 TreeNode* lchild=root->lchild; 189 TreeNode* rchild=root->rchild; 190 TreeDestroy(root->lchild); 191 TreeDestroy(root->rchild); 192 DestroyTreeNode(root); 193 return; 194 } 195(8)求二叉树节点个数
方法一:使用递归的方式来实现,详细见程序
196 //求二叉树节点个数(方法一) 197 void _TreeSize(TreeNode* root,size_t* size) 198 { 199 if(root == NULL) 200 { 201 //空树 202 return ; 203 } 204 ++(*size); 205 _TreeSize(root->lchild,size); 206 _TreeSize(root->rchild,size); 207 } 208 209 size_t TreeSize1(TreeNode* root) 210 { 211 size_t size=0; 212 //运用_TreeSize来辅助完成递归 213 _TreeSize(root,&size); 214 return size; 215 } 216
方法二:直接采用递归的方式来实现
217 //求二叉树节点个数(方法二) 218 //直接采用递归对方法,不如方法一那么直观,但是代码很简单 219 size_t TreeSize2(TreeNode* root) 220 { 221 if(root == NULL) 222 { 223 //空树 224 return 0; 225 } 226 return 1+TreeSize2(root->lchild)+TreeSize2(root->rchild); 227 } 228
(9)求二叉树叶子节点的个数
叶子节点就是二叉树最下面一层的节点,即没有左右孩子的节点,采用递归的方式来实现
229 //求二叉树叶子节点个数 230 size_t TreeLeafSize(TreeNode* root) 231 { 232 if(root == NULL) 233 { 234 //空树 235 return 0; 236 } 237 if(root->lchild == NULL && root->rchild == NULL) 238 { 239 //root是叶子节点 240 return 1; 241 } 242 //root不是叶子节点 243 //采用递归的方式 244 //叶子节点的个数=左子树叶子节点+右字数叶子节点 245 return TreeLeafSize(root->lchild)+TreeLeafSize(root->rchild); 246 } 247
(10)求第K层节点的个数
采用递归的方式来实现
248 //求二叉树第K层节点的个数 249 //采用递归的方法 250 size_t TreeKLevelSize(TreeNode* root,int k) 251 { 252 if(root == NULL || k<1) 253 { 254 //非法操作 255 return 0; 256 } 257 if(k == 1) 258 { 259 return 1; 260 } 261 return TreeKLevelSize(root->lchild,k-1)+TreeKLevelSize(root->rchild,k-1); 262 } 263
(11)求二叉树的高度
264 //求二叉树的高度 265 size_t TreeHeight(TreeNode* root) 266 { 267 if(root == NULL) 268 { 269 //空树 270 return 0; 271 } 272 size_t lheight=TreeHeight(root->lchild); 273 size_t rheight=TreeHeight(root->rchild); 274 return 1+(lheight>rheight?lheight:rheight); 275 } 276
(12)在二叉树中查找节点
277 //在二叉树中查找节点,给定一个数值求出对应对指针 278 //假设二叉树中的节点不重复 279 TreeNode* TreeFind(TreeNode* root,TreeNodeType to_find) 280 { 281 if(root == NULL) 282 { 283 //空树 284 return NULL; 285 } 286 if(root->data == to_find) 287 { 288 return root; 289 } 290 TreeNode* lresult=TreeFind(root->lchild,to_find); 291 TreeNode* rresult=TreeFind(root->rchild,to_find); 292 return lresult!=NULL?lresult:rresult; 293 } 294
(13)求当前节点的父节点
295 //求出当前节点的父节点 296 TreeNode* TreeParent(TreeNode* root,TreeNode* child) 297 { 298 if(root == NULL || child == NULL) 299 { 300 //空树||非法输入 301 return NULL; 302 } 303 if(root->lchild == child || root->rchild == child) 304 { 305 return root; 306 } 307 TreeNode* lresult=TreeParent(root->lchild,child); 308 TreeNode* rresult=TreeParent(root->rchild,child); 309 return lresult!=NULL?lresult:rresult; 310 }
(14)求当前节点的左右孩子节点
312 //返回当前节点的左右子树 313 TreeNode* TreeLChild(TreeNode* node) 314 { 315 if(node == NULL) 316 { 317 return NULL; 318 } 319 return node->lchild; 320 } 321 322 TreeNode* TreeRChild(TreeNode* node) 323 { 324 if(node == NULL) 325 { 326 return NULL; 327 } 328 return node->rchild; 329 }