算法学习之查找算法:动态查找表(1)二叉排序树

引言:

动态查找表的特点是,在表结构本身是在查找过程中动态生成的,即对于给定值key,若表中存在其关键字等于key的记录,则查找成功返回,否则插入关键字等于key的记录。

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

1、若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。

2、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

3、它的左、右子树也分别为二叉排序树。

<一>二叉排序树的查找:

二叉排序树又称二叉查找树,查找过程是先将给定值和根结点的关键字比较,若相等,则查找成功,否则将依据给定值和根结点的关键字之间的大小关系,分别在左子树或右子树上继续进行查找。通常,可取二叉链表作为二叉排序树的存储结构。举例如下:

45

/       \                                     以查找关键字100为例。

12      
 53                                 1、因为100 > 45,所以查找以45为根的右子树。此时右子树不空。

/     \         \                                 2、又100 > 53,所以继续查找以53为根的右子树。

3      37      
100                           3、53为根的右子树的关键字100,与查找关键字相等,查找成功。

/            /                                     返回关键字等于100的数据结点的指针。

24         61

\

90

/

78

从上面的分析可以看出,若将查找过程翻译成代码如下:

BiTree SearchBST(BiTree, T, KeyType key)

{

//在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素

//若查找成功,则返回指向该数据元素结点的指针,否则返回空指针

if((!T)||EQ(key, T->data.key))    return(T);               //查找结束

else if  LT(key, T->data.key)   return (SearchBST(T->lchild, key));   //在左子树中继续查找

else return(SearchBST(T->rchild, key));                                           //在右子树中继续查找

}

<二>二叉排序树的插入

二叉排序树是一种动态树表,特点是:树的结构不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。因此改写上面给出的查找算法,在查找不成功时返回插入位置,以便进行二叉排序树的插入。

二叉排序树插入操作代码实现:

/*********************************************************************
Author:李冰 date:2014-9-25
Email:[email protected]
*********************************************************************/

typedef int ElemType;

typedef struct node{
	ElemType key;
	struct node *Left, *Right;
}BSTNode, *SearchTree;

SearchTree Insert(ElemType X, SearchTree T)
{
	if(T == NULL){
		T = (SearchTree)malloc(sizeof(BSTNode));	//创建并返回一个节点树
		if(T == NULL)
			printf("Out of space!!");
		else
		{
			T->key = X;
	        T->Left = T->Right = NULL;
		}
	}else if(X < T->key){
		T->Left = Insert(X, T->Left);
	}else if(X > T->key){
		T->Right = Insert(X, T->Right);
	}

	return T;
}

以关键字序列为{45, 24, 53, 45, 12, 24, 90}为例,生成二叉排序树的过程如下:

○                  45                     45                           45                                             45                            45

a                    b                   /                              /     \                                          /     \                  
      /    \

24                            24     53                                    24    53                   24    53

c                             
d                                         /                              /           \

12                           12           90

e                          
 f

中序遍历二叉排序树可得到一个关键字有序序列。一个有序序列可通过构造一颗二叉排序树而变成一个有序序列,构造树的过程即为无序序列进行排序的过程。而且在插入的过程中,每次插入的新结点都是二叉排序树上新的叶子结点,则在进行插入操作时,不必移动其他结点,仅需改动某个结点的指针,由空变为非空即可。相当于在一个有序序列上插入一个记录而不需要移动其记录。二叉排序树既拥有类似折半查找的特性,又采用了链表作存储结构,因此是动态查找表的一种适宜表示。

测试如下:

