算法导论(CLRS)习题

6.5-1 试说明 HEAP-EXTRACT-MAX 在堆 A={15,13,9,5,12,8,7,4,0,6,2,1} 上的操作过程。

HEAP-EXTRACT-MAX 的伪代码和 MAX-HEAPIFY(A,i) :通过不断交换 i 和其孩子数据,从而维持最大堆性质

HEAP-EXTRACT-MAX(A)
if(A.heap-size < 1)        //判断堆中元素是否为空
  error "堆下溢"
max = A[1]                 //将根节点元素保存起来
A[1] = A[A.heap_size]      //将堆中最后一个元素赋值给第一个元素
A.heap_size = A.heap_size - 1		//堆元素数量减少1
MAX-HEAPIFY(A,1)		//保持最大堆的性质
return max		//返回最大元素

可知,在堆A中,判空后记录下了max为15,将堆中最后一个元素 1 交换到根节点A[1],堆的大小减一,然后执行 MAX-HEAPIFY(A,1) 即:1和13交换,1和12交换,1和6交换,最后返回max:15



7.1-1 参照图7-1的方法,说明PARTITION在数组A = {13, 19, 9, 5, 12, 8, 7, 4, 21, 2, 6, 11}上的操作过程。

解如下:




7.1-4 如何修改QUICKSORT,使得它能够以非递增序进行排列?

答:修改PARTITION() 中的 A[j] <= x 为 A[j] >= x 即可。


9.3-8 设 X[ 1…n ]和Y[ 1…n ]为两个数组,每个都包含 n 个有序元素。请设计一个 O(lg n) 时间的算法来找出数组X和Y中所有2n个元素的中位数。

分治法:

1、先分别求出X和Y的中位数。当数组中的元素是偶数时,在一个数组中取上中位数,在另一个数组中取下中位数,并且在整个过程中保持不变。在哪个数组中取上中位数,就一直在那个数组中取上中位数,反之亦然。

2、比较两个数的大小,如果相等就返回。如果不等的话,去掉较大中位数所在的那个数组的较大的一半,去掉较小的中位数所在的数组的较小的一半

3、在规模减半的两个数组上递归调用。

4、如果一直没有遇到正好相等的情况,那么递归到最后会剩下四个数,则返回第二个数。因为这两个数组都是有n个元素,所以最终返回的必然是2n个数中的下中位数。

public class SearchMiddle {
	static int search(int[] X, int p, int q, int[] Y, int r, int s) {
		int midx, midy;
		if ((p + 1 == q) && (r + 1 == s)) {
			if (X[p] < Y[r])
				return (X[q] < Y[r]) ? X[q] : Y[r];
			else
				return (Y[s] < X[p]) ? Y[s] : X[p];
		} else {
			if ((q - p) % 2 == 0) { // 数组元素为奇数个
				midx = (p + q) / 2;
				midy = (r + s) / 2;
			} else { // 数组元素为偶数个
				midx = (p + q) / 2;
				midy = (r + s) / 2 + 1;
			}
			if (X[midx] < Y[midy])
				return search(X, midx, q, Y, r, midy);
			else if (X[midx] > Y[midy])
				return search(X, p, midx, Y, midy, s);
			else
				return X[midx];
		}
	}

	// 快速排序
	static int Partition(int[] A, int p, int r) {
		int x = A[r];
		int i = p - 1;
		int t;
		for (int j = p; j <= r - 1; j++)
			if (A[j] <= x) {
				i++;
				t = A[i];
				A[i] = A[j];
				A[j] = t;
			}
		t = A[i + 1];
		A[i + 1] = A[r];
		A[r] = t;
		return i + 1;
	}

	static void QuickSort(int[] A, int p, int r) {
		int q;
		if (p < r) {
			q = Partition(A, p, r);
			QuickSort(A, p, q - 1);
			QuickSort(A, q + 1, r);
		}
	}

	static void QSort(int[] A) {
		QuickSort(A, 0, A.length - 1);
	}

	public static void main(String[] args) {
		int a[] = {3,5,7,1};
		int b[] = {6,8,2,4};
		int c[] = {4,5,1,2,3,6,7,8};
		QSort(a);
		QSort(b);
		QSort(c);

		int temp = search(a, 0, 3, b, 0, 3);
		if (temp != c[3]) {
			System.out.println("error!");
		}

		System.out.println(temp);
	}
}

