算法导论14.1节习题解答
2012-05-08 18:20
197 查看
算法导论习题解答系列停了一年了,现在重新拾起,好多算法已经忘了,有的记得大概,但是真正的用代码实现却很难下手。
CLRS 14.1-3 写出OS-SELECT的非递归形式
一般递归形式改写为非递归形式要用到while,有时还要用到栈结构。
[b]CLRS 14.1-4 写出一个递归过程OS-KEY-RANK(T, k)[/b]
[b]CLRS 14.1-5 确定元素x的第i个后继,时间为lg(n)[/b]
[b]CLRS 14.1-6[/b]
在这题中,将每个结点的秩存于该结点自身之中,这个秩是相对于以该结点为根的子树而言的。
因而在插入结点x时,对于从root到x结点的路经上的所有结点y,如果插入路经经过y的左支,则rank[y]的值加1,若经过其右支,则rank[y]的值不变。
在删除结点x时,对于从root到x结点的路经上的所有结点y,如果删除路经经过y的左支,则rank[y]的值减1,若经过其右支,则rank[y]的值不变。
如图,在进行右旋转时,x的秩是不变的,node的秩变为rank[node]减去原来的rank[x]。左旋同理。
[b]CLRS 14.1-7 利用顺序统计树在O(nlgn)的时间内统计逆序对[/b]
在习题2-4中,其要求用归并排序来计算逆序对,见算法导论2-4习题解答(合并排序算法)
在这里,我们对于数组{2,3,8,6,1}这样分析,对于每个数,选取其前面的数与其比较,
对于6,与其对比的为2,3,8,逆序对有1对,记作inversion_count,
6在原数组的索引为3,记作j,
然后我们来分析子数组{2,3,8,6},6在其中的排名为3,记作rank_j;
再通过分析其他数,我们归纳如下:
inversion_count = j + 1 - rank_j
当把每个结点插入顺序统计树时,我们可以知道j的值,同时调用OS-RANK来得到rank_j,从而得到inversion_count,在这里,每插入一次,就计算一次。
由于插入和OS-RANK都是lg(n),故n个结点即为n*lg(n)。
[b]CLRS 14.1-8 现有一个圆上的n条弦,每条弦都按其端点来定义,请给出一个能在O(n*lgn)时间内确定圆内相交弦的对数的算法,假设任意两条弦都不会共享端点。[/b]
参考自http://topic.csdn.net/u/20081119/17/397cc718-f4dc-4570-9991-aee57dd73416.html
如图,对于两条弦P1Q1和P3Q3来说,圆心与端点形成的向量有一个角度A
如果A(P1)<A(P3)<A(Q1)<A(Q3)或者A(P3)<A(P1)<A(Q3)<A(Q1),这样角度区间“交叉”就意味着两条弦有交叉。
由于有n条弦,故有2n个端点,每个端点的取值范围为[0, 2*π),对这2n个端点按角度值进行从小到大排序,排序的时间复杂度为O(n*lgn),所得数组为A[1...2n]
然后建立一个顺序统计树,起先为空,先插入A[1],它为一条弦的起始端点,然后遇到其他弦的起始端点就插入,当遇到一条弦的终端点时,就统计在该树中大于该弦的起始端点角度值的端点个数,之后就从树中删除该弦的两个端点。
如图,先插入P1,再插入P2,再插入P3,再插入P4,再插入Q4,Q4为弦P4Q4的终端店,故停止插入,开始统计树中大于P4角度值的端点个数,为0,然后删除P4与Q4。
再插入Q2,为终端点,统计此时树中大于P2端点值的个数,为1个,然后删除P2与Q2。
再插入Q1,为终端点,统计此时树中大于P1端点值的个数,为1个,然后删除P1与Q1。
再插入Q3,为终端点,统计此时树中大于P3端点值的个数,为0个,然后删除P3与Q3。
结束,统计结果一共为2,与图中相符。
在这里,要用到顺序统计树的插入、删除操作,以及OS-RANK函数。每个操作都为O(lgn)的时间复杂度,有2n个结点,故时间复杂度仍是O(n*lgn)。
CLRS 14.1-3 写出OS-SELECT的非递归形式
一般递归形式改写为非递归形式要用到while,有时还要用到栈结构。
OS-SELECT(x, i) { r = size[left[x]] + 1; while (r != i) { if (i < r) { x = left[x]; r = size[left[x]] + 1; } else { x = right[x]; i = i -r; r = size[left[x]] + 1; } } return x; }
[b]CLRS 14.1-4 写出一个递归过程OS-KEY-RANK(T, k)[/b]
int OS-KEY-RANK(T, k) { if (k == key[root[T]]) return size[left[root[T]]] + 1; else if (k < key[root[T]]) return OS-KEY-RANK(left[root[T]], k); else return OS-KEY-RANK(right[root[T]], k) + size[left[root[T]]] + 1; }
[b]CLRS 14.1-5 确定元素x的第i个后继,时间为lg(n)[/b]
GET-SUCCESSOR(T, x, i) { r = OS-RANK(T, x); return OS-SELECT(root[T], r + i); }
[b]CLRS 14.1-6[/b]
在这题中,将每个结点的秩存于该结点自身之中,这个秩是相对于以该结点为根的子树而言的。
因而在插入结点x时,对于从root到x结点的路经上的所有结点y,如果插入路经经过y的左支,则rank[y]的值加1,若经过其右支,则rank[y]的值不变。
在删除结点x时,对于从root到x结点的路经上的所有结点y,如果删除路经经过y的左支,则rank[y]的值减1,若经过其右支,则rank[y]的值不变。
如图,在进行右旋转时,x的秩是不变的,node的秩变为rank[node]减去原来的rank[x]。左旋同理。
[b]CLRS 14.1-7 利用顺序统计树在O(nlgn)的时间内统计逆序对[/b]
在习题2-4中,其要求用归并排序来计算逆序对,见算法导论2-4习题解答(合并排序算法)
在这里,我们对于数组{2,3,8,6,1}这样分析,对于每个数,选取其前面的数与其比较,
对于6,与其对比的为2,3,8,逆序对有1对,记作inversion_count,
6在原数组的索引为3,记作j,
然后我们来分析子数组{2,3,8,6},6在其中的排名为3,记作rank_j;
再通过分析其他数,我们归纳如下:
inversion_count = j + 1 - rank_j
当把每个结点插入顺序统计树时,我们可以知道j的值,同时调用OS-RANK来得到rank_j,从而得到inversion_count,在这里,每插入一次,就计算一次。
由于插入和OS-RANK都是lg(n),故n个结点即为n*lg(n)。
[b]CLRS 14.1-8 现有一个圆上的n条弦,每条弦都按其端点来定义,请给出一个能在O(n*lgn)时间内确定圆内相交弦的对数的算法,假设任意两条弦都不会共享端点。[/b]
参考自http://topic.csdn.net/u/20081119/17/397cc718-f4dc-4570-9991-aee57dd73416.html
如图,对于两条弦P1Q1和P3Q3来说,圆心与端点形成的向量有一个角度A
如果A(P1)<A(P3)<A(Q1)<A(Q3)或者A(P3)<A(P1)<A(Q3)<A(Q1),这样角度区间“交叉”就意味着两条弦有交叉。
由于有n条弦,故有2n个端点,每个端点的取值范围为[0, 2*π),对这2n个端点按角度值进行从小到大排序,排序的时间复杂度为O(n*lgn),所得数组为A[1...2n]
然后建立一个顺序统计树,起先为空,先插入A[1],它为一条弦的起始端点,然后遇到其他弦的起始端点就插入,当遇到一条弦的终端点时,就统计在该树中大于该弦的起始端点角度值的端点个数,之后就从树中删除该弦的两个端点。
如图,先插入P1,再插入P2,再插入P3,再插入P4,再插入Q4,Q4为弦P4Q4的终端店,故停止插入,开始统计树中大于P4角度值的端点个数,为0,然后删除P4与Q4。
再插入Q2,为终端点,统计此时树中大于P2端点值的个数,为1个,然后删除P2与Q2。
再插入Q1,为终端点,统计此时树中大于P1端点值的个数,为1个,然后删除P1与Q1。
再插入Q3,为终端点,统计此时树中大于P3端点值的个数,为0个,然后删除P3与Q3。
结束,统计结果一共为2,与图中相符。
在这里,要用到顺序统计树的插入、删除操作,以及OS-RANK函数。每个操作都为O(lgn)的时间复杂度,有2n个结点,故时间复杂度仍是O(n*lgn)。
相关文章推荐
- 算法导论6.1-7习题解答
- Introduction to Algorithms 算法导论 第1章 基础知识 学习笔记及习题解答
- 算法导论8.3-4习题解答(基数排序)
- 算法导论10.1-6习题解答(用两个栈实现一个队列)
- 算法导论6.2-5习题解答(堆排序)
- 算法导论10.1-7习题解答(用两个队列实现一个栈)
- 算法导论6.3-3习题解答
- 算法导论7.1-4习题解答(快速排序)
- Introduction to Algorithms 算法导论 第4章 递归式 学习笔记及习题解答
- Introduction to Algorithms 算法导论 第2章 算法入门 学习笔记及习题解答
- 【通告】算法导论习题解答
- 算法导论10.1-6习题解答(用两个栈实现一个队列)
- Introduction to Algorithms 算法导论 第3章 函数的增长 学习笔记及习题解答
- 算法导论12.1-3习题解答(非递归中序遍历)
- 算法导论2-4习题解答(合并排序算法)
- 算法导论9.2-3习题解答(寻找第i小的数)
- 算法导论8-5习题解答(平均排序)
- 算法导论6.1-2习题解答
- 算法导论8.2-4习题解答(计数排序)
- 算法导论习题5.1-3