//这里我要建立一棵二叉排序树 //实现插入,删除的功能 //再用前根序遍历,中序遍历,后续遍历 //下一步增加AVL树的功能 //bobzhang.wiki.zoho.com , kernelchina.cublog.cn //编译:gcc -o bst_operation tree_operation.c stack_lib.c //因为我们都是在linux工作的, 所以这里的风格完全用的kernel代码的风格,尤其 //是INIT_NODE() 宏,DECLARE_BST_TREE()等等, 都是kernel代码的风格 //从我们工作的实践来看,这种风格确实是非常可读性非常好 //BobZhang
//还有我们也可以考虑 既然有bst_node_t ,为什么还要定义bst_tree_t ? //事实上,不定义bst_tree_t 完全可以,只是我们说的时候,都是往树里面插入节点 //从这个角度来说,我觉得int bst_insert(bst_tree_t *tree, bst_node_t *node) 就更好 //否则写成: int bst_insert(bst_node_t *root, bst_node_t *node) //虽然root节点就代表一棵树,但是还是不直观 //所以专门定义一个bst_tree_t 类型
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "stack_lib.h" //我自己实现的栈,可以查阅 http://blog.chinaunix.net/u/22617/showart.php?id=1387160
typedef struct bst_node { //标准的二叉链表
void *data_p; //数据项指针,兼容所有类型,测试例子用的int型
struct bst_node *left; //左孩子
struct bst_node *right; //右孩子
}bst_node_t;
//这样包起来,好处多多, 实现起来非常自然
typedef struct bst_tree { bst_node_t *root; }bst_tree_t;
#define INIT_BST_NODE(node,ptr) \ node->data_p = ptr; \ node->left = NULL; \ node->right= NULL;
//声明一个节点
#define DECLARE_BST_NODE(name,ptr) \ bst_node_t *name = (bst_node_t *)malloc(sizeof(bst_node_t));\ assert(name); \ INIT_BST_NODE(name,ptr);
//声明一个二叉树
#define DECLARE_BST_TREE(bstree_name) \ bst_tree_t *bstree_name = (bst_tree_t *)malloc(sizeof(bst_tree_t)); \ INIT_BST_TREE(bstree_name); \
//一棵空树
static inline int INIT_BST_TREE(bst_tree_t *tree) { //这个和链表的操作是多少有些差异的
tree->root = NULL;
return 0; }
int bst_insert(bst_tree_t *tree, bst_node_t *node) ; int inorder_traverse(bst_node_t *root) ;
//要在二叉树中插入节点
//一开始其实就是遍历,左走右走, 直到找到一个叶节点,如果item 值大于叶节点值,就生成一个节点作为该叶节点的有节点,
//否则就是左节点
int bst_insert(bst_tree_t *tree, bst_node_t *node) { //p是t的父节点
bst_node_t * p = NULL; bst_node_t * t = tree->root; if(!t) { //原来是空树
printf("old is empty tree\n"); tree->root = node; return 0; } //开始遍历二叉树
while(t) {
p =t ; if (*(int *)(node->data_p) < *(int *)(t->data_p)) t = t->left; else t = t->right; }
//判断t到底是p的左节点还是有节点? if (*(int *)(node->data_p) < *(int *)(p->data_p)) p->left = node; else p->right = node;
return 0; }
//递归实现中序遍历
//递归的基本情况:根节点为空
//递归情况:遍历左右子树
int inorder_traverse(bst_node_t *root) { //空二叉树的情况,递归的基本情况
if(!root) return 0; //不为空
inorder_traverse(root->left); printf("%d\n",*(int *)(root->data_p)); inorder_traverse(root->right); return 0; }
//非递归实现前根序遍历二叉树
int preorder_traverse(bst_tree_t *tree) { DECLARE_STACK(RCHILD_STACK); node_t * node = NULL; int ret = 0;
bst_node_t * tmp_btnode = tree->root; //肯定可以用while()循环来代替goto ,但是那样我觉得理解起来没有goto LOOP这样更直接,自然 //不要跟我讨论goto 容易混乱的弊病,kernel 代码里面可都是goto 居多,尤其是跳转来handle error LOOP:
//入栈就是右子树的地址,因为node_t 里面的data_p 是 void *
while(tmp_btnode) { DECLARE_NODE(right_node,tmp_btnode->right);
printf("Node value = %d\n",*(int *)(tmp_btnode->data_p)); //右子树入栈
push(RCHILD_STACK,right_node); tmp_btnode = tmp_btnode->left; }
ret = pop(RCHILD_STACK,&node); //弹出右子树
if(!ret) { //说明不是空栈
tmp_btnode = (bst_node_t *)(node->data_p); free(node); //必须释放这个节点
goto LOOP; }
free_stack(RCHILD_STACK); //现在栈没有用了,要释放
return 0; }
//递归实现前根序遍历
int preorder_traverse2(bst_node_t *root) { if(!root) return -1; printf("%d\n",*(int *)(root->data_p)); preorder_traverse2(root->left); preorder_traverse2(root->right); return 0; } int main(void) { int v1 = 10; int v2 = 20; int v3 = 30; int v4 = 40; int v5 = 50; int v6 = 60; int v7 = 70; DECLARE_BST_NODE(node1,&v1); DECLARE_BST_NODE(node2,&v2); DECLARE_BST_NODE(node3,&v3); DECLARE_BST_NODE(node4,&v4); DECLARE_BST_NODE(node5,&v5); DECLARE_BST_NODE(node6,&v6); DECLARE_BST_NODE(node7,&v7); DECLARE_BST_TREE(bst_tree); //现在是一颗空树
//插入建立一个二叉排序表
bst_insert(bst_tree,node5); bst_insert(bst_tree,node2); bst_insert(bst_tree,node7); bst_insert(bst_tree,node1); bst_insert(bst_tree,node4); bst_insert(bst_tree,node6); bst_insert(bst_tree,node3); //再中根序遍历此而查表
printf("in order traverse \n"); inorder_traverse(bst_tree->root); printf("pre order traverse \n"); preorder_traverse(bst_tree); printf("pre order traverse ,recurse \n"); preorder_traverse2(bst_tree->root); return 0; }
|