0.PTA得分截图

1.本周学习总结

1.1 总结

1.1.1 查找的性能指标ASL。分析静态查找几种算法包括:顺序查找、二分查找的成功ASL和不成功ASL

  • ASL:查找算法的查找成功时的平均查找长度的缩写,是为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值。

  • 顺序查找:从表的一端开始,顺序扫描线性表,依次将扫描到的关键字与给定关键字k相比较,若当前扫描到的关键字与k相等,则查找成功;若扫描结束后,仍未找到关键字等于k的记录,则查找失败

    • 时间复杂度为O(n)
    • 成功时的平均查找长度为 ASL = (n+1)/2,不成功时的平均查找长度 ASL = n
  • 二分查找:二分查找也称为折半查找,要求线性表中的节点必须已按关键字值的递增或递减顺序排列

    • 时间复杂度为O(log2n)
    • 成功时的ASL为log2(n+1)取上限,不成功时的ASL也为log2(n+1)取上限

1.1.2 动态查找:二叉搜索树。如何构建、插入、删除。会操作及代码

  • 二叉搜索树:要么是一棵空树,要么是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值,且它的左、右子树也分别为二叉排序树,又称为二叉查找树、二叉排序树。

  • 二叉排序树的操作主要有:构建、查找、插入、删除

    • 查找:
BSTNode* SearchBST(BSTNode* bt, int k)  //非递归算法
{
    while (bt != NULL)
    {
        if (k == bt->key)
        {
  	      return bt;
        }
        else if (k < bt->key)
        {
  	      bt = bt->lchild;  //在左子树中迭代查找
        }
        else
        {
  	      bt = bt->rchild;  //在右子树中迭代查找
        }
    }
    return NULL;  //没有找到返回NULL
}
  • 建立:
BinTree CreateBST(int A[], int n)  //返回树根指针
{
    BinTree bt = NULL;  //初始时bt为空树
    int i = 0;
    while (i < n)
    {
        bt = Insert(bt, A[i]);  //将A[i]插入到二叉排序树中
        i++;
    }
    return bt;  //返回建立的二叉排序树的根指针
}
  • 插入:
BinTree Insert(BinTree BST, ElementType X)  //二叉排序树中插入元素X
{
    if (BST == NULL)  //若二叉排序树为空,创建新节点
    {
        BST = (struct TNode*)malloc(sizeof(struct TNode));  //新节点申请空间
        BST->Data = X;
        BST->Left = NULL;
        BST->Right = NULL;
    }
    else if (BST->Data == X)  //二叉排序树中已有此关键字,无需插入
    {
        return BST;
    }
    else if (X < BST->Data)
    {
        BST->Left= Insert(BST->Left, X);  //元素插入到左子树中
    }
    else
    {
        BST->Right= Insert(BST->Right, X);  //元素插入到右子树中
    }  
    return BST;
}
  • 删除:
BinTree Delete(BinTree BST, ElementType X)
{
  BinTree ptr, q;
  if (BST == NULL)  //树为空,直接返回
  {
      printf("Not Found\n");
      return BST;
  }
  else
  {
      if (X < BST->Data)
      {
  	    BST->Left = Delete(BST->Left, X);  //递归在左子树中删除关键字为X的节点
      }
      else if (X > BST->Data)
      {
  	    BST->Right = Delete(BST->Right, X);  //递归在右子树中删除关键字为X的节点
      }
      else
      {
  	    if (BST->Right == NULL)  //没有右子树
  	    {
  		    ptr = BST;
  		    BST = BST->Left;
  		    free(ptr);
  	    }
  	    else if (BST->Left == NULL)  //没有左子树
  	    {
  		    ptr = BST;
  		    BST = BST->Right;
  		    free(ptr);
  	    }
  	    else if (BST->Left != NULL && BST->Right != NULL)  //既有左子树又有右子树
  	    {

  		    ptr = FindMax(BST->Left);  //查找左子树最右孩子
  		    BST->Data = ptr->Data;
  		    BST->Left = Delete(BST->Left, BST->Data);
  	    }
      }
      return BST;
  }
}

1.1.3 AVL树的定义及4种调整做法

  • AVL树的定义:AVL树本质上还是一棵二叉搜索树,它的特点是:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

  • AVL树的4种调整:如果在一棵AVL树中插入一个新节点,就有可能造成失衡,此时必须重新调整树的结构,使之恢复平衡。且若有多个失衡点,则从最下面失衡点开始调整。调整平衡过程称为平衡旋转.

    • LL型调整:

    • RR调整:

    • LR型调整:

    • RL型调整:

