一道百度面试题

xiaozhuhaoa 2011-02-28 10:34:43
1 完成函数
size_t foo(unsigned int *a1, size_t al1, unsigned int* a2, size_t al2)
其中a1和a2都为无符号数组,al1和al2为数组的长度,数组的长度为偶数。
无符号数组由一对数字区间组成。如下例:
a1 为 0,1,3,6,10,20
a2 为 0,1,20,50,4,5
则 a1表示以下区间[0,1] [3,6] [10,20]
a2表示以下区间[0,1] [20,50] [4,5]
则a1,a2的重叠部分为[0,1] [4,5],其长度为2
函数foo要求返回重叠区间的长度。上例中为2.

要求:
详细说明自己的解题思路,说明自己实现的一些关键点。
写出函数foo原代码,另外效率尽量高,并给出代码的复杂性分析。
限制:
al1和al2的长度不超过100万。而且同一个数组的区间可能出现重重叠。
如a1可能为 0,5, 4,8, 9,100, 70,80
使用的存储空间尽量小。




我只能想到一种最蠢的办法,全程打点式的,大家发挥一下聪明才智吧。
...全文
827 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhusizhi007 2011-05-03
  • 打赏
  • 举报
回复
刚打错了,[1,5]应该是将第1位至第5位置为1
zhusizhi007 2011-05-03
  • 打赏
  • 举报
回复
我看其实,每个区段都可以看成一些连续的值,如[1,5],可以看做为:1,2,3,4,5,这样看的话,如果另有:[3,7],那么,也可以看成是:3,4,5,6,7,以这个想法,所以可以用二分法,将每两个区间排序,并合并(可以用quicksort,unique等STL),
更有效率的方法应该是用位图(可以参考<<编程珠玑>>),[1,5]不过是将第一位和第5位置为1,最后,将两个块的位图再&一次,得到共同置位的部分,再计算1的个数(这个可以参考<<高效程序的奥秘>>一书)
yagemoxin 2011-03-08
  • 打赏
  • 举报
回复
好佩服能写出那两个程序的大哥。顶
momomamo 2011-03-08
  • 打赏
  • 举报
回复
1、合并a1重叠区间(不排序),复杂度O(al1)
2、合并a2重叠区间(不排序),复杂度O(al2)
3、设al3=max(al1,al2),a3为a1和a2重叠区间,复杂度O(al1)+O(al2)
4、Count(a3),O(al3)
算法复杂度O(al1)+O(al2)
ai_at_china 2011-03-08
  • 打赏
  • 举报
回复
求AB交集:
if((A1 - B1) * (A2 - B2) <= 0)//AB有交集
{
交集 = [max(A1, B1), min(A2,B2)];
}
这样可以吗,没细考虑。
fallening 2011-03-08
  • 打赏
  • 举报
回复
bitmap 也成
xiaoboalex 2011-03-08
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 cknight2008 的回复:]

13楼在排序的时候取名为quick_sort,但实际上递归和迭代都用上了。众所周知,递归的效率是最值得商议的,在这里效率就打了个很大的折扣。13楼有没有考虑到这个情况?
[/Quote]
取名为quick_sort并不是说我认为它很“快”,而是因为这个算法是从快速排序算法修改而来的,以便支持按照下界排序且同时移动上界。
快速排序算法的平均复杂度为O(nlgn),对于百万级的数据,递归的深度大约在20次左右,应该不是太大的问题。
当然,如果考虑到最坏的情况,递归的深度会很大,这是可以修改算法,采用随机值为“轴”,以达到平均nlgn的复杂度。
fallening 2011-03-08
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 ouyh12345 的回复:]

先对a1和a2排序,并归并重叠的区间
然后再求重叠部分
[/Quote]
只排序一个就够了,另外一个直接二分查找计数
xiaoboalex 2011-03-08
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 cknight2008 的回复:]

如果是考算法的话,不必说明数据量有百万那么多。说明数据量大,就暗示效率优先。map是动态管理内存,保证只在需要的时候才分配必要的空间。我是没有信心能写出个算法比标准库的算法更有效率。我相信站在巨人的肩膀上,方可以看得更远。
[/Quote]
map的确是具有工业级别的强度,这点我没有异议。关键是在于map需要消耗额外的内存,因为至少需要将这些区间的节点构造出来吧。
如果说不考虑内存,那么map也不合适,因为在不考虑内存的情况下map的排序效率也赶不上非比较排序算法(基数排序,桶排序等)。
CKnight2008 2011-03-08
  • 打赏
  • 举报
