数据结构第九章——查找

平均查找长度ASL

 一、静态查找表——线性表的查找

//数据类型说明
typedef    int KeyType;
typedef   struct
{
	KeyType  key;
	...
}ElemType;

//对两个关键字的比较一般如下约定
#define  EQ(a,b)     ((a) == (b))
#define  LT(a,b)     ((a) <(b))
#define  LQ(a,b)     ((a) <= (b))

1.顺序查找

(1)时间复杂度O(n)  ;  ASL = (n+1) / 2

(2)缺点:当n很大时,平均查找度较大,效率低

(3)优点:对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。

//静态查找表的 顺序存储结构 
typedef  struct
{
          ElemType  *elem;       /* 数组基址,0号单元留空 */
          int        length;          /* 表长度 */
  }SSTable;

int Search_Seq(SSTable ST, KeyType key) 
{  
   // 若找到,则函数值为该元素在表中的位置,否则为0。
   int i=0;
   ST.elem[0].key=key;   // "哨兵"
   for (i=ST.length;  ST.elem[i].key!=key;  --i);  
  // 从后往前找
   return i;      // 找不到时,i为0
} // Search_Seq

2.折半查找(二分查找)

int binary (NODE  r[], int  n, int  key )
{   int  low = 1,  high = n;     // 置区间初值
    while (low <= high)
    {  mid = (low + high) / 2;
        if (key == r[mid].key)
             return  mid;        // 找到待查元素
        else  if (key < r[mid].key )
             high = mid - 1;     // 继续在前半区间进行查找
        else  
             low = mid + 1; // 继续在后半区间进行查找
   }
    return 0;                 // 顺序表中不存在待查元素
}

3.分块查找(索引顺序表查找)


二、动态查找表——树表查找

1.二叉排序树

(1)定义

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

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

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

存储结构——二叉链表:
typedef struct   BiTNode
{
       	KeyType   key;
	TElemType   other;
	struct    BiTNode   *lchild;*rchild; 
                                      //左右孩子指针
  } BiTNode ,  *BiTree;

 (2)查找算法

BiTree SearchBST (BiTree T, KeyType key) 
{ /* 在根指针T所指二叉排序树中递归地查找其关键字等于key的数据元素,若查找成功,则返回指向该数据元素结点的指针,否则返回空指针 */
   if (!T || EQ(key, T->key)) return T;// 查找结束
   else if (LT(key, T->key)) 
      return SearchBST(T->lchild, key);  
        // 在左子树中继续查找 
   else 
      return SearchBST(T->rchild, key);  
       // 在右子树中继续查找
} // SearchBST

(3)二叉排序树的插入算法

 为空时新插入的是新的根结点,不为空时

插入算法:
Status InsertBST(BiTree &T, ElemType e) 
{  // 当二叉排序树T中不存在关键字等于e.key的数据元素时,
  // 插入e并返回TRUE,否则返回FALSE
 if ( !SearchBST (T, e.key, NULL, p)) 
  {   // 查找不成功,则插入
    s = (BiTree)malloc(sizeof(BiTNode));
    s->data = e;  s->lchild = s->rchild = NULL;  
    if (!p) T = s;        // 树是空树,插入 s 为新的根结点
    else if (LT(e.key, p->data.key)) p->lchild=s; // 插入s为左孩子
    else p->rchild = s;   // 插入 s 为右孩子
    return TRUE;
  } else return FALSE;  // 树中已有关键字相同的结点,不插入
} // Insert BST

 

(4)二叉排序树的删除

 1)若p为叶子节点可直接删除

f->rchild=NULL(或f->lchild=NULL) ; free(p);

 2)若p结点只有左子树,或只有右子树,则可将p的左子树或右子树直接改为其双亲结点f的左(右)子树,即:f->rchild=p->lchild(或f->rchild=p->rchild); free(p);

 3)若p既有左子树,又有右子树,设p为双亲f的左孩子。 此时有两种处理方法:

方法1:将p的左子树改为f的左子树,而将p的右子树改为s的右子树。

 方法2:用s结点的值替代p结点的值,再将s结点删除,原s结点的左子树改为s的双亲结点q的右子树。

删除算法:
Status DeleteBST(BiTree &T,  KeyType key) 
{ 
  // 若二叉排序树T中存在关键字等于key的数据元素时,
  // 则删除该数据元素结点p,并返回TRUE;否则返回FALSE
  if (!T) return FALSE;       // 不存在关键字等于key的数据元素
  else {
    if (EQ(key, T->data.key))  // 找到关键字等于key的数据元素
      return Delete(T); 
    else if (LT(key, T->data.key)) return DeleteBST(T->lchild, key);
    else return DeleteBST(T->rchild, key);
  }
} // DeleteBST