1.1.4 B-树和B+树定义。主要介绍B-树的插入、删除的操作

  • B-树:又称B树,一个节点可放多个关键字,从而降低树的高度。适合大数据量查找,如数据库中的数据,B-树又称为多路平衡查找树

  • B-树特点:

    • 根结点至少有两个子女;
    • 每个非根节点所包含的关键字个数 j 满足:(m/2 )- 1 <= j <= m - 1;
    • 除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:(m/2) <= k <= m ;
    • 所有的叶子结点都位于同一层。
  • B-树的插入

    1. 树的初始化时如下:

    2. 插入一个关键字:30:

    3. 再插入26,得到如下的结果:

    4. 此时如图所示,在插入的那个终端结点中,它的关键字数已经超过了m-1=2,所以我们需要对结点进分裂,所以我们先对关键字排序,得到:26 30 37 ,所以它的左部分为(不包括中间值):26,中间值为:30,右部为:37,左部放在原来的结点,右部放入新的结点,而中间值则插入到父结点,并且父结点会产生一个新的指针,指向新的结点的位置,如下图所示:

    5. 继续插入新的关键字:85:

    6. 对刚才插入的那个结点进行“分裂”操作,操作方式和之前的一样:

    7. 结点的父亲结点的度为4了,说明它的关键字数超过了m-1,所以需要对其父结点进行“分裂”操作:

  • B-树的删除

    1. 利用前述的B-树的查找算法找出该关键字所在的结点。然后根据 k(需要删除的关键字)所在结点是否为叶子结点有不同的处理方法。如果没有找到,则直接返回。
    2. 若该结点为非叶结点,且被删关键字为该结点中第i个关键字key[i],则可从指针son[i]所指的子树中找出最小关键字Y,代替key[i]的位置,然后在叶结点中删去Y
    3. 如果被删关键字所在结点的原关键字个数n>=[m/2] ( 上取整),说明删去该关键字后该结点仍满足B-树的定义。这种情况最为简单,只需删除对应的关键字:k和指针:A 即可。
    4. 如果被删关键字所在结点的关键字个数n等于( 上取整)[ m/2 ]-1,说明删去该关键字后该结点将不满足B-树的定义,需要调整。
  • 调整过程为:如果其左右兄弟结点中有“多余”的关键字,即与该结点相邻的右兄弟(或左兄弟)结点中的关键字数目大于( 上取整)[m/2]-1。则可将右兄弟(或左兄弟)结点中最小关键字(或最大的关键字)上移至双亲结点。而将双亲结点中小(大)于该上移关键字的关键字下移至被删关键字所在结点中。

1.1.5 散列查找。哈希表和哈希链2种构造方法、相关AVL计算。

  • 散列查找:散列查找的查找时间与问题规模无关,查找的时间复杂度为O(1)

    • 哈希表:通过哈希函数,将关键字一一映射到数组中。例如:关键字7,哈希函数为存储地址=key%3,则关键字7的存储地址为数组中的第二个位置,即a[1]。但是不同的关键字根据哈希函数映射后,可能会对应同一存储地址,比如上述的关键字7和关键字10通过上述哈希函数映射后的地址都是a[1],这就叫哈希冲突。因为在实际中,哈希冲突是难以避免的,所以哈希表的设计主要需要解决哈希冲突
    • 装填因子α:α=存储的关键字个数/哈希表大小(长度);α越小,冲突的可能性就越小,反之越大;与所采用的哈希函数和解决冲突的方法有关。
    • 解决冲突的方法:
      1. 开放定址法:线性探查法,即在遇到冲突时,冲突位置一直+1直到找到空位置或超过哈希表长为止 ;平方探查法,即在冲突位置探查±i²(1≤i≤哈希表长-1)的位置,直到找到空位置或超过哈希表的尽头为止(表头或表尾);
      2. 拉链法:即通过链表的方式,将因哈希冲突映射到同一地址的所有关键字用链表链起来(哈希链)。

1.2.谈谈你对查找的认识及学习体会

  • 查找的认识:在这个信息时代,查找的重要性不言而喻,借助查找就可以在短时间内找到对应的客户信息、学生信息等。所以说,查找的实际用途十分广,和我们如今的生活息息相关。

  • 学习体会:对查找主要学了线性表查找、树表查找以及哈希表的查找。线性表查找主要是顺序查找、折半查找和分块查找,树表查找主要是二叉排序树、AVL树、B-树和B+树,哈希表查找主要是开发地址法建哈希表查找和拉链法建哈希表查找。无论哪种查找,都要涉及插入、创建、删除、查找和计算成功和不成功的ASL等操作。

2.PTA题目介绍

2.1 7-5(哈希链) 航空公司VIP客户查询

2.1.2 该题的伪代码

定义map类 airline
for i=0 to n
    输入用户,航程
    航程不满最小值替换为最小值
    用户,航程存入map类
for i=0 to n
    输入用户
    if 找到
        输出航程
    else
        输出No Info

2.1.3 PTA提交列表

主要是二三点的超时问题,百思不得其解,对比了其它人的代码发现同样的思路使用scanf,printf可以压着时间过;查了资料后知道c++中cin与cout要与stdio同步,中间会有缓存时间,导致超时,可以用ios::sync_with_stdio(false);取消cin cout 与 stdio 同步......虽然我不懂什么意思

2.1.4 本题设计的知识点

  1. 回顾了map容器的使用
  2. 知道了c++中cin与cout要与stdio同步,虽然不知道什么意思
posted on 2020-05-24 21:19  飞白2020  阅读(194)  评论(0编辑  收藏  举报