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

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

解题:

 struct Node {
int value;
Node *left;
Node *right;
}; Node* Array2BST(int *array, int i, int j) {
/**
* 如果上下范围相同说明只有一个节点
* 直接返回
* */
if(i==j) {
Node *root=new Node();
root->value=array[i];
root->left=NULL;
root->right=NULL;
return root;
} int m=(i+j)/;
Node *root=new Node();
root->value=array[m];
/**
* 由于(i+j)/2是向下取整,所以i+1=j时
* m=i,这个时候root的左子树为空
* */
if(m==i)
root->left=NULL;
else
root->left=Array2BST(array, i, m-);
root->right=Array2BST(array, m+, j); return root;
}

出题:要求实现函数:
  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);

解题:

 struct Interval {
int left;
int right;
int count;
};
/**
* 此函数用于创建线段树的基本结构
* */
void ConstructIT(int min, int max, Interval** InterTree, int index) {
/**
* 构建当前节点
* index用于索引InterTree中当前元素的位置
* 遵循父节点为i,则做有子节点分别为2*i, 2*i+1
* */
Interval *temp=new Interval();
temp->count=;
temp->left=min;
temp->right=max;
InterTree[index]=temp;
/**
* 如果min和max相等,则说明已经到叶子节点
* */
if(min==max)
return;
/**
* 分别递归创建左右子节点
* */
int middle=(min+max)/;
ConstructIT(min,middle,InterTree,index*);
ConstructIT(middle+,max,InterTree,index*+);
}
/**
* 此函数用于注入线段树的线段标记信息
* */
void InsertInfor(int min, int max, Interval** InterTree, int index) {
int middle=(InterTree[index]->left + InterTree[index]->right)/;
if(max<middle) {
InsertInfor(min, max, InterTree, *index);
} else if(min>middle) {
InsertInfor(min, max, InterTree, *index+);
} else if(min==InterTree[index]->left &&
max==InterTree[index]->right) {
InterTree[index]->count+=;
return;
} else {
InsertInfor(min, middle, InterTree, *index);
InsertInfor(middle+, max, InterTree, *index+);
}
}
/**
* 此函数用于检查覆盖区间,注意此时的min和max可能超出
* InterTree的范围
* */
int CheckInterval(int min, int max, Interval** InterTree, int index) {
int middle=(InterTree[index]->left + InterTree[index]->right)/;
if(max<middle) {
return CheckInterval(min, max, InterTree, *index);
} else if(min>middle) {
return CheckInterval(min, max, InterTree, *index+);
} else if(min==InterTree[index]->left &&
max==InterTree[index]->right) {
if(InterTree[index]->count>)
return InterTree[index]->count;
} else {
CheckInterval(min, middle, InterTree, *index);
CheckInterval(middle+, max, InterTree, *index+);
}
}
int GetOverlapSize(int *first, int lfirst, int *second, int lsecond) {
/**
* 获取first和second数组中各自的最大值与最小值的差值
* 选取差值较小的一个范围作为线段树的构建区间
* 节省空间消耗
* */
int minf=, maxf=,mins=,maxs=;
for(int i=;i<lfirst;i+=) {
if(maxf<first[i])
maxf=first[i];
if(minf>first[i])
minf=first[i];
}
for(int i=;i<lsecond;i+=) {
if(maxs<second[i])
maxs=second[i];
if(mins>second[i])
mins=second[i];
}
int min=minf,max=maxf;
int isFirst=true;
if((max-min)>(maxs-mins)) {
min=mins;
max=maxs;
isFirst=false;
}
/**
* 构建一个数组,数组元素为Interval*类型,
* 数组大小为线段树节点个数,由于线段树是
* 完全二叉树,所以节点数肯定为2N-1,N为区间
* 大小,也为叶节点数
* */
Interval *InterTree[*(max-min+)-];
/**
* 首先构建线段树的基本结构
* */
ConstructIT(min, max, InterTree, );
/**
* 然后将线段树范围大小对应的区间数组元素
* 插入到线段树中,如果区间在线段树之外则
* 需要特殊处理
* */
int *temp=NULL, length=;
int overlapSize=;
if(isFirst) {
for(int i=;i<lfirst;i+=) {
if(first[i]<InterTree[]->left ||
first[i-]>InterTree[]->right)
continue;
else if(first[i]==InterTree[]->left ||
first[i-]==InterTree[]->right)
overlapSize++;
else
InsertInfor(first[i-],first[i], InterTree, );
}
temp=second;
length=lsecond;
} else {
for(int i=;i<lsecond;i+=) {
if(second[i]<InterTree[]->left ||
second[i-]>InterTree[]->right)
continue;
else if(second[i]==InterTree[]->left ||
second[i-]==InterTree[]->right)
overlapSize++;
else
InsertInfor(second[i-],second[i], InterTree, );
}
temp=first;
length=lfirst;
}
/**
* 顺序将temp中的区间元素插入到InterTree中
* */ for(int i=;i<length;i+=) {
length+=CheckInterval(temp[i-],temp[i],InterTree,);
} }