/*********************************************************************
Author:李冰 date:2014-9-25
Email:[email protected]
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

void InOrder(SearchTree T)
{
	if(T){
		InOrder(T->Left);
		printf("%d ", T->key);
		InOrder(T->Right);
	}
}

int main(int argc, char **argv)
{
	SearchTree T = NULL;
	int array[] = {45, 24, 53, 45, 12, 24, 90};
	for(int i = 0; i < 7; i++){
		T = Insert(array[i], T);
	}

	InOrder(T);

	return 0;
}

输出结果为:12 24 45 53 90

<三>二叉排序树的删除

对于二叉排序树,删去树上一个结点相当于删去有序序列中的一个记录,只要在删除某个结点之后依旧保持二叉排序树的特性即可。

删除结点分为3种情况:

1、如果节点是一片树叶,可直接删除。

2、如果节点有一个儿子,则该节点可在其父节点调整指针绕过该节点后被删除。

3、如果节点有两个儿子,则用所删除结点的右子树的最小数据代替该节点的数据并递归地删除那个节点。

通过上面的删除节点,能够保证删除节点后的二叉排序树仍然是二叉排序树,中序遍历该节点,各节点的顺序不变。

删除操作C语言实现如下

/*********************************************************************
Author:李冰 date:2014-9-25
Email:[email protected]
*********************************************************************/
SearchTree FindMin(SearchTree T)
{
	if(T == NULL)
		return NULL;
	else if(T->Left == NULL)
		return T;
	else
		return FindMin(T->Left);
}

SearchTree Delete(ElemType X, SearchTree T)
{
	SearchTree TmpCell;

	if(T == NULL)
		printf("Element not found");
	else if(X < T->key)
		T->Left = Delete(X, T->Left);
	else if(X > T->key)
		T->Right = Delete(X, T->Right);
	else if(T->Left && T->Right)    //找到了要被删除的节点,两个孩子
	{
		TmpCell  = FindMin(T->Right);
		T->key   = TmpCell->key;
		T->Right = Delete(T->key, T->Right);
	}else{                      //一个孩子或没有孩子
		TmpCell = T;
		if(T->Left == NULL)
			T = T->Right;
		else if(T->Right == NULL)
			T = T->Left;
		free(TmpCell);
	}

	return T;
}

查找性能:二叉排序树的平均查找长度和logn是等数量级别的。

时间: 2024-10-14 08:07:54

算法学习之查找算法:动态查找表(1)二叉排序树的相关文章

算法学习笔记 KMP算法之 next 数组详解

最近回顾了下字符串匹配 KMP 算法,相对于朴素匹配算法,KMP算法核心改进就在于:待匹配串指针 i 不发生回溯,模式串指针 j 跳转到 next[j],即变为了 j = next[j]. 由此时间复杂度由朴素匹配的 O(m*n) 降到了 O(m+n), 其中模式串长度 m, 待匹配文本串长 n. 其中,比较难理解的地方就是 next 数组的求法.next 数组的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀,也可看作有限状态自动机的状态,而且从自动机的角度反而更容易推导一些. "前

数据结构与算法学习 第1季01 顺序表 链表

2015年学习计划安排: http://www.cnblogs.com/cyrus-ho/p/4182275.html 顺序表:顺序存储结构的线性表.所谓顺序存储结构,就是指用一组连续地址的内存单元来存储整张线性表的存储结构.(因此按序遍历数据很方便,直接做指针偏移就可以了.) 常用操作 A)向顺序表中第i个位置插入元素item 1. 判断插入位置是否合法 2. 将i-1以后的元素后移一个元素的位置(注意静态顺序表和动态顺序表的差异)--- 从原来最后一个元素开始操作到原来的第i个元素,依次后移

算法学习一~分治法~二分查找,快速的找~

现在编程也算是走上门了,但是没有把算法掌握下来只能说自己还是门外汉,所以以后我们就开始努力的学习算法,现在把自己每天的学习分享在这里希望大家能喜欢,并且我也要在这里整理我一天的学习和思路,. 二分查找..大家经常需要在一个数组中寻找某个值.如果是一个已经拍好序的话那么可以很快的找到.我们考虑下暴力查找,就是a[n],中找一个m,那么最好时是o(1),最差时是0(n),最终平均情况就是长度n/2.这样的时间复杂度不算很高但是可以改进到logn的级别. 1 #include<stdio.h>//算

