BST算法原理及其实现
2016-04-28 00:35
369 查看
首先,我们先了解下递归。说到递归,数据结构中树介绍章节中有大量的实例用了,包括二叉树定义与遍历等。当然一般公司项目中很少用它,主要是效率低下,同时递归涉及到函数反复建栈,压栈,销毁栈的过程,开销较大。这里仅仅作为一种技术探讨来研究。递归,简单地说就是函数自己调用自己,网上也有不少解释了。这里我的看法是将整个递归过程看做一种函数映射关系,即 F(A) --> B,如果B与A具有相同的属性和操作,同时B的作用域小于A的作用域,算法逐步收敛的话,就可以用递归来描述了。之前看过SCIP《计算机程序构造与解释》一书,里面讲到了函数式编程语言scheme,该书中将函数作为头等公民,所有操作都用函数来体现,用了大量的递归实现,状态值也是保存在函数入参中(这里也称作尾递归)。书中将函数看做某种规则,数据作为函数的输入,可以抽象理解为函数为对数据进行的操作行为,函数参数为输入数据;如果我们用更底层地用汇编方式描述就是函数可以看做代码段(对一类算法行为的描述),数据看做数据段(是对现有数据的集合)。那么程序本质上就是一段用可计算公式描述的抽象过程,简单地说就是一组相同属性集合上面的相同映射关系。比如说整数域上的加法运算,其动作为加法,数据就是整个整数集合。每个整数对于加法这个“规则”来说都是等价的,用scheme语言很容易描述这一点:(+
a b)。将算法与数据区别对待的好处就是很容易识别出规则。如果是数值计算的话,则便于用递归来描述。总之,递归适用于有明显递归特性的数据结构(比如树),或者有着明显递归公式,可以用数学归纳法证明的问题。我们来看一位大牛给出的通用递归公式:
Result M(Problem prob)
{
if (<problem can be solved easily>) //递归入口条件,不然就是无限递归了
return <easy solution>;
// The problem cannot be solved easily. //其他条件,满足相同的处理方式就可以了。
Problem smaller1 = <reduce problem to smaller problem>
Result result1 = M(smaller1); //注意用递归方式,子问题的规模一定要缩小,即算法要保证收敛
Problem smaller2 = <reduce problem to smaller problem>
Result result2 = M(smaller2);
...
//递归函数下面可以想象是栈底,上面的部分一直在将数据压栈,下面的步骤可以看做恢复栈的过程。
Result finalResult = <combine all results of smaller problem to solve large problem>
return finalResult;
}
我们这里看几段经典递归的例子:
=========================下面再看BST主要函数接口实现========================
其实树的数据结构实现用递归是最直观的,一般的教科书中也是这么定义与实现的。如果改用非递归算法,那就要在函数里面维护一个堆栈的数据结构,用来保留每次递归时候的中间状态变量。
a b)。将算法与数据区别对待的好处就是很容易识别出规则。如果是数值计算的话,则便于用递归来描述。总之,递归适用于有明显递归特性的数据结构(比如树),或者有着明显递归公式,可以用数学归纳法证明的问题。我们来看一位大牛给出的通用递归公式:
Result M(Problem prob)
{
if (<problem can be solved easily>) //递归入口条件,不然就是无限递归了
return <easy solution>;
// The problem cannot be solved easily. //其他条件,满足相同的处理方式就可以了。
Problem smaller1 = <reduce problem to smaller problem>
Result result1 = M(smaller1); //注意用递归方式,子问题的规模一定要缩小,即算法要保证收敛
Problem smaller2 = <reduce problem to smaller problem>
Result result2 = M(smaller2);
...
//递归函数下面可以想象是栈底,上面的部分一直在将数据压栈,下面的步骤可以看做恢复栈的过程。
Result finalResult = <combine all results of smaller problem to solve large problem>
return finalResult;
}
我们这里看几段经典递归的例子:
#include <stdio.h> void print(const char *s) { if (*s == '\0')//假设已经到栈底了 { return; } print (s+1); //从栈底开始计算或者打印 printf("%c\n", *s); } int length(const char *s) { if (*s == '\0') //假设已经到了栈底 { return 0; } //否则,通过观察法,查看下一次递归参数与本次之间有啥关系 return length(s+1) + 1; } int main() { print("abcdef"); printf("height= %d\n", length("abcdef")); return 0; }
=========================下面再看BST主要函数接口实现========================
/* ============================================================================ Name : BST.c Author : @CodingGeek Version : Copyright : V1.0 Description : BST算法的程序演示 ============================================================================ */ #include <stdio.h> #include <stdlib.h> typedef struct tagBST { struct tagBST *lchild; struct tagBST *rchild; int val; int height; //高度 }BST; //生成结点 BST *makeNode(int key) { BST *pBST = (BST*)malloc(sizeof(BST)); pBST->lchild = NULL; pBST->rchild = NULL; pBST->height = 1; pBST->val = key; return pBST; } //获取BST树的高度 int getHeight(BST *root) { return (root == NULL)? 0: root->height; } int max(int a, int b) { return a > b ? a : b; } //在树中新增一个结点 BST *insertNode(BST *root, int key) { if (root == NULL) { return makeNode(key); } if (root->val == key) { return root; } else if (key < root->val) //插入的值比根节点小,插入左子树 { root->lchild = insertNode(root->lchild ,key); } else { root->rchild = insertNode(root->rchild, key); } //递归函数后面的求值,一般是自底向上一层层计算,这里从根节点开始计算高度 root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1; return root; } BST *removeNode(BST *root, int key) { if (root == NULL) { return NULL; } if (key == root->val) { if(root->rchild == NULL) { BST* temp = root; root = root->lchild; free(temp); return root; } else { BST* temp = root->rchild; while(temp->lchild) { temp = temp->lchild; } root->val = temp->val; root->rchild = removeNode(root->rchild, temp->val); } } else if (key < root->val) { root->lchild = removeNode(root->lchild, key); } else { root->rchild = removeNode(root->rchild, key); } root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1; return root; } //遍历所有元素,默认是排序好的 void traverse(BST *root) { if (root == NULL)//假设已经到栈底,一般设为叶子结点的孩子 { return; } traverse(root->lchild); printf("结点: %d; 深度: %d\n", root->val, root->height); traverse(root->rchild); } void print_tree_indent(BST *node, int indent) { for (int ix = 0; ix < indent; ix++) { printf(" "); } if (node == NULL) { printf("空孩子结点\n"); } else { printf("结点: %d; 深度: %d\n", node->val, node->height); print_tree_indent(node->lchild, indent + 4); print_tree_indent(node->rchild, indent + 4); } } void print_tree(BST *node) { print_tree_indent(node, 0); } int main(void) { BST* root = NULL; int arr[] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9}; const int length = sizeof(arr) / sizeof(int); for(int i = 0; i< length;i++) { root = insertNode(root, arr[i]); } root = removeNode(root, 7); printf("打印BST树:\n"); print_tree(root); printf("==================================\n"); traverse(root); }
其实树的数据结构实现用递归是最直观的,一般的教科书中也是这么定义与实现的。如果改用非递归算法,那就要在函数里面维护一个堆栈的数据结构,用来保留每次递归时候的中间状态变量。
相关文章推荐
- 定时任务
- Linux Shell 02 流程控制语句
- read op case $op in
- 在Windows 7下删除注册表项时,权限不足
- 身份证校验原理和PHP实现
- iOS之深入了解控制器View的加载
- Android 如何在一个app中动态加载另一个app程序中的类
- Uva 11134 Fabled Rooks
- LeetCode 327. Count of Range Sum(区间和计数)
- BJTU 1729 Ryan的弹幕游戏(BFS)
- C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析,左值与右值
- Makefile里面的$(MAKE)到底是啥
- BZOJ 4558|JLOI 2016|SHOI 2016|方|容斥原理
- 初识scss(1)
- Fresco 缓存自定的 Bitmap
- unity 3D之ugui Scroll Rect拖拽消息冲突问题
- UI模版设计
- Spring入门之创建Bean的时间以及lazy-init属性
- 常见pip方法
- 山寨币开发教程