回复
13楼在排序的时候取名为quick_sort,但实际上递归和迭代都用上了。众所周知,递归的效率是最值得商议的,在这里效率就打了个很大的折扣。13楼有没有考虑到这个情况?
CKnight2008 2011-03-08
  • 打赏
  • 举报
回复
如果是考算法的话,不必说明数据量有百万那么多。说明数据量大,就暗示效率优先。map是动态管理内存,保证只在需要的时候才分配必要的空间。我是没有信心能写出个算法比标准库的算法更有效率。我相信站在巨人的肩膀上,方可以看得更远。
BigQiu66 2011-03-04
  • 打赏
  • 举报
回复
都是牛逼人物
xiaoboalex 2011-03-03
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 cknight2008 的回复:]

再加上Map重载的< 操作符 以及标准算法的for_each,应该能搞定
[/Quote]
STL的Map是用红黑树实现的,排序的效率也不过是O(nlgn), for_each也只是普通的变量,效率也只是O(n),我并没有看出它们有多大的优势。

而题目要求所需的存储空间尽量的少,用了Map之后肯定涉及到节点的创建,额外的内存是少不了的;况且是否允许使用STL还是未知之数,有可能就是想考察排序算法呢。

就算允许用Map,那又用什么做Key呢?上界?下界?还是区间本身?Value又是什么呢?

其实用Set可能还更合理一些(以下界为序)。

如果不考虑内存,完全可以用非比较排序算法,复杂度O(n),再加上两个求两个序列并集的过程,复杂度也是O(n),肯定比Map或Set有效率。
direction917 2011-03-03
  • 打赏
  • 举报
回复
同意用二叉排序树。排序速度快,空间占用也不大。在合并区间的时候顺便对树进行剪枝,会加速后边的进度。程序就不写了。重要的是思想。
CKnight2008 2011-03-03
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 xiaoboalex 的回复:]
引用 16 楼 qq44835 的回复:

引用 8 楼 xiaoboalex 的回复:
1.对两个序列分别按偶数索引(0,2,4 ...,即区间的下界)排序,可以用原地排序算法(快速,插入等)以节省空间。在交换排序的时候注意连同奇数索引一起进行交换。例如 [9,10] [5,7] [4,8]排序后变成 4,8,5,7,9,10

2.分别合并两个序列的重叠区间:
4 5 9
/ /……
[/Quote]
你这个方法是最原始的方法,对于数据量大的时候,根本没效率可言,不过很佩服你居然有耐心把代码写出来,但是肯定不合格。
xiaoboalex 2011-03-02
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 qq44835 的回复:]

引用 8 楼 xiaoboalex 的回复:
1.对两个序列分别按偶数索引(0,2,4 ...,即区间的下界)排序,可以用原地排序算法(快速,插入等)以节省空间。在交换排序的时候注意连同奇数索引一起进行交换。例如 [9,10] [5,7] [4,8]排序后变成 4,8,5,7,9,10

