出题:要求将一个有序整数数组转换成最小深度的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 }