• [数据结构-划分树小结]


    划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k大值。

    先看下图已经建好的划分树是什么样子的,原始数组是[1,5,2,3,6,4,7,3,0,0],并把它作为树的第0层,然后把这些数中较小的数再组成[1,2,3,0,0],顺序还是遵照原数组的顺序,同样将较大的数再组成[5,6,4,7,3]。把这两个数组作为树的第二层,它们的父亲则是第一层的原始数组,这样一直往下建立,就把划分树建好了。

    那么到底如何建立这个划分树呢?例如如何将[1,5,2,3,6,4,7,3,0,0]分为[1,2,3,0,0]和[5,6,4,7,3]两部分?

    设[1,5,2,3,6,4,7,3,0,0]为数组A,长度为len。

    首先预处理将数组A排序:[0,0,1,2,3,3,4,5,6,7],然后在A中分别找到“0,0,1,2,3”这(len/2)个数,并按数组A的顺序放到下一层[1,2,3,0,0]。可以发现,这一操作的复杂度是O(len)。

    下面看如何查询在某一区间内的第k小的数。仍然观察下图,要查找区间[3,9]中第2小的数,先标记在原数组A中[3,9]这个区间(数组下标从1开始),即val[0]层涂上黄色背景的部分,现在我们如何知道这个黄色背景的部分在下一层如何分布呢?这里就需要用到一个数组toleft[ ][ ] ,toleft[dep][i]表示下标小于等于i的数中在第dep层中分在其左孩子的个数。

    比如图中val[0]这一层中,toleft[0][1]=1,toleft[0][2]=1,toleft[0][3]=2…..。这样的话就可以利用toleft数组来确定黄色背景区域在下一层如何分布了。可以确定[3,9]区间在左孩子的有left[0][9]-left[0][3]=3个,所以最终结果肯定在左孩子中找了。当然在这个左孩子中,要排除掉蓝色背景部分的1,0,就顺利得到下一步要在第二层查找的是:区间[2,4]的第2小的数。依次类推,得到最终结果。

    clip_image002

    图片摘自http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html

    /*
    * 划分树(查询区间第k大)
    */
    const int MAXN = 100010;
    int tree[20][MAXN];//表示每层每个位置的值
    int sorted[MAXN];//已经排序好的数
    int toleft[20][MAXN];//toleft[p][i]表示第i层从1到i有数分入左边
    void build(int l,int r,int dep)
    上海大学 ACM 模板 by kuangbin
    40 / 152  ACM  模板  kuangbin
    {
        if(l == r)return;
        int mid = (l+r)>>1;
        int same = mid - l + 1;//表示等于中间值而且被分入左边的个数
        for(int i = l; i <= r; i++) //注意是l,不是one
            if(tree[dep][i] < sorted[mid])
                same--;
        int lpos = l;
        int rpos = mid+1;
        for(int i = l; i <= r; i++)
        {
            if(tree[dep][i] < sorted[mid])
                tree[dep+1][lpos++] = tree[dep][i];
            else if(tree[dep][i] == sorted[mid] && same > 0)
            {
                tree[dep+1][lpos++] = tree[dep][i];
                same--;
            }
            else
                tree[dep+1][rpos++] = tree[dep][i];
            toleft[dep][i] = toleft[dep][l-1] + lpos - l;
        }
        build(l,mid,dep+1);
        build(mid+1,r,dep+1);
    }
    //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
    int query(int L,int R,int l,int r,int dep,int k)
    {
        if(l == r)return tree[dep][l];
        int mid = (L+R)>>1;
        int cnt = toleft[dep][r] - toleft[dep][l-1];
        if(cnt >= k)
        {
            int newl = L + toleft[dep][l-1] - toleft[dep][L-1];
            int newr = newl + cnt - 1;
            return query(L,mid,newl,newr,dep+1,k);
        }
        else
        {
            int newr = r + toleft[dep][R] - toleft[dep][r];
            int newl = newr - (r-l-cnt);
            return query(mid+1,R,newl,newr,dep+1,k-cnt);
        }
    }
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)==2)
        {
            memset(tree,0,sizeof(tree));
            for(int i = 1; i <= n; i++)
            {
                scanf("%d",&tree[0][i]);
                sorted[i] = tree[0][i];
            }
            sort(sorted+1,sorted+n+1);
            build(1,n,0);
            int s,t,k;
            while(m--)
            {
                scanf("%d%d%d",&s,&t,&k);
                printf("%d
    ",query(1,n,s,t,0,k));
            }
        }
        return 0;
    }
  • 相关阅读:
    有效的形成传感器(执行器)的控制方式
    QT进行多传感器(执行器)的编程框架
    Pytorch 分割模型构建和训练【直播】2019 年县域农业大脑AI挑战赛---(四)模型构建和网络训练
    9月份以前还是深入了解各个技术原理吧
    位操作基础篇
    实现一个简单实用的动态加载上千条目的UGUI模块
    C++题目汇总
    Leetcode 92. Reverse Linked List II
    Leetcode Reverse Linked List
    Lintcode Recover Rotated Sorted Array
  • 原文地址:https://www.cnblogs.com/lastone/p/5275860.html
Copyright © 2020-2023  润新知