查找算法

查找算法

查找分类:静态查找,动态查找


查找结构:

1.静态查找:顺序查找,二分查找,递归查找

2.动态查找:二叉排序树,散列表查找


顺序查找

就是从第一个开始遍历然后找出要查找的数

int find(int a[],int res)//传进去一个数组和要查找的数字
{
int k = a.length();//可能c++没有这个数组的长度的,但是差不多就是这个意思
int deng = -1;//记录查找的下标,先初始化为-1
for(int i = 0; i < k; i ++)
{
if(a[i] == res)
deng = i;
}
   if(deng == -1)//没有找到
return 0;
   else//找到了
   return deng;
}

 

还有设置哨兵

int find(int a[],int res)
{
   int i = a.length();
   a[0] = res;           //我们假设a[0]是没有元素的
   while(a[i] != res)
  {
       i--;
  }
   return i;
}

相比于第一个只需要判断一次就可以,因为第一次for循环判断一次,if又判断一次


时间复杂度都是O(n)

二分查找

一般二分查找,都是整数二分

对了二分查找前提得是先有序,可以sort排序

int find(int a[], int res)
{
   int k = a.length();
   int l = 1;
   int h = k;
   low = (l + h) / 2;
   while(l <= h)
  {
       if(mid == res)
      {
           return mid - 1;
      }
       if(mid < res)
      {
           l = mid + 1;
           mid = l + h / 2;
      }
       if(mid > res)
      {
           h = mid - 1;
           mid = l + h / 2;
      }
  }
   return 0;
}

这个都是自己写的,比较繁琐,但是我感觉应该能看的明白

在书的195页还是建议找一个数组顺一顺,代码其实很容易懂

时间复杂度O(log2n)

//插值查找(优化的二分)
//和二分查找没有什么两样,就是mid的值是取中间的值
//然而插值查找是取res值大概占整个数组的多少比例
mid = l + (res - a[l])/(a[h] - l)*(h - l);
(res - a[l])分子
(a[h] - l) 分母
(res - a[l])/(a[h] - l)比例
(res - a[l])/(a[h] - l)*(h - l)站大致位置

时间复杂度O(log2n)

相对于数据增加差不多的

关于折半查找的ASL

ASL = (1/n)*(每个元素查到需要的次数)

n = 一共多少元素

折半查找相当于一个二叉树(不难想到嗷,想一想树的查找)

查到需要的次数也就是树的深度

这个博客写了ASL的查找方法

 


 

分块查找==索引排序查找

 

核心思想:块内无序,块间有序,可以找到每大块的最大值,从区间找,性能介于顺序查找和折半查找(可以说这个分块是先折半查找,在顺序查找,因为分块就相当于折半,遍历块相当于顺序查找)

算法:

建立索引表,包括两大内容,1.关键字(子表最大值)2.指针项(每一块的最开始位置)

ASL:

假设n个元素,分为b块,每块里面有k个元素

所以b = n / k, 总的ASL应该分为b的ASL和K的ASL,最后等于1/2*(n/k + k) + 1(实在是难写表达式p197有)

 


树表的查找

二叉排序树

void searchTree(Tree t,int key)
{
   if(k == t->data.key || (!t))return t;
   else if(k > t->data.key)return searchTree (t->lchild,k);
   else if(k < t->data.key)return searchTree (t->rchilde,k);
}

就是一个递归的思想,如果key>data.key往右找,key<data.key往左找

可想而知最后的结果是中序遍历的结果,因为是树所以和二分时间复杂度一样都是O(log2n)

ASL也是同理

 

二叉排序树的插入

插入的基础上要有查找

void insertTree(Tree t,int key)
{
   if(!t)
  {
       s = new TreeNode;
       s->data = key;
       s->lchild = s->rchild = null;
  }
   else if(key > t->data.key)
  {
       return insertTree(t->lchild,key);
  }
   else if(key < t->data.key)
  {
       return insertTree(t-<rchild,key);
  }
}

 

这个实际上也是一个递归

二叉排序树的删除

image-20211116174659120

分为三种,叶子,只有一颗子树,有两个子树

我们讲一下如果删除105该怎么办

因为是中序遍历,所以找直接前驱,让104补上去,然后103这个节点断了再补充到100的左边


 

平衡二叉树

定义:要么是一个空树,要么他的左子树和左子树都是平衡二叉树,且左子树和右子树的深度之差绝对值不超过1(还有二叉排序树,就是左子树小于右子树)

平衡因子:左子树和右子树的深度之差

平衡二叉树的平衡调整方法:

LL :左边多了,需要最右边的转下来,平衡一下平衡因子

RR :右边多了,需要最左边的转下来,平衡一下平衡因子

LR :首先先转R交换位置并于L一条线,然后变成LL线,同上了

RL:首先先转L交换位置并于R一条线,然后变成LL线,同上了

例题image-20211116195111179

还有一到十二月份的排序表

【C语言描述】《数据结构和算法》哔哩哔哩bilibili

 

散列表(哈希表)查找

取别名,当时记得是找小姚明,学校名单没有小姚明,但是去了体育场发现了小姚明,因为打篮球打的好,所以起外号叫小姚明

(1)散列函数,散列地址:在存储key时候用函数H()计算出的地址p,p = H(key);

(2)散列表:连续的地址空间

构造散列函数

直接定址法:需要知道关键字的分布情况,这个表就是减去了1980得出来的(我感觉知道不知道的都是那么一回事)

image-20211116202341669

数字分析法:

image-20211116202509501

平方取中法:

 

image-20211116202611138

 

折叠法:

image-20211116202710509

除留余数法:

image-20211116202800600

解决冲突的两种方法

1.地址开方法

H = (H(key) + d)%m

线性探测法d = 1 , 2 , 3 ...m - 1

二次探测法d = 1^2, -1^2,2^2, -2^2....K^2,-K^2(K<=m/2)

伪随机数法

2.链地址法

就是散列地址相同的放在一块就和链表差不多

posted @ 2021-11-16 20:39  爽爽子的秃头生活  阅读(90)  评论(0)    收藏  举报