您的位置:首页 > 职场人生

百度面试题——首尾相连的珠子问题

2012-09-09 19:05 218 查看
问题描述:

一串首尾相连的珠子(n 个),有M 种颜色(M<=10),

设计一个算法,取出其中一段,要求包含所有N 中颜色,并使长度最短。

并分析时间复杂度与空间复杂度。

解题思路:

该问题看起来比较复杂,其实思想非常简单。我们可将珠子先断环,计算在断环的情况下,包含M钟颜色的最小珠子数,然后再处理断环处的情况。

为了简洁明了,这里举一个例子来进行阐述:

假设有这么一串珠子,其珠子数量为n = 13个,共包含m = 4种颜色(分别为1, 2, 3, 4),且如下分布 :

pearls[] = {2, 1, 3, 2, 3, 4, 2, 2, 1, 1, 4, 3, 1};

那么,我们怎么找出这串已断环(先暂时不考虑环)的珠子包含所有颜色的最少珠子数呢?

首先,我们先引入一些临时变量:

1、colors[m]数组:用来进行哈希映射,其中colors[j]用来表示颜色为j的珠子是否已被包含到计算中。

2、left_pearls变量,用来存储当前还未遇到的珠子颜色数,如果left_pearls为0,则表示所有颜色的珠子都已被包含。

3、next_pos
数组,用来存储对应pearls数组中对应位置的珠子下一个相同颜色的位置,该数组非常关键。

然后,我们计算next_pos[]数组中各个元素的值,对于上例,其对应的pearls[]数组为:

pearls[] = {2, 1, 3, 2, 3, 4, 2, 2, 1, 1, 4, 3, 1};

next_pos[] = {3, 8, 4, 6, 11, 10, 7, -1, 9, 12, -1, -1, -1}; //-1代表当前颜色已经到达最后一个珠子

好了,所有的计算环境已经具备了,现在就要正式开始进行计算,其计算过程大致如下:

1、将当前珠子加入候选集中,并查看colors[pears[i]]是否为0,如果为0, 则当前颜色的珠子还未进入候选集,将colors[pears[i]]置1,并更新left_pearls。

2、查看候选集中的第一个珠子,判断与当前珠子保存的下一个与该颜色相同的珠子所在的索引位置是否已包含在候选集中,如果已存在,则将该珠子移除候选集,继续比较候选集的第二个珠子,如此重复直到条件不满足(即当前颜色珠子的下一个位置超出了当前候选集的范围或为-1)。

3、如果left_pearls为0且当前的最小长度min_len(初始时设置为n) > 当前候选集的长度,则更新min_len。

4、右移指针,如果到达末尾则退出,否则进入第1步。

通过上述过程,我们将断环的最少珠子数计算出来了,对于上面得例子其结果为:{1, 3, 2, 3, 4}, 最小长度为5。

现在对处理断环处进行处理(当然,如果最小长度与颜色数量相同,则可直接跳过这步),我们将pearls数组的前min_len个与pearls数组的后min_len个数据进行合并(注意顺序),然后再次调用上述过程即可得到环点的最少珠子数。如果环点的最少珠子数更少,则返回环点的min_len,否则返回上面数组的min_len。

对于上述例子,断环处得到的结果为:{2, 1, 3, 4}, 最少珠子数为4。由于断环处的最少珠子数小于上面数组所得的最小珠子数,因此返回4。

参考代码如下:

int shortest_pearl_helper(int *array, int n, int m)
{
int min_len, left_pearls, *colors, *next_pos;
colors = new int[m + 1];
next_pos = new int
;
for (int i = 0; i != m + 1; ++i) {
colors[i] = -1;
}
for (int i = 0; i != n; ++i) {
next_pos[i] = -1;
}
//建立next_pos[]数组。
for (int i = 0; i != n; ++i) {
if (-1 != colors[array[i]]) {
next_pos[colors[array[i]]] = i;
}
colors[array[i]] = i;
}
for (int i = 0; i != m + 1; ++i) {
colors[i] = 0;
}
min_len = n;
left_pearls = m;
//计算包含所有颜色的珠子的最少珠子数
for (int i = 0, j = 0; i != n; ++i) {
if (!colors[array[i]]) {
--left_pearls;
colors[array[i]] = 1;
}
while(1) {
if (-1 == next_pos[j] || next_pos[j] > i) {
break;
}
++j;
}
if (!left_pearls && min_len > (i - j + 1) ) {
min_len = i - j + 1;
if (min_len == m) {
break;
}
}
}
delete []colors;
delete []next_pos;
return min_len;
}
int shortest_pearl(int *array, int n, int m)
{
int min_len;
//先对数组进行处理,找出数组中最少珠子数。
min_len = shortest_pearl_helper(array, n, m);
if (m != min_len) {
//对断环处进行处理,只需计算前min_len以及后min_len个元素(或者更少)组成的最少珠子数
int k, min_len1, *tarray;
tarray = new int[2 * min_len];
k = 0;
for (int i = min_len - 1; i >= 0; --i) {
tarray[k++] = array[i];
}
for (int i = n - 1; i != n - min_len + 1; --i) {
tarray[k++] = array[i];
}
min_len1 = shortest_pearl_helper(tarray, k, m);
if (min_len > min_len1) {
min_len = min_len1;
}
delete []tarray;
}
return min_len;
}
int main(void)
{

int pearls[] = {2, 1, 3, 2, 3, 4, 2, 2, 1, 1, 4, 3, 1};
cout<<"the min_len is:"<<shortest_pearl(pearls, 13, 4);
getchar();
return 0;
}


该算法的时间复杂度为O(N),空间复杂度为O(N)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: