二叉树算法的实现(递归与非递归)

来源:互联网 发布:新开淘宝店物流 编辑:程序博客网 时间:2024/06/05 09:57

二叉树是数据结构中的基本类型之一,相关算法在各种框架,项目中运用很多,而且笔试面试也很容易考到,这里解释一下相关算法和原理。


采用链式存储结构,树的每个节点类型:

typedef struct _tree_node { //二叉树节点类型struct _tree_node *lchild, *rchild;type_data data_field[MAX_IN_NODE];}node_data, *node_point;
其中data_field为字符数组:

typedef char type_data, *type_point;
一、创建二叉树

创建时按照递归的方式进行,代码如下:

node_point create_bitree(){node_point cp = NULL, addr = NULL;char node_str[MAX_IN_NODE] = {0};fprintf(stdout, "Input the node data:");fgets(node_str, MAX_IN_NODE, stdin);if(strncmp(node_str, "\n", 1) == 0) {return NULL;} //若直接输入回车,则表示不创建该节点.cp = node_malloc();strncpy(cp->data_field, node_str, MAX_IN_NODE);addr = create_bitree();printf("addr 0x%x\n", cp->lchild = addr);addr = create_bitree();printf("addr 0x%x\n", cp->rchild = addr);fprintf(stdout, "node addr 0x%8x\n", cp);return cp;}

如想要构建如下二叉树时,应该以这样的顺序输入:a -> b -> d -> <enter> -> <enter> -> <enter> -> c -> e -> <enter> -> <enter> -> f -> <enter> -> <enter>


二、二叉树遍历

二叉树的遍历主要分为三种,以根节点的访问顺序划分为:

1,先序遍历:先访问根节点,再访问左子树,最后访问右子树。

2,中序便利:左-》中-》右

3,后序遍历:左-》右-》中

递归方法遍历二叉树:

先序:

void preorder_travel(node_point pt){if(pt != NULL) {//printf("node data:%s", pt->data_field);visit(pt);printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);preorder_travel(pt->lchild);preorder_travel(pt->rchild);}}
中序:

void inorder_travel(node_point pt){if(pt != NULL) {printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);inorder_travel(pt->lchild);visit(pt);inorder_travel(pt->rchild);}}
后序:

void postorder_travel(node_point pt){if(pt != NULL) {printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);postorder_travel(pt->lchild);postorder_travel(pt->rchild);visit(pt);}}
可以观察到,三种顺序遍历方法只是访问根节点的语句位置变了。抹去与递归无关的visit语句,则三个遍历算法完全相同。

非递归:

仿照递归工作时,每次栈的变化情况,可以发现遍历时的一些规律:

(1)节点指针不为空时,将其入栈,之后跳转至其左子树。

(2)节点指针为空时,若栈不为空,说明当前分支到尽头了,需要返回上级节点(类似递归调用的返回)。弹栈后,此时指针重新指回局部二叉树 的根节点,然后再指向右子树。

按照以上原则,写出中序遍历二叉树的非递归算法如下:

栈结构:

typedef struct _stack_type {node_point *bottom, *top;int length;}stack_data, *stack_point;//栈类型
函数代码:

void inorder_travel_stack(node_point pts){stack_data sp;node_point travel_p = pts;int cnt = 0;if(!stack_malloc(100, &sp)) {printf("Stack init failed!\n");return;}while((travel_p != NULL) || (stack_empty(&sp) != 1)) {if(travel_p != NULL) {stack_push(travel_p, &sp);travel_p = travel_p->lchild;} else {travel_p = stack_pop(&sp);visit(travel_p);travel_p = travel_p->rchild;}/* *printf("CNT%d\n", cnt++); */}}

三、总结

二叉树的代码虽然看起来简短,但其中要理解的逻辑还是挺多的,只不过递归调用使代码简化了许多(与非递归,用栈的方式相比),但代价是堆栈段占用大量空间,并且速度也会减慢。总之,作为常见数据结构之一,基础还是要掌握好。

说明下,忘了写摧毁二叉树的函数了,这里简单说下思路吧,跟后序遍历二叉树类似,只不过把visit访问节点的操作变成了释放节点内存空间,即free操作。

附完整代码,环境linux,编译器gcc:

tree.h

#ifndef __TREE_H#define __TREE_H#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define MAX_IN_NODE 128typedef char type_data, *type_point;//typedef int (*node_visit)(node_point );typedef struct _tree_node { //二叉树节点类型struct _tree_node *lchild, *rchild;type_data data_field[MAX_IN_NODE];}node_data, *node_point;typedef struct _stack_type {node_point *bottom, *top;int length;}stack_data, *stack_point;//栈类型static node_point node_malloc(void);//节点分配static node_point node_free(node_point );//节点释放static int node_visit(node_point );//访问节点数据static int stack_malloc(int , stack_point);//非递归操作时需要使用的栈空间,参数为栈长度static int stack_push(node_point ,stack_point );//将指针信息入栈static node_point stack_pop(stack_point );//弹出栈顶元素static int stack_empty(stack_point );//判断栈是否为空/* * 通过递归进行操作 * */node_point create_bitree(void);//创建二叉树根节点,参数为根节点数据域内容。void preorder_travel(node_point);//先序遍历二叉树,并对节点进行visit操作void inorder_travel(node_point );//中序遍历二叉树void postorder_travel(node_point );//后序遍历二叉树void levelorder_travel(node_point );//层序遍历二叉树/* * 非递归操作 * */void preorder_travel_stack(node_point);void inorder_travel_stack(node_point);void postorder_travel_stack(node_point);//unfinished functionsint insert_node(node_point );//以某种算法,将建立节点,插入至特定树中位置int delete_node(type_point );//删除数据域为type_point相同的数据的节点#endif
tree.c
#include "tree.h"node_point node_malloc(){unsigned int node_length = sizeof(node_data);node_point mem_point = malloc(node_length);if(mem_point == NULL) {perror("tree node malloc failed:");exit(EXIT_FAILURE);}memset(mem_point, 0, node_length);mem_point->lchild = mem_point->rchild = NULL;return mem_point;}node_point node_free(node_point fp){if(fp != NULL) {free(fp);fp = NULL;} elsefprintf(stdout, "Point has already been null!\n");return fp;}int visit(node_point rd){if(rd == NULL) {fprintf(stdout, "branch end.\n");return 0;} else {fprintf(stdout, "node value: %s", rd->data_field);return 1;}}int stack_malloc(int length, stack_point sp){length = length * sizeof(node_point);sp->length = length;sp->bottom = (node_point *)malloc(length);if(sp->bottom == NULL) {printf("Malloc for stack space failed.\n");return 0;} else {sp->top = sp->bottom;memset(sp->bottom, 0, length);printf("Stack init total length %d Bytes, each unit %d Bytes.\n",length, sizeof(sp->bottom));return 1;}}int stack_push(node_point pp, stack_point sp){if((sp->top - sp->bottom)/sizeof(sp->bottom) == sp->length) {printf("Stack full, can't push.\n");return 0;} else {*(sp->top) = pp;sp->top++;printf("Push data 0x%x into stack, top point to 0x%x now(bottom to 0x%x).\n", pp, sp->top, sp->bottom);return 1;}}node_point stack_pop(stack_point sp){node_point rtv_point = NULL;if(stack_empty(sp)) {printf("Stack empty!Cann't pop.\n");return NULL;} else {sp->top --;rtv_point = *(sp->top);printf("Pop data 0x%x from stack, top point to 0x%x now.\n", rtv_point, sp->top);return rtv_point;}}int stack_empty(stack_point se){if(se->bottom == se->top) {printf("Reach bottom of the stack:0x%x to 0x%x.\n", se->bottom, se->top);return 1;} else {return 0;}}void inorder_travel_stack(node_point pts){stack_data sp;node_point travel_p = pts;int cnt = 0;if(!stack_malloc(100, &sp)) {printf("Stack init failed!\n");return;}while((travel_p != NULL) || (stack_empty(&sp) != 1)) {if(travel_p != NULL) {stack_push(travel_p, &sp);travel_p = travel_p->lchild;} else {travel_p = stack_pop(&sp);visit(travel_p);travel_p = travel_p->rchild;}/* *printf("CNT%d\n", cnt++); */}}void postorder_travel(node_point pt){if(pt != NULL) {printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);postorder_travel(pt->lchild);postorder_travel(pt->rchild);visit(pt);}}void inorder_travel(node_point pt){if(pt != NULL) {printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);inorder_travel(pt->lchild);visit(pt);inorder_travel(pt->rchild);}}void preorder_travel(node_point pt){if(pt != NULL) {//printf("node data:%s", pt->data_field);visit(pt);printf("lchild addr %x, rchild addr %x\n", pt->lchild, pt->rchild);preorder_travel(pt->lchild);preorder_travel(pt->rchild);}}node_point create_bitree(){node_point cp = NULL, addr = NULL;char node_str[MAX_IN_NODE] = {0};fprintf(stdout, "Input the node data:");fgets(node_str, MAX_IN_NODE, stdin);if(strncmp(node_str, "\n", 1) == 0) {return NULL;} //若直接输入回车,则表示不创建该节点.cp = node_malloc();strncpy(cp->data_field, node_str, MAX_IN_NODE);addr = create_bitree();printf("addr 0x%x\n", cp->lchild = addr);addr = create_bitree();printf("addr 0x%x\n", cp->rchild = addr);fprintf(stdout, "node addr 0x%8x\n", cp);return cp;}int main(void){node_point root = NULL;root = create_bitree();printf("##########Start to travel the tree 0x%8x.\n", root);printf("\nPreorder travel:\n");preorder_travel(root);printf("\nInorder travel:\n");inorder_travel_stack(root);printf("\nPostorder_travel:\n");postorder_travel(root);return 0;}



0 0
原创粉丝点击