静态查找+动态查找 总结

一、静态查找

即仅查询的表,生成的时候不会进行调整

ASL:平均查找长度,公式中p是查找第i个元素的概率,c是查找第i个元素时比较的次数

1. 顺序查找

该方法一般不用链式存储,比较麻烦,空间占用的也多。

从后向前比较,若直到第一个记录都不相等,则查找不成功

可以将 list[0] = key;  作为哨兵,省略对数组下表越界的检查,提高算法执行的速度(虽然我觉得好像没提高)

代码非常简单:

int list[100]={-1007},n; //哨兵,数组长度
int my_find(int x){
	for(int i=n;list[i]!=-1007;i--){
		if(list[i]==x) return i;
	}
	return 0;
}

性能分析:O(n)

在等概率查找的情况下,查找成功的ASL=(n+1)/2,查找失败的ASL=(n+1)

2.折半查找

也叫二分查找,必!须!得!是!有序表

int bifind(int low,int high){
	int mid=(low+high)/2;
	if(low>high) return 0;
	if(arr[mid]==n) return mid;
	if(arr[mid]>n) return bifind(low,mid-1);
	if(arr[mid]<n) return bifind(mid+1,high);
}

性能分析:O(logn)

二分查找的判定树:由此可以计算出概率相等的情况下查找成功/失败的ASL,即总查找次数÷数组长度

下图中ASL成功=(1+2*2+3*4+4*4)/11=3

           ASL失败=(4*4+5*8)/11=5.09

也可以用公式,不过这个公式需要为满二叉树:

应用有很多很多,比如折半查找求一个数x的平方根:

#include <iostream>
using namespace std;
double x;
double binary(double low,double high){
	double mid=(low+high)/2;
	if(high-low<1e-6) return mid;
	if(mid*mid>x)  return binary(low,mid);
	if(mid*mid<x)  return binary(mid,high);
}
int main(){
	int t;cin>>t;
	while(t--){
		cin>>x;
		printf("%.3lf\n",binary(0,x));
	}
	return 0;
}

3.分块查找

索引表将主表分为几块,要求块内无序、块间有序。块间有序指后一块表中所有数字大于或小于前一块表的所有数字。索引表还会知道每个块中的max。

题目描述

给出一个队列和要查找的数值,找出数值在队列中的位置,队列位置从1开始

要求使用顺序索引查找算法,其中索引表查找和块内查找都采用不带哨兵、从头开始的顺序查找方法。

输入:

第一行输入n,表示主表有n个数据
第二行输入n个数据,都是正整数,用空格隔开
第三行输入k,表示主表划分为k个块,k也是索引表的长度
第四行输入k个数据,表示索引表中每个块的最大值
第五行输入q,表示有q个要查找的数值
第六行起,输入t个数值,输入t行

输出:

每行输出一个要查找的数值在队列的位置和查找次数,数据之间用短划线隔开,如果查找不成功,输出字符串error

#include <iostream>
using namespace std;
int arr[1000],n,k,b[100],f,ans;//分成k块 
int find_w(){     //寻找在哪个块中
	int cnt=0;
	ans=1;
	if(f<=b[1]) return 1;
	for(int i=1;i<k;i++){
		ans++;
		if(f>b[i]&&f<=b[i+1]) return i+1;
	}
	return 0;//error
}
int my_find(int x){
	for(int i=1;i<=n/k;i++){   //在该块中继续寻找
		ans++;
		if(arr[i+n/k*(x-1)]==f) return i+n/k*(x-1);
	}
	return 0;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>arr[i];
	cin>>k;
	for(int i=1;i<=k;i++) cin>>b[i];
	int q;
	cin>>q;
	while(q--){
	  ans=0;
	  cin>>f;
	  int x=find_w();
	  if(x==0) {
	  	cout<<"error"<<endl;
	  	continue;
	  }
	  int y=my_find(x);
	  if(y==0) {
	  	cout<<"error"<<endl;
	  	continue;
	  }
	  cout<<y<<"-"<<ans<<endl;
	}
	return 0;
}

二、动态查找

表结构本身是在查找过程中动态生成的。若某值查找不成功,则插入该值。

