数据结构——查找技术

查找的基本概念
关键码
由同一类型的数据元素组成的集合
键值
关键码的值
主关键码
可以唯一的标识一个记录的关键码
次关键码
不能唯一的标识一个记录的关键码
静态查找
不涉及插入和删除操作的查找
动态查找
涉及删除和插入操作的查找
静态查找适用于:查找一经生成,便只对其进行查找,而不进行删除操作
动态查找适用于:查找和插入和删除操作在同一个阶段进行。
所以
线性表:适用于静态查找
树表:适用于动态查找
散列表:均适用,主要采用散列技术。
查找算法的性能:
ASL=(求和1-n)pi*ci;
其中n为问题规模,pi为查找第i条记录的概率;
ci为查找第i个记录所需要的比较次数;
线性表的查找技术
线性表查找结构类定义:

const int MaxSize = 100;
class LineSearch
{
public:
	LineSearch(int a[], int n);//构造函数
	~LineSearch() {};
	int SeqSearch(int k);//顺序查找
	int BinSearch(int k);//折半非递归查找
	int BinSearch2(int low, int high, int k);//折半递归查找
private:
	int data[MaxSize];//查找集合为整形
	int length;//查找集合的元素个数
};
LineSearch::LineSearch(int a[], int n)
{
	for (int i = 0; i < n; ++i)
		data[i + 1] = a[i];//查找元素从下标1开始存放
	length = n;
}

顺序查找
基本思想等省略
直接上代码

int LineSearch::SeqSearch(int k)
{
	int i=length;//从数组高端开始比较
	data[0]=k;//设置哨兵哨兵就是待查值
	while(data[i]!=k)//不用判断下标i是否越界
	i--;
	return i;
}

折半查找:
基本思想:
假设有序表按升序排列,取中间的值作为比较对象,若给定值的中间记录相等,则查找成功。
若给定值小于中间记录,则在有序表的左半区进行查找,否则在右半区进行查找。不断重复上述过程,直到查找成功或者查找失败。
折半查找的非递归算法:

int LineSearch::BinSearch(int k)
{
	int mid, low = 1, high = length;
	while (low <= high)
	{
		mid = (low + high) / 2;
		if (k < data[mid])
			high = mid - 1;
		else if (k > data[mid])low = high + 1;
		else
			return mid;//查找成功返回元素序号
	}
	return 0;//查找失败返回0;
}

折半查找递归算法

int LineSearch::BinSearch2(int low,int high,int k)
{
	if (low > high)
		return 0;//递归结束条件
	else {
		int mid = (high +low)/2;
		if (k < data[mid])
			return BinSearch2(low, mid - 1, k);
		else if (k > data[mid])return BinSearch2(mid + 1, high, k);
		else
			return mid;//查找成功返回序号;
	}
}

折半查找的平均时间复杂度为log2n;
树表的查找技术
二叉排序树:
性质:
若左子树不空,那么左子树上的所有结点的值均小于根节点的值;
若右子树不空,那么右子树上的所有结点的值均大于根结点的值;
左右子树也都是二叉排序树;
二叉排序树通常用二叉链表来存储,其结点结构可以用二叉链表的结点结构存储。

template <class DataType>
struct BiNode
{
	DataType data;
	BiNode<DataType>* lchild, * rchild;
};
class BiSortTree
{
public:
	BiSortTree(int a[], int n);//建立a[n]的二叉排序树
	~BiSortTree() { Release(root); }//同二叉链表的析构函数
	BiNode<int>* InsertBST(int x) { return InsertBST(root, x); }
	//插入记录x
	void DeleteBST(BiNode<int >* p, BiNode<int >* f);//删除f的左孩子p
	BiNode<int>* BiSearchBST(int k) { return SearchBST(root, k); }//查找值为k的结点
private:
	BiNode<int >* InsertBST(BiNode<int >* bt, int x);
	BiNode<int>* SearchBST(BiNode<int >* bt, int k);
	void Release(BiNode<int>* bt);
	BiNode<int>* root;//二叉排序树的根指针;	
};
BiNode<int>* BiSortTree::SearchBST(BiNode<int>* bt, int k)
{
	if (bt == nullptr)
		return nullptr;
	if (bt->data == k)
		return bt;
	else if (bt->data > k)//bt的值大于k则在左子树上查找
		return SearchBST(bt->lchild, k);
	else if (bt->data < k)//bt的值小于k则在右子树上查找
		return SearchBST(bt->rchild, k);
}

二叉排序树的插入
向二叉排序树中插入一个结点首先需要查找该结点的位置,然后在进行插入操作
新节点作为叶子插入到二叉排序树当中,无论是插在左子树还是右子树,都是按同样的方法进行处理,所以插入过程是递归的。

//二叉排序树的插入
BiNode<int>* BiSortTree::InsertBST(BiNode<int>* bt, int x)
{
	if (bt == nullptr)
	{
		BiNode<int>* s = new BiNode<int >; s->data = x;
		s->lchild = s->rchild = nullptr;
		bt = s;
		return bt;
	}
	else if (bt->data > x)
		bt->lchild = InsertBST(bt->lchild, x);
	else bt->rchild = InsertBST(bt->rchild, x);
}