从二叉排序树中删除结点p,并重接它的左或右子树:
Status Delete(BiTree &p) 
{
	BiTree q, s;
  if (!p->rchild) 
  {  // 右子树空则只需重接它的左子树
    q = p;  p = p->lchild;  free(q);
  } 
  else if (!p->lchild) 
  {  // 只需重接它的右子树
    q = p;  p = p->rchild;  free(q);
  } 
else 
  {  // 左右子树均不空
    q = p;  s = p->lchild;
    while (s->rchild)   // 找左子树上的最大的结点
      { q = s;  s = s->rchild; }
    p->data = s->data;                  // 替换掉待删除数据
    if (q != p) q->rchild = s->lchild;  // 重接*q的右子树
    else q->lchild = s->lchild;         // 重接*q的左子树
    free(s);    
  }
  return TRUE;
} // Delete

 (5)ASL

1)最差的情况:当关键字有序时,树的深度为n,其平均查找次数为(n+1)/2。

2)最好的情况:二叉排序树的形态和折半查找的判定树相同,平均查找次数和log2n成正比。

2.平衡二叉树

1.AVL树:若一棵二叉排序树中每个结点的左、右子树的高度至多相差1,则称此二叉树为平衡二叉树。

平衡因子BF(Balance Factor) :二叉树中每个结点的平衡因子是该结点左子树的高度减去右子树的高度。 从平衡因子的角度可以说,若一棵二叉树中所有结点的平衡因子的绝对值小于或等于1,即平衡因子取值为1、0或-1,则该二叉树称为平衡二叉树。

 二叉排序树——>平衡树

(1)单向右旋平衡处理,LL型调整:在A的左孩子的左子树上插入结点,使A的BF(1→2)而失去平衡。(B变为根结点,A变为B的右孩子,B原来的右孩子变成了A的左孩子

将结点B提升为新的二叉树的根,结点A连同其右子树向右下旋转,使其成为B的右子树,B的原右子树则作为A的左子树。

(2)单向左旋平衡处理, RR型调整:在A的右孩子的右子树上插入结点,使A的BF(-1→-2)而失去平衡

将结点B提升为新的二叉树的根,结点A连同其左子树向左下旋转,使其成为B的左子树,B的原左子树则作为A的右子树。

 (3)双向旋转(先左后右)平衡处理,LR型调整:在A的左孩子的右子树上插入结点,使A的  BF(1→2)而失去平衡

(4) 双向旋转(先右后左)平衡处理, RL型调整:在A的右孩子的左子树上插入结点,使A的 BF(-1→-2)而失去平衡

3.B+ 树和 B- 树


三、哈希表及其查找(计算式查找)

1.哈希表

(1)以 f(key) 作为关键字为 key 的记录在表中的位置,通常称这个函数 f(key) 为哈希函数。

(2)根据设定的哈希函数 H(key) 和所选中的处理冲突的方法,将一组关键字映象到一个有限的、地址连续的地址集 (区间) 上,并以关键字在地址集中的“象”作为相应记录在表中的存储位置,如此构造所得的查找表称之为“哈希表”。

2.哈希函数的构造方法

(1)直接定址法

哈希函数为关键字的线性函数  H(key) = key   或者  H(key) = a  key + b

(2)数字分析法

能预先估计出全体关键字的每一位上各种数字出现的频度。

(3)平方取中法

以关键字的平方值的中间几位作为存储地址。关键字中的每一位都有某些数字重复出现频度很高的现象。

(4)折叠法

将关键字分割成若干部分,然后取它们的叠加和为哈希地址。

关键字的数字位数特别多。

(5)除留余数法

设定哈希函数为:H(key) = key MOD p  ;其中,  p≤m (表长)  并且 p 应为不大于 m 的素数或是不含 20 以下的质因子

(6)随机数法

设定哈希函数为: H(key) = Random(key) 其中,Random 为伪随机函数。

通常,此方法用于对长度不等的关键字构造哈希函数。

3.处理冲突的方法

(1)开放定址法

为产生冲突的地址求得一个地址序列:  H0, H1, H2, …, Hs     1≤ s≤m-1

其中:H0 = H(key) ,H(key)为哈希函数,m为哈希表表长            

Hi = ( H(key) + di ) MOD m ,   i=1, 2, …, s

(2)链地址法

将所有哈希地址相同的记录 都链接在同一链表中。 

(3)建立公共溢出区

设哈希函数产生的哈希地址集为[0,m-1],则分配两个表:

基本表 ElemType base_tbl[m];    

溢出表 ElemType over_tbl[k];

4.哈希表的查找

(1)哈希表是一种重要的存储方法,也是一种查找方法。

(2)哈希表饱和的程度,装填因子  α=n/m 值的大小(n—记录数,m—表的长度)

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值