笔试算法题(31):将有序数组转换成BST表示 & 线段树的应用的更多相关文章

  1. LeetCode 108. Convert Sorted Array to Binary Search Tree (将有序数组转换成BST)

    108. Convert Sorted Array to Binary Search Tree Given an array where elements are sorted in ascendin ...

  2. 笔试算法题(05):转换BST为双向链表 & 查找栈中的最小元素

    出题:把二元查找树转变成排序的双向链表.输入一棵二元查找树,要求将该二元查找树按照中序转换成一个排序的双向链表,要求不能创建任何新的节点,只能调整指针的指向: 分析: 递归的思路,当前节点需要进行的处 ...

  3. 笔试算法题(26):顺时针打印矩阵 & 求数组中数对差的最大值

    出题: 输入一个数字矩阵,要求从外向里顺时针打印每一个数字: 分析: 从外向里打印矩阵有多重方法实现,但最重要的是构建合适的状态机,这样才能控制多重不同的操作: 注意有四种打印模式(左右,上下,右左, ...

  4. [LeetCode每日一题]80. 删除有序数组中的重复项 II

    [LeetCode每日一题]80. 删除有序数组中的重复项 II 问题 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度. 不要使用额外 ...

  5. js将一个具有相同键值对的一维数组转换成二维数组

    这两天,一个前端朋友在面试的笔试过程中遇到了一道类似于"用js实现将一个具有相同code值的一维数组转换成相同code值在一起的二维数组"的题目.他面试过后,把这个问题抛给了我,问 ...

  6. LeetCode OJ:Convert Sorted Array to Binary Search Tree(将排序好的数组转换成二叉搜索树)

    Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 讲一 ...

  7. C#字节数组转换成字符串

    C#字节数组转换成字符串 如果还想从 System.String 类中找到方法进行字符串和字节数组之间的转换,恐怕你会失望了.为了进行这样的转换,我们不得不借助另一个类:System.Text.Enc ...

  8. 100怎么变成100.00 || undefined在数字环境下是:NaN || null在数字环境下是0 || 数组的toString()方法把每个元素变成字符串,拼在一起以逗号隔开 || 空数组转换成字符串后是什么?

    100怎么变成100.00?

  9. C/C++中数组转换成指针的情况

    数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针.比如: int ia[10]; int *p = ia; //ia转换成指向数组首元素的指针 以下情况上述转换不会发生: ...

随机推荐

  1. HDU 3944 DP? (Lucas定理)

    题意:在杨辉三角中让你从最上面到 第 n 行,第 m 列所经过的元素之和最小,只能斜向下或者直向下走. 析:很容易知道,如果 m 在n的左半部分,那么就先从 (n, m)向左,再直着向上,如果是在右半 ...

  2. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(二)数据库初始化、基本登录页面以及授权逻辑的建立

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  3. php pdo操作数据库

    POD扩展是在PHP5中加入,该扩展提供PHP内置类 PDO来对数据库进行访问,不同数据库使用相同的方法名,解决数据库连接不统一的问题. PDO的特点: 性能.PDO 从一开始就吸取了现有数据库扩展成 ...

  4. CentOS 7安装并设置启动图形桌面

    服务器端有时没有安装图形桌面,采用下面的步骤安装gnome桌面,并能够启动后进入图形桌面 1.安装图形环境 #yum grouplist#yum groupinstall 'GNOME Desktop ...

  5. JAVA 添加、修改和删除PDF书签

    当阅读篇幅较长的PDF文档时,为方便我们再次阅读时快速定位到上一次的阅读位置,可以插入一个书签进行标记:此外,对于文档中已有的书签,我们也可以根据需要进行修改或者删除等操作.本篇文章将通过Java编程 ...

  6. pip 的具体含义

    Python 2.7.9 and later (on the python2 series), and Python 3.4 and later include pip (pip3 for Pytho ...

  7. nginx静态资源服务器简单配置

    有时候我们可以把服务器的一些文件放在固定目录以便下载,比如image,css,js等.就可以使用nginx转发静态资源. 参考链接:https://blog.csdn.net/name_is_wl/a ...

  8. ACM经历与感悟合集

    ACM经历与感悟合集 ACM起步要点总结(转哈工大) ACM 荣耀之路-学习方法 ACM感悟 一位ACMer过来人的心得 ACM经历总结 大学ACM的总结 ACM大牛的退役贴 各大牛退役总结帖 女生应 ...

  9. google breakpad 使用初步总结

    项目地址:https://code.google.com/p/google-breakpad/    访问不了请挂VPN 这是一个由google主导的开源项目,官方介绍为:An open-source ...

  10. 设计模式 -- Abstract Factory 抽象工厂

    1.常规的对象创建方法 //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对“具体实例化类型”额变化. 解决思想: 封装变化点--哪里变化,封装哪里( ...