比如对输入X = {1, 3, 5, 7}, Y = {2, 4, 6, 8}

结果是4。




12.2-3 写出TREE-PREDECESSOR的伪代码
TREE-PREDECESSOR
 if x.left != NIL
    return TREE-MAXIMUM(x.left)
 y = x.p
 while y != NIL and y.left == x
    x = y
    y = y.p
return y

与书上的TREE-SUCCESSOR相对称.

解释:给定一棵二叉搜索树中的一个结点x,需要按中序遍历查找它的前驱

一般情况,x的前驱是小于x.key中的最大关键字结点,所以代码前两句if x.left != NIL
return TREE-MAXIMUM(x.left)
在x的左子树中找到max即为它的前驱。

在中序遍历中,如果x左子树为空,y是它的底层祖先,且x==y.left。就像这种情况,所以根据中序遍历,x的前驱为y.p 。代码的后面几行就是在处理这种情况。


12.3-6 当TREE-DELETE中的结点z有两个孩子时,可以选择它的前驱作为y,而不是作为它的后继。如果这样做,对TREE-DELETE应该做哪些什么必要的修改?一些人提出了一个公平的策略,为前驱和后继赋予相等的优先级,这样得到了较好的实验性能。如何对TREE-DELETE进行修改来实现这样一种公平策略?

(1)选择y作为前驱时,在TREE-DELETE中做孩子情况的修改,伪代码如下:

TREE-DELETE(T, z)
    if z.left == NIL		//z没有左孩子
        TRANSPLANT(T, z, z.right)
    else if z.right == NIL		//z没有右孩子
        TRANSPLANT(T, z, z.left)
//z有两个孩子的情况:
    else y = TREE-MAXIMUM(z.left)		//找前驱:左子树中最大的 就是z的前驱
        if y.p != z		//如果y不是z的直接左孩子
            TRANSPLANT(T, y, y.left)	//1、先用y的左孩子代替y,并成为y父亲的孩子
            y.left = z.left
            y.left.p = y
        //2、再用y代替z,并成为z父亲的一个孩子
        TRANSPLANT(T, z, y)
        y.right = z.right
        y.rignt.p = y

解释:当z只有一个孩子时,与课本处理后继的方式相同,代码前四句就是处理这种情况。

当z有两个孩子时,先找到其前驱y = TREE-MAXIMUM(z.left),再分情况处理 y是z的直接左孩子,还是左子树中的结点。

最终都要走的流程是:用前驱y代替z,原来z右边子树移植到y上,这是代码后三行处理的情况。

如果y不是z的左孩子,那么要先用 y.left 代替y,这就是中间部分处理的情况。

(2)若要采用公平策略,只需用抛硬币方法 即用随机数来决定,1 2 两数任取其一来决定是采用前驱还是后继即可。

TREE-DELETE(T, z)
    if z.left == NIL
        TRANSPLANT(T, z, z.right)
    else if z.right == NIL
        TRANSPLANT(T, z, z.left)
        
    else if random(1, 2) == 1		//用随机数来决定
        y = TREE-MINIMUM(z.right)		//选取后继
        if y.p != z
            TRANSPLANT(T, y, y.right)
            y.right = z.right
            y.right.p = y
        TRANSPLANT(T, z, y)
        y.left = z.left
        y.left.p = y
            
    else y = TREE-MAXIMUM(z.left)		//选取前驱
        if y.p != z
            TRANSPLANT(T, y, y.left)
            y.left = z.left
            y.left.p = y
        TRANSPLANT(T, z, y)
        y.right = z.right
        y.rignt.p = y



13.3-2 将关键字 41、38、31、12、19、8 连续地插入一棵初始为空的红黑树之后,试画出该结果树。

解如下:


13.4-3 在练习 13.3-2 中,将关键字 41、38、31、12、19、8 连续插入一棵初始化的空树中,从而得到一棵红黑树。请给出从该树中连续删除关键字 8、12、19、31、38、41 后的红黑树。

解如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值