首页 > 其他 > 详细

<知识整理>2019清北学堂提高储备D2

时间:2019-05-01 14:17:06      阅读:169      评论:0      收藏:0      [点我收藏+]

简单数据结构:

  一、二叉搜索树

  1、前置技能:

      n/1+n/2+……+n/n=O(n log n)  (本天复杂度常涉及技术分享图片

  2、入门题引入:

    技术分享图片

    N<=100000.

    这里多了一个删除的操作,因此要将所有的数都记录下来维护。一个个枚举很容易超时,这时就到了二叉搜索树显示本领的时候了。

    技术分享图片

    技术分享图片

    (注:子树节点的key值小/大于这个点,即子树中所有的节点的key值都小/大于这个点。同时不考虑有两个节点key值相等的情况)

      实例:

      技术分享图片

    技术分享图片

    技术分享图片

      1、查询最大/小值:

        技术分享图片

        最大值自然就是往右儿子走啦。

        核心代码(最小值):

        

1 int FindMin()
2 {
3     int x=root;
4     while(ls[x]) x=ls[x];
5     return key[x];
6 }

 

      2、插入一个值:

      技术分享图片

      核心代码:

      

 1 void insert(int val) {
 2     key[+ + tot] = val;
 3     ls[tot] = rs[tot] = 0;
 4     int now = root;//当前访问的节点为now.
 5     for(; ;) {
 6         if (val < key[now])
 7             if (!ls[now]) ls[now] = x, fa[x] = now, break;
 8             else now =ls[now];
 9         else if (!rs[now]) rs[now] = x, fa[x] =now, break;
10             else now = rs[now];
11     }
12 }

      3、删除一个值

      技术分享图片

      代码:

      

1 int Find(int x)
2 {
3     int now = root;
4     while(key[now]! = x)
5     if (key[now] < x) now = rs[now]; else now = ls[now];
6     return now;
7 }

       技术分享图片

        设y是x右子树中所有点里,权值最小的点,则y必没有左儿子。找这个点可以从x先走一下右儿子,再走一下左儿子。找到y后,把y的右子树接到y的父节点上(必为其左子树),再用y覆盖掉x就行了。  

        技术分享图片

      代码:

        

//(由于不需要求每个节点的子树大小,在相应的代码前打上的注释号)
void del(int x)//删除一个值为x的点
{
        int id=Find(x),t=fa[id];//找到这个点的编号 
        if (!ls[id]&&!rs[id]) 
        {
                if (ls[t]==id) ls[t]=0; else rs[t]=0; //去掉儿子边
    //            for (i=id;i;i=fa[i]) size[i]--; 
        }
        else
        if (!ls[id]||!rs[id])
        {
                int child=ls[id]+rs[id];//找存在的儿子的编号 
                if (ls[t]==id) ls[t]=child;     
                else rs[t]=child;
                fa[child]=t;//让父亲认自己儿子为儿子,自己受到冷淡 
    //            for (i=id;i;i=fa[i]) size[i]--;
        }
        else
        {
                int y=rs[id]; while (ls[y]) y=ls[y]; //找y
                if (rs[id]==y) //y正好是这个点的右儿子,则直接替代它(篡位…) 
                {
                        if (ls[t]==id) ls[t]=y; else rs[t]=y;
                        fa[y]=t;
                        ls[y]=ls[id];
                        fa[ls[id]]=y;
                    //    for (i=id;i;i=fa[i]) size[i]--;
                    //    size[y]=size[ls[y]]+size[rs[y]];//y的子树大小需要更新 
                }
                else //最复杂的情况         
                {
                    //    for (i=fa[y];i;i=fa[i]) size[i]--;//注意到变换完之后y到root路径上每个点的size都减少了1
                        int tt=fa[y]; //先把y提出来 
                        ls[tt]=rs[y];
                        fa[rs[y]]=tt;              //再来提出x          
                        if (ls[t]==x)
                        {
                            ls[t]=y;
                            fa[y]=t;
                            ls[y]=ls[id];
                            rs[y]=rs[id];
                        }
                        else
                        {
                            rs[t]=y;
                            fa[y]=t;
                            ls[y]=ls[id];
                            rs[y]=rs[id];
                        }
                //        size[y]=size[ls[y]]+size[rs[y]]+1;//更新一下size 
                }
        }
}

        (这么长的代码吓我一跳) 

     4、

      技术分享图片

      (注:子树长度包括该子树的根)

      代码:

    

 1 int Findkth(int now, int k)//当前根节点     “第k大”的k 
 2 {
 3     if (size[rs[now]] >= k) //第k大在右子树 
 4         return Findkth(rs[now], k);
 5     else 
 6         if (size[rs[now]] + 1 == k) //当前即为第k大 
 7             return key[now];
 8         else //第k大在左子树,由于递归后要不受递归前右子树的影响,递归进去的k进去时对递归前的整体而言,进去后只对递归后的整体而言,
 9              //因此k应减去递归前右子树和根节点的数目 
10             return Findkth(ls[now], k - size[rs[now]] - 1);
11 }

      5、遍历:

      技术分享图片

        代码:

        技术分享图片

     让我们回到最初的题。
      一个良好的例子(数据):3 1 2 4 5(3层深的树)
      一个糟糕的例子(数据):1 2 3 4 5(5层深的树)
      二叉搜索树每次操作访问O(h)个节点。

      (故建树时可先sort一遍,以中间点为根,这样深度基本就位logn级别的了)

      技术分享图片

  二、二叉堆

    https://www.cnblogs.com/InductiveSorting-QYF/p/10776293.html(曾经写过,安利一下,在这里就只写写新东西吧 

    堆没有二叉搜索树的性质,即左边的不一定比右边的小。堆只满足儿子与父亲的关系,因此常常用来搞最大/最小值。  

   1、建堆:

      除了一个个插入以外,还有一个时间复杂度相差不大(稍慢一点)、很便捷的方法:直接sort排下序。

   2、查询最大/最小值:大/小根堆的堆顶就是啦。

   3、插入删除:详见链接。

   4、修改一个点的权值(以小根堆为例):变小往上浮,变大往最小的儿子的方向下潜(交换)

   应用:堆排序(详见链接)

    例题:  

     1、丑数

     技术分享图片

      题解:

      考虑构造小根堆。

      技术分享图片

    EX:  

     技术分享图片

        (注:技术分享图片:优先队列,跟堆差不多。默认大根堆,改成小根堆一是可以重载小于号成大于号的功能(感觉怪怪的),而是在定义的类型后加上“vector<类型>,greater<类型>  ”(注意最后有空格,否则右移运算符警告))

       Q.push(x)插入一个元素
       Q.top()访问堆顶
       Q.pop()删除一个
       Q.clear() 清空

    技术分享图片

    深入学习:https://blog.csdn.net/byn12345/article/details/79523516

      浅谈:set可看做集合,内部实际上采用红黑树。有两个特点:1、自动排序。2、每个元素只会出现一次(即没有值相等的两个元素出现在同一个set中)

        功能函数:

  .insert(x)            ,向容器插入元素x

  .erase(x)    ,删除容器中的元素x

  .begin()        ,返回set容器第一个元素的迭代器

  .end()      ,返回一个指向当前set末尾元素的下一位置的迭代器.

  .clear()          ,删除set容器中的所有的元素

  .empty()    ,判断set容器是否为空

  .max_size()   ,返回set容器可能包含的元素最大个数(set的最大容量)

  .size()      ,返回当前set容器中的元素个数

  .find()     ,

        注:访问set容器中的元素应在指向该元素的迭代器前加“*”(星号)

            声明迭代器:    set<类型>::iterator 名字;  注:迭代器只支持++和--两种运算。

      

 

       

 

 

    

 

 

      

 

 

      

 

<知识整理>2019清北学堂提高储备D2

原文:https://www.cnblogs.com/InductiveSorting-QYF/p/10792759.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!