算法学习之排序算法:插入排序(直接插入排序、折半插入排序、2-路插入排序)

引言: 插入排序作为最简单易于理解的排序算法,基本实现比较简单.本文详细介绍直接插入排序,并给出实现,简单的介绍折半插入排序,并给出2-路插入排序和表插入排序两种插入排序,但并未给出具体实现. 一.直接插入排序 直接插入排序的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的.记录数增1的有序表. 算法描述: 步骤1.将待排序的一组记录中的第1个记录拿出来作为一组有序的记录(当然此时该组记录仅有1个记录). 步骤2.依次将待排序的一组记录中的记录拿出来插入到前面已排好序的记录中. 步

[算法学习]A星算法

一.适用场景 在一张地图中,绘制从起点移动到终点的最优路径,地图中会有障碍物,必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (如果有路径)采用"结点与结点的父节点"的关系从最终结点回溯到起点,得到路径. 2. 路径代价的估算:F = G+H A星算法的代价计算使用了被称作是启发式的代价函数. 先说明一下各符号意义:G表示的是 ** 从起点到当前结点的实际路径代价 ** (为啥叫实际?就是已经走过了,边走边将代价计算好了):H表示 ** 当前结点到达最终结点的估计代价 ** (为

经典算法学习之贪心算法

贪心算法也是用来求解最优化问题的,相比较动态规划很多问题使用贪心算法更为简单和高效,但是并不是所有的最优化问题都可以使用贪心算法来解决. 贪心算法就是在每个决策点都做出在当时看来最佳的选择. 贪心算法的设计步骤: 1.将最优化问题转换为:对其做出一次选择之后,只剩下一个问题需要求解的形式(动态规划会留下多个问题需要求解) 2.证明做出贪心选择之后,原问题总是存在最优解,即贪心算法总是安全的 3.证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最

[算法学习笔记]排序算法——堆排序

堆排序 堆排序(heapsort)也是一种相对高效的排序方法,堆排序的时间复杂度为O(n lgn),同时堆排序使用了一种名为堆的数据结构进行管理. 二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆. 如上图显示,(a)是一个二叉堆(最大堆), (b)是这个二叉堆在数组中的存储形式. 通过给个一个节点的下标i, 很容易计算出其父节点,左右子节点的的下标,为了方便,

算法学习之排序算法:归并排序

"归并"的含义是将两个或两个以上的有序表组合成一个新的有序表.无论是顺序存储还是链表存储结构,都可在O(m+n)的时间量级上实现. 归并排序又是一类不同的排序方法.假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个为2或1的有序子序列:再两两归并,....... ,如此重复,直至得到一个长度为n的有序序列为止. 初始关键字:[49]   [38]   [65]   [97]   [76]   [13]   [27] A       A

算法学习之排序算法(三)(选择排序法)

1.引言 选择排序工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完. 选择排序是不稳定的排序方法.选择排序是和冒泡排序差不多的一种排序.和冒泡排序交换相连数据不一样的是,选择排序只有在确定了最小的数据之后,才会发生交换.怎么交换呢?我们可以以下面一组数据作为测试: 2, 1, 5, 4, 9 第一次排序:1, 2, 5, 4, 9 第二次排序: 1, 2, 5, 4, 9 第三次排序: 1, 2, 4, 5, 9 第四次排序:

算法学习之排序算法:堆排序

要了解堆排序,首先要了解堆的概念,因为本文主要研究堆排序的算法,此处对数据结构堆只是给出概念:n个元素的序列{k1,k2,...kn},当且仅当满足如下关系时,称之为堆. k[i] <= k[2i]且k[i] <= k[2i+1] (或 k[i] >= k[2i]且k[i] >= k[2i+1]) 比如:序列96.83.27.38.11.09(或12.36.24.85.47.30.53.91)都是堆. 如果将堆对应的一维数组看成是一个二叉树,则堆的含义表明:完全二叉树中所有非终端结