2.分别合并两个序列的重叠区间:
4 5 9
/ /
8 7 10
比较前两个区间a=[4,8……
[/Quote]
没看清题目,写错了,计算并集的二分查找并不正确。
请参考后面的代码,里面已经改过来了。
CKnight2008 2011-03-02
  • 打赏
  • 举报
回复
再加上Map重载的< 操作符 以及标准算法的for_each,应该能搞定
CKnight2008 2011-03-02
  • 打赏
  • 举报
回复
用标准库的Map,插入后直接对于比较,因为Map在插入后就已经排序,所以直接用对应方式比较,比如对一个对第一个,第二个对第二个,一次性搞定
xiaoboalex 2011-03-01
  • 打赏
  • 举报
回复
#include <iostream>
using namespace std;

// 交换区间(同时交换上界与下界)
void swap(unsigned int*arr, size_t i, size_t j)
{
unsigned int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;

tmp = arr[i+1];
arr[i+1] = arr[j+1];
arr[j+1] = tmp;
}

// 按偶数索引进行快速排序
void quick_sort(unsigned int* arr, size_t s, size_t e)
{
if (s>=e) return;
unsigned int key = arr[s];
size_t i = s;
size_t j = e-1;
while (i < j)
{
while (i < j && arr[j]>=key) j-=2;
swap(arr, i, j);

while (i < j && arr[i]<=key) i+=2;
swap(arr, i, j);
}
// 因为用的是size_t(unsigned int),所以需要额外的检查,避免溢出
if (i != s)
quick_sort(arr, s, i-1);
quick_sort(arr, i+2, e);
}

// 依次合并同一个序列中的两个相邻区间(例如A和B需要合并,则A置为空[0,0],B置为A+B)
void merge(unsigned int* arr, size_t s, size_t e)
{
if (s >= e-1) return;
size_t i = s;
size_t j = s+2;
while (j+1 <= e)
{
if (arr[i+1]>=arr[j])
{
// 置为i+j
arr[j] = arr[i];
arr[j+1] = (arr[i+1]>arr[j+1])?arr[i+1]:arr[j+1];
// 置空[0,0]
arr[i] = arr[i+1] = 0;
}
i += 2;
j += 2;
}
}

// 因为存在空区间[0,0],所以在移动指针时需要跳过,移动到下一个有效区间或者序列的尾部
void move_next(unsigned int* arr, size_t s, size_t e, size_t& i)
{
if (i < s) return;
i += 2;
while (i <= e-1 && 0 == arr[i] && 0 == arr[i+1]) i += 2;
}

size_t foo(unsigned int *a1, size_t al1, unsigned int* a2, size_t al2)
{

// 排序与合并
quick_sort(a1, 0, al1-1);
quick_sort(a2, 0, al2-1);
merge(a1, 0, al1-1);
merge(a2, 0, al2-1);

// 统计并集
size_t total = 0;

// i指向序列一,j指向序列二
size_t i = 0, j = 0;

// e1为序列一的末端,e2为序列二的末端
size_t e1 = al1-1, e2 = al2-1;

// 依次比较序列一和序列二中的对应区间,一共有六种情况出现
while (i+1 <= e1 && j+1 <= e2)
{
// 1. |i~~~~i+1| |j~~~~j+1|, 移动i至下一组
if (a1[i+1] < a2[j]) move_next(a1, 0, e1, i);

// 2. |j~~~~j+1| |i~~~~i+1|, 移动j至下一组
if (a2[j+1] < a1[i]) move_next(a2, 0, e2, j);

// 3. |j~~~|i~~~i+1|~~~j+1|, 累加total(arr1[i+1]-arr1[i]),移动i至下一组
if (a2[j] <= a1[i] && a1[i+1] <= a2[j+1])
{
total += (a1[i+1]-a1[i]);
move_next(a1, 0, e1, i);
}

// 4. |i~~~|j~~~j+1|~~~i+1|, 累加total(arr2[j+1]-arr2[j]),移动j至下一组
if (a1[i] <= a2[j] && a2[j+1] <= a1[i+1])
{
total += (a2[j+1]-a2[j]);
move_next(a2, 0, e2, j);
}

// 5. |i~~~|j~~~i+1|~~~j+1|, 累加total(arr1[i+1]-arr2[j]),移动i至下一组
if (a1[i] <= a2[j] && a2[j] <= a1[i+1] && a1[i+1] <= a2[j+1])
{
total += (a1[i+1]-a2[j]);
move_next(a1, 0, e1, i);
}

// 6. |j~~~|i~~~j+1|~~~i+1|, 累加total(arr2[j+1]-arr1[i]),移动j至下一组
if (a2[j] <= a1[i] && a1[i] <= a2[j+1] && a2[j+1] <= a1[i+1])
{
total += (a2[j+1]-a1[i]);
move_next(a2, 0, e2, j);
}
}
return total;
}

int main()
{
unsigned int arr1[] = {0,1,3,6,10,20};
unsigned int arr2[] = {0,1,20,50,4,5};
cout<<"Result = "<<foo(arr1, sizeof(arr1)/sizeof(int), arr2, sizeof(arr2)/sizeof(int))<<endl;
system("pause");
return 0;
};
ray_corpse 2011-03-01
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 xiaoboalex 的回复:]
1.对两个序列分别按偶数索引(0,2,4 ...,即区间的下界)排序,可以用原地排序算法(快速,插入等)以节省空间。在交换排序的时候注意连同奇数索引一起进行交换。例如 [9,10] [5,7] [4,8]排序后变成 4,8,5,7,9,10

2.分别合并两个序列的重叠区间:
4 5 9
/ /
8 7 10
比较前两个区间a=[4,8]和b=[5,7],比较a2(8)和b1(5)……
[/Quote]

序列1:[2,5]
序列2:[3,6]
这种情况呢?
加载更多回复(10)

65,202

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