构造一颗二叉排序树

BiSortTree::BiSortTree(int a[], int n)
{
	root = nullptr;
	for (int i = 0; i < n; ++i)
	{
		root = InsertBST(root, a[i]);
	}
}

二叉排序树的删除

void BiSortTree::DeleteBST(BiNode<int >* p, BiNode<int>* f)
{
	if (p->lchild == nullptr&&p->rchild==nullptr)
	{
		f->lchild == nullptr;
		delete p;
		return;
	}
	else if (p->rchild == nullptr)
	{
		f->lchild = p->lchild;
		delete p;
		return;
	}
	else if (p->lchild == nullptr)
	{
		f->lchild = p->rchild;
		delete p;
		return;
	}
	BiNode<int >* par, * s = p->rchild;//p的左右子树都不为空
	while (s->lchild != nullptr)
	{
		par = s;
		s = s->lchild;
	}
	p->data = s->data;
	if (par == p)
		par->rchild = s->rchild;
	else par->lchild = s->rchild;
	delete s;
}

时间性能
log2n 和n之间;
平衡二叉树,平衡二叉树的调整办法
首先什么是平衡二叉树:
或者是一颗空的二叉排序树
或者是具有下列两种性质的二叉排序树
1.根结点的左右子树的深度最多相差1。
2.根结点的左右子树都是平衡二叉树。
结点平衡因子是,左右子树深度之差。
所以平衡二叉树的平衡因子为-1或0或1
最小不平衡子树是指再平衡二叉树的就够构造过程中,以距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树。这个根结点称为最小不平衡结点。
类型
LL型,RR,LR,RL型。
平衡方法:
原则:(1)降高度。
(2)二叉排序树原则。
在这个基础上进行排序
B树
B树是一种平衡的多路查找树,常用在问价系统中。
定义如下
一个m阶的B树或者为空树或者为满足以下特性的m二叉树。
1.每个结点最多右m棵子树
2.根节点至少有两棵子树。
3.根节点除外其余结点至少有m/2棵子树。
4.所有结点包含以下数据(n,A0,K1,A1,K2,。。。Kn,An);
5.所有的结点都出现在同一层,所以B树是树高平衡。

散列表的查找技术
两个关键的技术
1散列表的构造
最直接定址法
除留余数法
数字分析法
平方取中法
折叠法
2冲突处理方法
开放地址法
链地址法
建立公共溢出区
散列的基本思想
在记录的存储地址和它的关键码之间建立一个确定的关系。这样,不经过比较,一次读取就能的到所查元素的查找方法
散列表:采用散列技术将存储在这一块的存储空间中,这块连续的存储空间称为散列表
散列函数:将关键码映射为散列表中适当的存储位置的函数
散列地址:由散列函数所得的存储位置址
散列是主要面向查找的存储结构
不适用于允许多个记录有同样的关键码的情况
不适用于范围查找
冲突:对于两个不同的关键码,即两个不同的记录需要存放在同一个存储位置,ki和kj相对于H称作同义词
散列函数
设计散列函数一般准寻以下两个原则
(1)计算简单。散列函数不应该有很大的计算量,否则会降低查找效率
(2)函数值即散列地址分布均匀。函数值要尽量均匀散步在地址空间,这样才能保证存储空间的有效利用并减少冲突。
直接定址法
H(key)=a*key+b;
例关键码集合为{10,30,50,70,80,90}
H(key)=key mod p;
一般情况下,选p为小于或等于表长(最好接近表长)的最小素数
数字分析法
根据关键码在各位上的分布情况,选取分布比较均匀的若干位组成散列地址。
平方取中法
对关键码平方后,按散列表大小,取中间的若干位作为散列地址
折叠法
将关键码从左到右分割成位数相等的几部分,将这几部分叠加求和,取后几位作为散列地址。
冲突处理
开散列表法
闭散列表法
建立公共溢出区
开放地址法
由关键码得到的散列地址一旦产生了冲突,就去寻找下一个空的散列地址,并将记录存入。
线性探测法
当发生冲突时,从冲突位置的下一个位置起,一次寻找空的散列地址。
对于键值key设H(key)=d,闭散列表的长度为m,则发生冲突时,寻找下一个散列地址的公式为
H(H(key)+di)%m;
堆积:在处理冲突 的过程中出现的非同义词之间对同一个散列地址争夺的现象

int HashSearch1(int ht[ ], int m, int k) 
{
     j=H(k);  
     if (ht[j]==k) return j;   //没有发生冲突,比较一次查找成功
     i=(j+1) % m;
     while (ht[i]!=Empty && i!=j)  
     {
         if (ht[i]==k) return i;  //发生冲突,比较若干次查找成功
         i=(i+1) % m;    //向后探测一个位置
     }
     if (i==j) throw "溢出";
     else ht[i]=k;   //查找不成功时插入
}

拉链法:
基本思想
将所有散列地址相同的记录,即所有同义词的记录存储在一个单链表中,在散列表中存储的是所有同义词子表的头指针。
用拉链法处理冲突构造的散列表叫做开散列表。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值