6.1 (用插入的方法建堆)我们可以通过反复调用MAX-HEAP-INSERT实现向一个堆中插入元素,考虑BUILD-MAX-HEAP的如下实现方式:
BULID-MAX-HEAP(A)
A.heap-size=1
for i=2 to A.length
MAX-HEAP-INSERT(A,A[i])
a.当输入数据相同的时候,BULID-MAX-HEAP和BULID-MAX-HEAP’生成的堆是否总是一样?瑞国是,请证明;否则,请举一个反例。
b.证明:在最坏情况下,调用BULID-MAX-HEAP’建立一个包含n个元素的堆的时间复杂度是Θ(nlgn).
(a)不一样。就拿书中建立build-max-heap时的例子。A={4,1,3,2,16,9,10,14,8,7}
build-max-heap生成最大堆过程:A={4,1,3,2,16,9,10,14,8,7}
A={4,1,3,14,16,9,10,2,8,7}
A={4,1,10,14,16,9,3,2,8,7}
A={4,16,10,14,7,9,3,2,8,1}
最终生成的最大堆为:A={16,14,10,8,7,9,3,2,4,1}
build-max-heap'生成最大堆过程:A={4,1}
A={4,1,3}
A={4,2,3,1}
A={16,4,3,1,2}
A={16,4,9,1,2,3}
A={16,4,10,1,2,3,9}
A={16,14,10,4,2,3,9,1}
最终生成的最大堆为: A={16,14,10,8,2,3,9,1,4}
6.2(对d叉堆的分析)d叉堆与二叉堆很类似,但(一个可能的例外是)其中的每个非叶结点有d个孩子,而不是仅仅2个。
a.如何在一个数组中表示一个d叉堆?
b.包含n个元素的d叉堆的高度是多少? 请用n和d表示。
c.请给出EXTRACT-MAX在d叉最大堆上的一个有效实现,并用d和n表示出它的时间复杂度。
d.给出INSERT在d叉最大堆上的一个有效实现。并用d和n表示出它的时间复杂度。
e.给出INCREASE-KEY(A,i,k)的一个有效实现。当k<A[i]时,它会触发一个错误,否则执行A[i]=k,并更新相应的d叉最大堆。并用d和n表示出它的
时间复杂度。
a.我们可以把d叉堆近似看成完全d叉树,除了最底层外,该树是完全充满的,给定一个下标i.有
i的父结点为:PARENT(i) return (i+2)/d i的最左孩子结点为:LEFT(i) return (i-1)d+2
b.设一个有着n个元素的d叉堆的高度h 则d^0+d^1+...+d^h=n => h=log(d,n)
c.给出EXTRACT-MAX函数实现实际上是需要给出MAX-HEAPIFY(A,1)具体实现,因为在实现d叉堆删除最大元素时,EXTRACT-MAX函数中除了
MAX-HEAPIFY(A,1)函数实现和二叉堆不一样外,其他原理都是一样的。下面给出MAX-HEAPIFY(A,1)具体实现。
- <span style="font-size:18px;">MAX-HEAPIFY(A,i)
- L=LEFT(i)
- for i=1 to d
- if(A[L+i]>max)
- max=A[L+i]
- r=L+i
- if(r<=A.heap-size and A[r]>A[i] )
- largest=r//由于这个largest已经是最大值,所以省略了原书伪代码6-7行中的内容
- else largest=i
- if largest!=i
- exchange A[i] with A[largest]
- MAX-HEAPIFY(A,largest)
MAX-HEAPIFY时间复杂度是每次需要进行d次比较,而一共需要进行O(log(d,n))次交换 所以总的时间复杂度是O(dlog(d,n))
d.INSERT在d叉最大堆上的具体实现其实就是HEAP-INCREASE-KEY的具体实现,HEAP-INCREASE-KEY的具体实现实际上就是while循环的实现
while循环的实现实际就是PARENT(i)的实现与二叉堆不同,而PARENT(i)的实现在a部分已经给出。
下面看看它的时间复杂度,我们有递归式 T(n)=T((n+2)/d)+Θ(1) 这个递归式的解释T=Θ(log(d,n))
e. d已经给出答案。
6.3(Young氏矩阵) 在一个mXn的Young氏矩阵中,每一行的数据都是从左到右排序的,每一列的数据都是从上到下排序的。Young氏矩阵也会存
在一些值为无穷大的数据项,表示哪些不存在的元素。因此,Young氏矩阵可以用来存储r<=mn个有限的数。
(a)很容易 这里就不给出了。
(b)因为Young氏矩阵从左到右,从上到下,都是递增数列(以下程序也是基于这个原因来运行的),所以如果第一个元素为无穷大,那么以第一个元素
为中心,向右或者向下的元素都是无穷大,不可能比第一个元素小,因为这样就不满足递增的性质了,所以整个矩阵都是无穷大不存在的数,矩阵
应该为空。如果最后一个元素小于无穷大也就是有有效值,那么以这个元素为中心,还是递增数列的性质决定了,向上或向左的元素都应该比这个元
素小。所以整个矩阵都含有有效值,矩阵肯定是满的。
- <span style="font-size:18px;">#include <iostream>
- using namespace std;
- const m=4;const n=5;
- void Print(int A[][5]);
- int Young_EXTRACT_MIN(int A[][5],int i,int j)//young氏矩阵的删除
- {//删除最小值,我们用递归函数把删除的数通过两个方向的比较移动到矩阵右下角
- static int k=0;
- static int h=0;
- int min=A[0][0];
- if (k==h)
- {
- A[0][0]=A[m-1][n-1]+1;
- }
- k++;
- if (i==m-1&&j==n-1)
- {
- return -1;
- }
- else
- {
- if (A[i][j+1]<=A[i+1][j]&&j!=n-1)
- {
- swap(A[i][j+1],A[i][j]);
- Young_EXTRACT_MIN(A,i,j+1);
- }
- else if (A[i][j+1]>=A[i+1][j]&&i!=m-1)
- {
- swap(A[i+1][j],A[i][j]);
- Young_EXTRACT_MIN(A,i+1,j);
- }
- else if (A[i][j]>=A[i+1][j]&&j==n-1)
- {
- swap(A[i][j],A[i+1][j]);
- Young_EXTRACT_MIN(A,i+1,j);
- }
- else if (A[i][j]>=A[i][j+1]&&i==m-1)
- {
- swap(A[i][j],A[i][j+1]);
- Young_EXTRACT_MIN(A,i,j+1);
- }
- }
- h=k;
- return min;
- }
- void Young_INSERT(int A[][5],int i,int j,int key)
- {//插入与删除类似。
- static int k=0;
- if (k==0)
- {
- i=m-1;j=n-1;
- A[i][j]=key;//因为这个young氏矩阵元素不满,所以矩阵最后一个位置必是不存在值
- }
- k=1;
- if (i==0&&j==0)
- {
- return ;
- }
- else if(A[i-1][j]<=A[i][j]&&A[i][j-1]<=A[i][j])
- {
- return ;
- }
- else
- {
- if (A[i][j-1]>=A[i-1][j]&&j!=0)
- {
- swap(A[i][j-1],A[i][j]);
- Young_INSERT(A,i,j-1,key);
- }
- else if (A[i][j-1]<=A[i-1][j]&&i!=0)
- {
- swap(A[i-1][j],A[i][j]);
- Young_INSERT(A,i-1,j,key);
- }
- else if (A[i][j]<=A[i-1][j]&&j==0)
- {
- swap(A[i][j],A[i-1][j]);
- Young_INSERT(A,i-1,j,key);
- }
- else if (A[i][j]<=A[i][j-1]&&i==0)
- {
- swap(A[i][j],A[i][j-1]);
- Young_INSERT(A,i,j-1,key);
- }
- }
-
- }
- void Young_sort(int A[][5])
- {//排序我们通过循环调用Young_EXTRACT_MIN函数进行排序
- int kk=m*n,t;
- for (int h=0;h<kk;h++)//O()
- {
- t=Young_EXTRACT_MIN( A,0,0);
- if(t!=10000)
- {
- cout<<t<<" ";//O(m+n)
- }
- }
- cout<<endl;
- }
- int Young_find(int A[][5],int i,int j,int key)
- {//查找我们用递归函数,这里难点是矩阵4个角,左上和右下角因为在横竖方向上均是递增数组,
- //所以不可取,至少我还没有发现取这两个角能实现查找功能的方法。另外2个角横竖方向上一增一减有利于查找比较
- static flag=0,t=0;
- if (t==0)
- {
- i = 0, j = n-1;//我们取 Young氏矩阵右上角作为起始位置开始查找。
- }
- t=1;
- if (A[i][j] == key)
- {
- flag++;
- return flag;
- }
- else if(A[i][j] > key&&i!=m&&j!=-1)
- {
- Young_find(A,i,j-1,key);
- }
- else if(A[i][j] < key&&i!=m&&j!=-1)
- {
- Young_find(A,i+1,j,key);
- }
- return flag;
- }
- void Print(int A[][5])//辅助输出函数
- {
- for (int i=0;i<m;i++)//10000以上的数都表示不存在的数。
- {
- for (int j=0;j<n;j++)
- {
- cout<<A[i][j]<<"\t";
- }
- cout<<endl;
- }
- }
- void main()
- {
- int A[m][n]={{1,5,10,20,24},{4,30,35,52,55},{18,43,48,60,10000},{19,45,49,10000,10000}},x;
- cout<<"初始化:"<<endl;
- Print(A);
- cout<<endl;
- cout<<"删除当前矩阵最小值"<<endl;
- Young_EXTRACT_MIN(A,0,0);//删除矩阵最小值
- Print(A);
- cout<<"请输入待插入的数"<<endl;
- cin>>x;
- Young_INSERT(A,4,5,x);//插入特定值矩阵
- Print(A);
- cout<<"请输入待查找的数"<<endl;
- cin>>x;
- if(Young_find(A,0,0,x)) cout<<"已经找到"<<endl;
- else cout<<"没有找到"<<endl;
- cout<<"Young氏矩阵排序后:"<<endl;
- Young_sort(A);
- //(c)题的时间复杂度:T(p)=T(p-1)+c 每次递归或减少1位i或减少1位j//p=m+n T(p)=cp=O(p)=O(m+n)