1.二叉排序树 (插入、查找、删除)

左子树小于根,右子树大于根,中序遍历结果为从小到大的排序结果。

二叉排序树的插入、查找、删除,代码如下:

输入和输出:

第一行:初始结点个数n;随后输入n个结点。随后输出建树结果

第二行:输入需要查询的结点个数q; 

随后q行输入需要查询的结点。如果查找成功,则输出查找次数,否则输出error

接着:输入需要删除的结点个数d

随后d行输入需要删除的结点。如果删除成功,则输出删除后的二叉排序树,否则输出error

#include <iostream>
using namespace std;
int ans;
struct NODE {
	int data;
	NODE* left=nullptr;
	NODE *right=nullptr;
};
class BisortTree {
	public:
		NODE *root=nullptr;
		void* insert(NODE*&p,int key) { //建树过程
			if(!p) {
				p=new NODE();
				p->data=key;
			} else {
				if(p->data>key) insert(p->left,key);
				else insert(p->right,key);
			}
		}
		void find_key(NODE *now,int key) {
			if(key==now->data) return;
			if(!now->left&&!now->right) {
				ans=0;
				return;//ans代表查找次数,失败则为0
			}
			if(key<now->data) ans++,find_key(now->left,key);
			else ans++,find_key(now->right,key);
		}
		int B_delete(NODE *&now,int key) {
			if(!now) return 0; //error
			if(key==now->data) {
				NODE *p=now;
				if(!now->left&&!now->right) { //如果是叶子结点直接删除
					now=nullptr;
					delete p;
				} else if(!now->left) { //如果只有左孩子,就用左孩子替代它
					now=now->right;
					delete p;
				} else if(!now->right) { //如果只有右孩子,就用右孩子替代它
					now=now->left;
					delete p;
				} else { //既有左孩子又有右孩子
					NODE *parent=now; //记录q的前驱结点
					NODE *q=now->left;//左转
					while(q->right) { 	//然后向右找到尽头max,即先找到小于该结点的最大值
						parent=q;
						q=q->right;
					}
					now->data=q->data;
					if(parent!=now)
						parent->right=q->left;
					else parent->left=q->left;
					delete q;
				}
				return 1; //OK
			}
			if(key<now->data) B_delete(now->left,key);
			else B_delete(now->right,key); //首先要找到要删除的结点的位置
		}
		void inorder(NODE *now) { //中序遍历
			if(now->left) inorder(now->left);
			cout<<now->data<<" ";
			if(now->right) inorder(now->right);
		}
};
int main() {
	int n,q,d;
	cin>>n;
	BisortTree T;
	for(int i=1; i<=n; i++) {
		int key;
		cin>>key;
		T.insert(T.root,key);
	}
	T.inorder(T.root);
	cout<<endl;
	cin>>q;
	while(q--) {
		int key;
		cin>>key;
		ans=1;
		T.find_key(T.root,key);
		if(ans) cout<<ans<<endl;
		else cout<<"error"<<endl;
	}
	cin>>d;
	while(d--) {
		int key;
		cin>>key;
		int flag=T.B_delete(T.root,key);
		if(flag) T.inorder(T.root),cout<<endl;
		else cout<<"error"<<endl;
	}
	return 0;
}

ASL成功与删除的计算参考二分查找的判定树,方法很像。

时间复杂度:O(logn)。最坏时间复杂度:O(n)

2.平衡二叉树(AVL)

其特点为,树中的结点的左、右子树深度之差的绝对值不大于1,即为平衡因子,平衡因子只能取-1,0,1

(1)插入

如果在一棵平衡的二叉查找树中插入一个新结点,造成了不平衡,此时必须调整树的结构,使之平衡化。

  

(2)删除

平衡二叉树的删除与二叉排序树相同:

如果被删结点是叶子结点,那么直接删除;

如果有一个孩子,则让被删节点的父亲指向这个孩子;

如果有两个孩子,则找到被删节点的直接前驱(即小于该数的最大值)S,替代被删节点,然后对直接前驱S做删除处理。   注:S只能有一个左孩子或者没有孩子。 

 突然发现这个题没学代码怎么写,考试的话会理论应该就足够了。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值