二叉搜索树(遍历不递归,不用栈)
来源:互联网 发布:互联网金融发展 知乎 编辑:程序博客网 时间:2024/05/16 23:46
什么是二叉搜索树
一棵二叉搜索树是以一棵二叉树来组织的,如下图:
这样的一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。除了key和卫星数据之外,每个结点还包含属性left, right 和 parent, 它们分别指向左孩子,右孩子和双亲。比如:
typeof struct node{ int key; // other data... 即卫星数据 struct node *lchild; //左孩子 struct node *rchild; //右孩子 struct node *parent; //双亲};
二叉搜索树,对任何结点x,其左子树中的关键字最大不超过x.key, 其中右子树中的关键字最小不低于x.key。不同的二叉搜索树可以代表同一组值的集合。大部分搜索树操作的最坏运行时间与树的高度成正比。如上图中(a)一棵包含6个结点高度为2的二叉搜索树。(b)一棵包含相同关键字、高度为4的低效二叉搜索树.
二叉搜索树中的关键字总是以满足二叉搜索树性质的方式来存储:
这是什么意思呢,设x为二叉搜索树中一结点,若y是x左子树中一结点,则
二叉搜索树遍历
那么根据上面性质,我们可知,要想按序输出二叉搜索树中的所有关键字,需用中序遍历(inorder tree walk)算法.
inorder tree walk 伪代码如下:
INORDER-TREE-WALK(x) // x为根结点指针
if x != NIL INORDER-TREE-WALK(x.left) printf(x.key) INORDER-TREE-WALK(x.right)
C/C++语言代码实现:
(1)非递归,用栈来实现,在我写的二叉树遍历里面有地址:
http://blog.csdn.net/jun2016425/article/details/72783252
(2)非递归,不用栈
一、中序遍历
步骤:
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
重复以上1、2直到当前节点为空。
图示:
下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。
注意:本来想用下面这个(1)结点结构的,但是写到请求前驱后继的时候有点难写,就用了有双亲的(2)结点结构。正如UNIX编程艺术里面说的:数据结构才是编程的核心,宁愿让数据结构复杂一点,也不愿省的一点两点的数据结构空间,而增大算法的复杂度,因为相比于算法逻辑的复杂,数据结构的复杂更容易让人看懂. so KISS(Keep It Simple, Stupid)
(1)
typedef struct _binary_search_tree{ int key; struct _binary_search_tree *left; struct _binary_search_tree *right;}binary_search_tree;
(2)
typedef struct _binary_search_tree{ int key; struct _binary_search_tree *left; struct _binary_search_tree *right; struct _binary_search_tree *parent;}binary_search_tree;
void inorderMorrisTraversal(binary_search_tree *root){ binary_search_tree *cur = root, *prev = NULL; while (cur != NULL){ if ( cur->left == NULL ){// 1. printf("%d ", cur->key); cur = cur->right; }else{ // 查找当前结点的前驱,中序遍历中当前结点的前一个结点 prev = cur->left; /* 查找当前结点的前驱 prev->right != NULL. * 当前结点的左子树已经被遍历之后, prev->right != cur * */ while ( prev->right != NULL && prev->right != cur ) prev = prev->right; if (prev->right == NULL){ // 2.a 查找当前结点的前驱. prev->right = cur; cur = cur->left; }else{ // 2.b 当前结点的左子树已经被遍历之后. prev->right = NULL; printf("%d ", cur->key); cur = cur->right; } } }}
二叉搜索树插入
将一个新值v插入到一棵二叉搜索树树T中,需要调用TREE-INSERT。该过程以结点z作为输入,其中z.key=v, z.left=NIL, z.right=NIL。
二叉搜索树插入伪代码:
TREE-INSERT(T,z)
y = NILx = T.rootwhile x != NIL y = x if z.key < x.key x = x.left else x = x.rightif y == NIL T.root = z // tree was empty.else if z.key < y.key y.left = zelse y.right = z
二叉搜索树插入C语言代码:
void TREE_INSERT(binary_search_tree *root, binary_search_tree *node){ binary_search_tree *prev = NULL, *cur = NULL; prev = root; while ( prev != NULL ){ cur = prev; if ( node->key < prev->key ) prev = prev->left; else prev = prev->right; } if ( cur == NULL ) root = node; // tree was empty. else if ( node->key < cur->key ) cur->left = node; else cur->right = node;}
二叉搜索树搜索
输入一个指向树根的指针和一个关键字k,如果这个结点存在,
TREE-SEARCH返回一个指向关键字为k的结点的指针,否则返回NIL
TREE-SEACH(x, k)
if x == NIL or k == x.key return xif k < x.key return TREE-SEACH(x.left, k)else return TREE-SEACH(x.right, k)
迭代方式:
这里可以采用while循环来展开递归,用一种迭代方式重写这个过程,
对于大多数计算机,迭代版本的效率要高得多。
TREE-SEACH(x, k)
while x != NIL and k != x.key if k < x.key x = x.left else x = x.rightreturn x
二叉搜索树后继
binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node){ binary_search_tree *prev = NULL; binary_search_tree *cur = node; /* 如果有右子树,则next为右子树的最左节点 */ if ( cur->right ) return TREE_MINIMUM( cur->right ); /* 无右子树的结点,只需简单的沿数而上直到遇到一个其双亲结点有左孩子的结点, * 那么该双亲结点就是node的后继 */ prev = cur->parent; while ( prev && cur == prev->right ){ cur = prev; prev = prev->parent; } return prev;}
二叉搜索树前驱
binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node){ binary_search_tree *prev = NULL; binary_search_tree *cur = node; /* 如果有左子树,则next为左子树的最右节点 */ if ( cur->left ) return TREE_MAXIMUM( cur->left ); /* 如果无左子树,则next在祖宗节点上,本节点为祖宗节点的右支 */ prev = cur->parent; while ( prev && cur == prev->left ){ cur = prev; prev = prev->parent; } return prev;}
C语言代码:
binary_search_tree *TREE_SEACH(binary_search_tree *root, int k){ binary_search_tree *tmp = root; while ( tmp != NULL && k != tmp->key ){ printf("[%s][%d] tmp->key = %d\n",__FUNCTION__, __LINE__, tmp->key); if ( k < tmp->key ) tmp = tmp->left; else tmp = tmp->right; } return tmp;}
二叉搜索树删除
替换
/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node){ if ( NULL == old_node->parent ) // old_node 为树根 root = new_node; else if ( old_node == old_node->parent->left ) // old_node 是双亲的左孩子 old_node->parent->left = new_node; else old_node->parent->right = new_node;// old_node 是双亲的右孩子 if ( new_node != NULL ) new_node->parent = old_node->parent;}
删除
/* 删除树中某个结点,且释放该结点资源,如果该结点存在 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node){ binary_search_tree *tmp = NULL; /* node (即z) 没有左孩子 q q | | z -----> r /\ /\ NULL r /\ */ if ( NULL == node->left ) TRANSPLANT(root, node, node->right); /* node(即z) 没有右孩子 q q | | z -----> l /\ /\ l NULL /\ */ else if ( NULL == node->right ) TRANSPLANT(root, node, node->left); else{ tmp = TREE_MINIMUM(node->right); // 获取node右子树最小的结点tmp /* 如果node(即z)的右子树的最小结点tmp(即y)不是node(z)的右孩子. 即z的后继是y,而且y还是不是z的右孩子,但是y是z右子树里面最小的, 所以y是没有左子树的. 则在以node->right(即r)为根的小子树(substree root)里面, 让(tmp)y被x(y的右孩子)取代,让tmp(y)成为node(即z)的右孩子(这里是r)的双亲结点, 让tmp(y)成为node->parent(q)的孩子,还有成为node->left(即l)的双亲. q q q | | | z -----> z ------> y /\ /\ /\ l r l y l r /\ /\ /\ /\ y NULL r x /\ /\ /\ NULL x x /\ */ if (tmp->parent != node){ TRANSPLANT(root, tmp, tmp->right); tmp->right = node->right; // meaning y->right = r tmp->right->parent = tmp; // meaning r->parent = y } /* 如果node(即z)的右子树的最小结点tmp(即y)是node(z)的右孩子. q q | | z ------> y /\ /\ l y l x /\ / \ /\ NULL x /\ */ TRANSPLANT(root, node, tmp); tmp->left = node->left; tmp->left->parent = tmp; } if (node){ free(node); node = NULL; }}
完整的测试例子(C语言)
#include <stdio.h>#include <stdlib.h>typedef struct _binary_search_tree{ int key; struct _binary_search_tree *left; struct _binary_search_tree *right; struct _binary_search_tree *parent;}binary_search_tree;static inline void INIT_NODE(binary_search_tree *node, int key){ node->key = key; node->left = NULL; node->right = NULL; node->parent = NULL;}void inorderMorrisTraversal(binary_search_tree *root);void TREE_INSERT(binary_search_tree *root, binary_search_tree *node);binary_search_tree *CREATE_NODE(int key);void REMOVE_TREE(binary_search_tree *root);binary_search_tree *TREE_SEACH(binary_search_tree *root, int k);binary_search_tree *TREE_MINIMUM(binary_search_tree *root){ binary_search_tree *node = root; while ( node->left ) node = node->left; return node;}binary_search_tree *TREE_MAXIMUM(binary_search_tree *root){ binary_search_tree *node = root; while ( node->right ) node = node->right; return node;}binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node);binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node);/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node);/* 删除树中某个结点 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node);int main(void){ int i = 0; binary_search_tree *root = NULL; binary_search_tree *node = NULL; binary_search_tree *position = NULL; root = CREATE_NODE(6); // create root node node = CREATE_NODE(9); TREE_INSERT(root, node); node = CREATE_NODE(8); TREE_INSERT(root, node); node = CREATE_NODE(1); TREE_INSERT(root, node); node = CREATE_NODE(2); TREE_INSERT(root, node); node = CREATE_NODE(5); TREE_INSERT(root, node); node = CREATE_NODE(4); TREE_INSERT(root, node); node = CREATE_NODE(3); TREE_INSERT(root, node); node = CREATE_NODE(7); TREE_INSERT(root, node); /* 查找特定结点 */ TREE_SEACH(root, 8); /* 查找最大,最小结点 */ node = TREE_MAXIMUM(root); printf("maximun number = %d\n", node->key); node = TREE_MINIMUM(root); printf("minimum number = %d\n", node->key); /* 查找后继结点 */ position = TREE_SEACH(root, 9); node = TREE_SUCCESSOR(position); if (node == NULL) printf("[%s][%d] %d is the last node no successor!!!\n", __FUNCTION__, __LINE__, position->key); else printf("[%s][%d] %d successor is %d\n", __FUNCTION__, __LINE__, position->key, node->key); for (i=1; i<=9; ++i){ position = TREE_SEACH(root, i); node = TREE_SUCCESSOR(position); if (node == NULL) printf("[%s][%d] %d is the last node no successor!!!\n", __FUNCTION__, __LINE__, position->key); else printf("[%s][%d] %d successor is %d\n", __FUNCTION__, __LINE__, position->key, node->key); } for (i=1; i<=9; ++i){ position = TREE_SEACH(root, i); node = TREE_PREDECESSOR(position); if (node == NULL) printf("[%s][%d] %d is the last node no predecessor!!!\n", __FUNCTION__, __LINE__, position->key); else printf("[%s][%d] %d predecessor is %d\n", __FUNCTION__, __LINE__, position->key, node->key); } /* 中序遍历 */ inorderMorrisTraversal(root); printf("\n"); /* 删除结点 */ node = TREE_SEACH(root, 3); printf("after delete node:%d\n", node->key); TREE_DELETE(root, node); inorderMorrisTraversal(root); printf("\n"); REMOVE_TREE(root); return 0;}void inorderMorrisTraversal(binary_search_tree *root){ if (NULL == root) printf("[%s][%d] root is null!!!\n", __FUNCTION__, __LINE__); binary_search_tree *cur = root, *prev = NULL; while (cur != NULL){ if ( cur->left == NULL ){ printf("%d ", cur->key); cur = cur->right; }else{ // find predecessor prev = cur->left; /* when find predecessor prev->right != NULL. * when cur->left tree was walked. * */ while ( prev->right != NULL && prev->right != cur ) prev = prev->right; if (prev->right == NULL){ // find predecessor. prev->right = cur; cur = cur->left; }else{ // cur->left tree was walked. prev->right = NULL; printf("%d ", cur->key); cur = cur->right; } } }}void TREE_INSERT(binary_search_tree *root, binary_search_tree *node){ binary_search_tree *prev = NULL, *cur = NULL; prev = root; while ( prev != NULL ){ cur = prev; if ( node->key < prev->key ) prev = prev->left; else prev = prev->right; } node->parent = cur; if ( cur == NULL ) root = node; // tree was empty. else if ( node->key < cur->key ) cur->left = node; else cur->right = node;}binary_search_tree *CREATE_NODE(int key){ binary_search_tree *node = malloc(sizeof(binary_search_tree)); if ( node == NULL ){//malloc failed. printf("malloce memory failed!!!\n"); return node; } INIT_NODE(node, key); return node;}void REMOVE_TREE(binary_search_tree *root){ if (root == NULL) return; REMOVE_TREE(root->left); REMOVE_TREE(root->right); free(root); root = NULL; return;}binary_search_tree *TREE_SEACH(binary_search_tree *root, int k){ binary_search_tree *tmp = root; while ( tmp != NULL && k != tmp->key ){ if ( k < tmp->key ) tmp = tmp->left; else tmp = tmp->right; } return tmp;}binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node){ binary_search_tree *prev = NULL; binary_search_tree *cur = node; /* 如果有右子树,则next为右子树的最左节点 */ if ( cur->right ) return TREE_MINIMUM( cur->right ); /* 无右子树的结点,只需简单的沿数而上直到遇到一个其双亲结点有左孩子的结点, * 那么该双亲结点就是node的后继 */ prev = cur->parent; while ( prev && cur == prev->right ){ cur = prev; prev = prev->parent; } return prev;}binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node){ binary_search_tree *prev = NULL; binary_search_tree *cur = node; /* 如果有左子树,则next为左子树的最右节点 */ if ( cur->left ) return TREE_MAXIMUM( cur->left ); /* 如果无左子树,则next在祖宗节点上,本节点为祖宗节点的右支 */ prev = cur->parent; while ( prev && cur == prev->left ){ cur = prev; prev = prev->parent; } return prev;}/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node){ if ( NULL == old_node->parent ) // old_node 为树根 root = new_node; else if ( old_node == old_node->parent->left ) // old_node 是双亲的左孩子 old_node->parent->left = new_node; else old_node->parent->right = new_node;// old_node 是双亲的右孩子 if ( new_node != NULL ) new_node->parent = old_node->parent;}/* 删除树中某个结点,且释放该结点资源,如果该结点存在 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node){ binary_search_tree *tmp = NULL; /* node (即z) 没有左孩子 q q | | z -----> r /\ /\ NULL r /\ */ if ( NULL == node->left ) TRANSPLANT(root, node, node->right); /* node(即z) 没有右孩子 q q | | z -----> l /\ /\ l NULL /\ */ else if ( NULL == node->right ) TRANSPLANT(root, node, node->left); else{ tmp = TREE_MINIMUM(node->right); // 获取node右子树最小的结点tmp /* 如果node(即z)的右子树的最小结点tmp(即y)不是node(z)的右孩子. 即z的后继是y,而且y还是不是z的右孩子,但是y是z右子树里面最小的, 所以y是没有左子树的. 则在以node->right(即r)为根的小子树(substree root)里面, 让(tmp)y被x(y的右孩子)取代,让tmp(y)成为node(即z)的右孩子(这里是r)的双亲结点, 让tmp(y)成为node->parent(q)的孩子,还有成为node->left(即l)的双亲. q q q | | | z -----> z ------> y /\ /\ /\ l r l y l r /\ /\ /\ /\ y NULL r x /\ /\ /\ NULL x x /\ */ if (tmp->parent != node){ TRANSPLANT(root, tmp, tmp->right); tmp->right = node->right; // meaning y->right = r tmp->right->parent = tmp; // meaning r->parent = y } /* 如果node(即z)的右子树的最小结点tmp(即y)是node(z)的右孩子. q q | | z ------> y /\ /\ l y l x /\ / \ /\ NULL x /\ */ TRANSPLANT(root, node, tmp); tmp->left = node->left; tmp->left->parent = tmp; } if (node){ free(node); node = NULL; }}
参考:
[1] http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html
- 二叉搜索树(遍历不递归,不用栈)
- 不用栈实现二叉树非递归中序遍历
- 不用栈实现二叉树非递归中序遍历
- 线索二叉树 Threaded Binary Tree (不用递归,不用栈,遍历二叉树)
- 二叉搜索树的递归遍历和非递归遍历
- 不使用递归和栈遍历二叉树
- 不使用递归和栈遍历二叉树
- 非递归,不用栈实现二叉树中序遍历
- parent指针非递归不用栈中序遍历二叉树
- 不用递归和辅助空间对二叉树进行遍历
- 不用栈实现二叉树非递归中序遍历源代码说明
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- 数据结构《12》----二叉树遍历的非递归版本(不用栈)
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
- 前馈神经网络和反向传播算法
- Linux下用ndk r15编译boost_1_64_0
- java基础知识点
- 对上一篇jni的纠正把
- 最小生成树
- 二叉搜索树(遍历不递归,不用栈)
- QT中读写文件操作
- 设计模式02-工厂模式
- 【CodeForces 713A】
- JDBC连接mysql
- 单例模式的c++实现
- maven项目 错误: 找不到或无法加载主类
- PAT甲级 1122. Hamiltonian Cycle (25)
- 从零搭建Hadoop集群