zoukankan      html  css  js  c++  java
  • 笔试算法题(31):将有序数组转换成BST表示 & 线段树的应用

    出题:要求将一个有序整数数组转换成最小深度的Binary Search Tree表示;

    分析:由于需要是最小深度,所以BST应保持平衡,左右节点数大致相当,并且BST中当前根节点大于所有其左子树中的元素,小于所有其右子树中的元素。对于排序数组而言,中间元素必然作为根节点,然后递归对由中间元素分割的左右数组部分进行处理;

    解题:

     1 struct Node {
     2         int value;
     3         Node *left;
     4         Node *right;
     5 };
     6 
     7 Node* Array2BST(int *array, int i, int j) {
     8         /**
     9          * 如果上下范围相同说明只有一个节点
    10          * 直接返回
    11          * */
    12         if(i==j) {
    13                 Node *root=new Node();
    14                 root->value=array[i];
    15                 root->left=NULL;
    16                 root->right=NULL;
    17                 return root;
    18         }
    19 
    20         int m=(i+j)/2;
    21         Node *root=new Node();
    22         root->value=array[m];
    23         /**
    24          * 由于(i+j)/2是向下取整,所以i+1=j时
    25          * m=i,这个时候root的左子树为空
    26          * */
    27         if(m==i)
    28                 root->left=NULL;
    29         else
    30                 root->left=Array2BST(array, i, m-1);
    31         root->right=Array2BST(array, m+1, j);
    32 
    33         return root;
    34 }

    出题:要求实现函数:
      size_t foo(unsigned int *a1, size_t a1l, unsigned int *a2, size_t a2l)
      其中a1和a2都是无符号数组,a1l和a2l是对应数组的长度,并且都为偶数。
      无符号数分成两个数字组成的多个数字区间,如下:
        a1为0,1,3,8,10,20
        a2为0,1,20,50,4,5
      则a1表示的区间为[0,1],[3,8],[10,20]
      则a2表示的区间为[0,1],[20,50],[4,5]
      所以a1和a2重叠的部分为[0,1],[4,5],长度为2
      foo函数用于求给定区间数组a1和a2的重叠长度
      注意:a1和a2长度不超过100万,并且同一个区间数组内部可能出现重叠

    分析:

    • 线段树(Interval Tree)是BST的一种特例,它将一个区间划分成单元区间(最小元素),每个单元区间对应一个叶子节点;对于一个内部节点而言[a,b],它的左子树节 点表示为[a,(a+b)/2],它的右子树节点表示为[(a+b)/2+1,b]。由于每次划分都是对等划分所以IT为平衡树,最终子节点数为N(对于 整数区间而言就是所有数字分布的范围大小),深度为logN +1。快速确定某一个元素在哪些区间节点出现,时间复杂度为O(NlogN),但是空间复杂度为O(N);
    • 确定区间数组A和B中差值较小的的最大值m和最小值n(假设数组A的区间范围更小,O(A)),并构建区间[m,n]的线段树(O(AlogA)),这样 可以节省空间,如果区间范围较大,可以使用离散的建树方法针对区间元素出现的范围构建多棵区间树,或者使用压缩的方法减少区间节点;
    • 在线段树中标记在A中出现的区间节点(O(AlogA));将B的区间节点插入线段树(实际上并没有新节点的插入,只是检测是否有A标记过的节点),如果有A区间节点标记的节点,则说明有重复(O(BlogA))。总时间复杂度O(AlogA);

    解题:

      1 struct Interval {
      2         int left;
      3         int right;
      4         int count;
      5 };
      6 /**
      7  * 此函数用于创建线段树的基本结构
      8  * */
      9 void ConstructIT(int min, int max, Interval** InterTree, int index) {
     10         /**
     11          * 构建当前节点
     12          * index用于索引InterTree中当前元素的位置
     13          * 遵循父节点为i,则做有子节点分别为2*i, 2*i+1
     14          * */
     15         Interval *temp=new Interval();
     16         temp->count=0;
     17         temp->left=min;
     18         temp->right=max;
     19         InterTree[index]=temp;
     20         /**
     21          * 如果min和max相等,则说明已经到叶子节点
     22          * */
     23         if(min==max)
     24                 return;
     25         /**
     26          * 分别递归创建左右子节点
     27          * */
     28         int middle=(min+max)/2;
     29         ConstructIT(min,middle,InterTree,index*2);
     30         ConstructIT(middle+1,max,InterTree,index*2+1);
     31 }
     32 /**
     33  * 此函数用于注入线段树的线段标记信息
     34  * */
     35 void InsertInfor(int min, int max, Interval** InterTree, int index) {
     36         int middle=(InterTree[index]->left + InterTree[index]->right)/2;
     37         if(max<middle) {
     38                 InsertInfor(min, max, InterTree, 2*index);
     39         } else if(min>middle) {
     40                 InsertInfor(min, max, InterTree, 2*index+1);
     41         } else if(min==InterTree[index]->left &&
     42                         max==InterTree[index]->right) {
     43                 InterTree[index]->count+=1;
     44                 return;
     45         } else {
     46                 InsertInfor(min, middle, InterTree, 2*index);
     47                 InsertInfor(middle+1, max, InterTree, 2*index+1);
     48         }
     49 }
     50 /**
     51  * 此函数用于检查覆盖区间,注意此时的min和max可能超出
     52  * InterTree的范围
     53  * */
     54 int CheckInterval(int min, int max, Interval** InterTree, int index) {
     55         int middle=(InterTree[index]->left + InterTree[index]->right)/2;
     56         if(max<middle) {
     57                 return CheckInterval(min, max, InterTree, 2*index);
     58         } else if(min>middle) {
     59                 return CheckInterval(min, max, InterTree, 2*index+1);
     60         } else if(min==InterTree[index]->left &&
     61                         max==InterTree[index]->right) {
     62                 if(InterTree[index]->count>0)
     63                         return InterTree[index]->count;
     64         } else {
     65                 CheckInterval(min, middle, InterTree, 2*index);
     66                 CheckInterval(middle+1, max, InterTree, 2*index+1);
     67         }
     68 }
     69 int GetOverlapSize(int *first, int lfirst, int *second, int lsecond) {
     70         /**
     71          * 获取first和second数组中各自的最大值与最小值的差值
     72          * 选取差值较小的一个范围作为线段树的构建区间
     73          * 节省空间消耗
     74          * */
     75         int minf=0, maxf=0,mins=0,maxs=0;
     76         for(int i=1;i<lfirst;i+=2) {
     77                 if(maxf<first[i])
     78                         maxf=first[i];
     79                 if(minf>first[i])
     80                         minf=first[i];
     81         }
     82         for(int i=1;i<lsecond;i+=2) {
     83                 if(maxs<second[i])
     84                         maxs=second[i];
     85                 if(mins>second[i])
     86                         mins=second[i];
     87         }
     88         int min=minf,max=maxf;
     89         int isFirst=true;
     90         if((max-min)>(maxs-mins)) {
     91                 min=mins;
     92                 max=maxs;
     93                 isFirst=false;
     94         }
     95         /**
     96          * 构建一个数组,数组元素为Interval*类型,
     97          * 数组大小为线段树节点个数,由于线段树是
     98          * 完全二叉树,所以节点数肯定为2N-1,N为区间
     99          * 大小,也为叶节点数
    100          * */
    101         Interval *InterTree[2*(max-min+1)-1];
    102         /**
    103          * 首先构建线段树的基本结构
    104          * */
    105         ConstructIT(min, max, InterTree, 0);
    106         /**
    107          * 然后将线段树范围大小对应的区间数组元素
    108          * 插入到线段树中,如果区间在线段树之外则
    109          * 需要特殊处理
    110          * */
    111         int *temp=NULL, length=0;
    112         int overlapSize=0;
    113         if(isFirst) {
    114                 for(int i=1;i<lfirst;i+=2) {
    115                         if(first[i]<InterTree[0]->left ||
    116                                         first[i-1]>InterTree[0]->right)
    117                                 continue;
    118                         else if(first[i]==InterTree[0]->left ||
    119                                         first[i-1]==InterTree[0]->right)
    120                                 overlapSize++;
    121                         else
    122                                 InsertInfor(first[i-1],first[i], InterTree, 0);
    123                 }
    124                 temp=second;
    125                 length=lsecond;
    126         } else {
    127                 for(int i=1;i<lsecond;i+=2) {
    128                         if(second[i]<InterTree[0]->left ||
    129                                         second[i-1]>InterTree[0]->right)
    130                                 continue;
    131                         else if(second[i]==InterTree[0]->left ||
    132                                         second[i-1]==InterTree[0]->right)
    133                                 overlapSize++;
    134                         else
    135                                 InsertInfor(second[i-1],second[i], InterTree, 0);
    136                 }
    137                 temp=first;
    138                 length=lfirst;
    139         }
    140         /**
    141          * 顺序将temp中的区间元素插入到InterTree中
    142          * */
    143 
    144         for(int i=1;i<length;i+=2) {
    145                 length+=CheckInterval(temp[i-1],temp[i],InterTree,0);
    146         }
    147 
    148 }
  • 相关阅读:
    阿里云服务器ECS centos_7 安装jdk环境
    Spring Data Jpa 投影(Projection)的用法
    win10 docker 部署微服务
    centos 安装docker
    VMware 启动 虚拟机后一直黑屏
    VMware 新建虚拟机选择操作系统的时候提示,此主机不支持64位客户机操作系统,此系统无法运行
    mysql 表名全变小写的解决(windows)
    docker: Error response from daemon: driver failed programming external connectivity on endpoint jovial_morse (71d0315110605c3812f7255c7072f5d38512118a4635feaf84cdc170d3bee8d1): Error starting userland
    win10 docker部署springboot项目
    nginx failed (1113: No mapping for the Unicode character exists in the target multi-byte code page)
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/3749291.html
Copyright © 2011-2022 走看看