8-4 用邻接矩阵表示图,若图中有1000个顶点,1000条边,则形成的邻接矩阵有多少矩阵元素?有多少非零元素?是否稀疏矩阵?


形成的邻接矩阵有10^6个元素;如果是有向图则拥有1000个非零元素,若是无向图则有1000×2=2000个非零元素;是稀疏矩阵。


8-5 用邻接矩阵表示图时,矩阵元素的个数与顶点个数是否相关?与边的条数是否相关?

矩阵元素的个数与顶点个数相关。非零元素与边的条数相关。


8-6 有n个顶点的无向连通图至少有多少条边?有n个顶点的有向强连通图至少有多少条边?试举例说明。

n个顶点连通,如果将n个顶点排成一直线,则很容易知道,至少需要n-1条边才能把它连通。
还是和刚才一样,只不过多一条边,将首尾连起来,使得从每个定点开始都可以回到该顶点。
例子已举。


8-7 对于有n个顶点的无向图,采用邻接矩阵表示,如何判断以下问题:图中有多少条边?任意两个顶点i和j之间是否有边相连?任意一个顶点的度是多少?

图中的边数等于邻接矩阵中非零元素的一半;查看矩阵中E[i][j]或者E[j][i]是否等于零,零的话不联通,反之连通;假设要观察顶点i的度,只要查看i行有多少个非零元素,即为i的度(如果查看i列也一样,因为无向图的邻接矩阵是行列对称的)。


8-9 试扩充深度优先搜索算法,在遍历图的过程中建立生成森林的左子女——右兄弟链表。算法的首部为
Void Graph::DFS(const int v, int visited[], TreeNode< int > *t)
其中,指针t指向生成森林上具有图顶点v信息的根结点。


答案建立生成森林的主函数里的q,定义很奇怪。还是老老实实按照书中DFS的定义来写。
声明:不写模板了。可以直接修改TreeNode的指针.

void Graph::DFS(){ //主过程
int* visited=new int[n]; //n为图中顶点的个数
for(int i=0;i< n;i++)visited[i]=0; //初始化辅助数组
Tree< int > &T; //这里要不要写成 Tree T? 还是迷糊
DFS(0,visited,T.root); //从顶点0开始构建生成森林
delete[] visited;
}

void Graph::DFS(const int v, int visited[], TreeNode< int > *t){
t.SetData(GetValue(v)); //访问该顶点,拷贝值到相应森林结点。
visited[v]=1; //修改为已访问
int w=GetFirstNeighbor(v); //找到顶点v的第一个邻接顶点w
t=t->FirstChild; //t指向t的左子女
while(w!=1){ //有邻接顶点
if(!visited[w]) //如果没有访问过
DFS(w,visited,t); //从该顶点开始建立生成森林
w=GetNextNeighbor(v,w); //找到v的下一个邻接顶点
t=t->NextSibling; //同时把t指向t的右兄弟
}
}



8-10 用邻接表表示图时,顶点个数设为n,边的条数设为e,在邻接表上执行有关图的遍历操作时,时间代价是O(n*e)?还是O(n+e)?或者是O(max(n,e))?

必须要顺邻接表的顶点表挨个检查,并且沿着每条边向下遍历,所以时间代价为O(n+e)。


8-16 试证明在一个有n个顶点的完全图中,生成树的数目至少有2^(n-1)-1

这题有如下疑问
首先生成树的根固定么?如果不固定的话,就相当于将这n个顶点得到一个排序序列,就是生成树了。p(n)显然是答案,当然p(n)永远都大于2^(n-1)-1.这样的解答貌似太简单了。
然后我们假设必须从某个顶点开始,作为生成树的根,那么问题就变成剩下的n-1个顶点,做一个排序序列,那么显然就是比较p(n-1)与2^(n-1)-1。而这个显然必须在n>=5时才能成立,好像又不对。
迷惑中。


8-18 利用Dijkstra算法的思想,设计一个求最小生成树的算法。

计算连通网络的最小生成树的dijkstra算法可描述如下:将连通网络中所有的边以方便的次序逐步加入到初始为空的生成树的边集合T中。每次选择并加入一条边后,需要判断它是否会与先前加入T的边构成回路。如果构成了回路,则从这个回路中将权值最大的边退选。

具体的实现,答案的程序看不太懂。并且从这里开始暂时不把数据结构的答案放到网上了。因为迷糊的东西太多了。
今天已经20号了,7月份满打满算还剩11天,之前的20天,虽然看书的热情很高,但总是各种各样的原因导致许多时间被其他事情占用,又加上最近几天出现了点消极念头,所以本月的计划任务告急。良好的开端就是成功的一半,为了达到这种“开门大吉”的效果,决定将7月份最后11天调整为冲刺计划,史称“吐血大跃进”。

简单的说,本次大跃进的手段就是在一定的时间内不停地看同一种科目,直到吐了为止。

第一个要完成的目标就是高数,目前为止看到完第5章,除2道题有困难外,都还算顺利.上册还剩2章,加上下册的5章,一共是7章。按照之前的高数速度估计,一天12小时计可以完成2章左右,计划从21号到23号,3天时间完成任务。

第二个任务就是单词,这十一天,除了每天仍然要保持250单词/天外,专门抽出24号和25号两天,按最低效率算,每小时100个单词,背15小时,两天可以搞定3000个单词。

接下来的任务更艰巨,26,27号两天之内无论如何都要结束《数据结构》和习题。然后28,29号,完成《计算机原理》,翻过一边就行,不管看不看的懂。30,31号,看完电子版的汇编教材。

完成这些任务后,放风2天。
7-10 假定用一个循环链表来实现一个有序表,并让指针head指向具有最小关键码的结点。指针current初始时等于head,每次搜索后指向当前检索的结点,但如果搜索不成功则current重置为head。试编写一个函数search(head,current,key)实现这种搜索。当搜索成功时函数返回被检索的结点地址,若搜索不成功则函数返回空指针0.请说明如何保持指针current以减少搜索时的平均搜索长度。

思路:定义搜索函数为类的公共函数。并且这个有序循环列表按key值从小到大排列。
 ListNode< Type >* Search(ListNode< Type >*head,ListNode< Type > *current,Type key){
if(key < current->data ) //key小,在前半段,递归调用.
return current=Search(current,head,key);
while(key > current->data && current!=head)
current=current->link;
if(current->data != key)
return NULL;
else return current;
}


7-11 考虑用双向链表来实现一个有序表,使得能在这个表中进行正向和反向的搜索。若指针p总是指向最后成功搜索到的结点,搜索可以从p指示的结点出发沿任一方向进行。试根据这种情况编写一个函数search(head,p,key),检索具有关键码key的结点,并相应的修改p。最后请给出搜索成功和搜索不成功时的平均搜索长度。

思路:类似上题
 ListNode< Type >* Search(ListNode< Type > *head,ListNode< Type > *p,Type key){
if(p->data > key)
while(p->data > key&&p !=NULL)p=p->llink;
if(p->data < key)
while(p->data < key&&p !=NULL)p=p->rlink;
if(p->data == key)
return p;
else{ p=head;reutrn NULL;}
}


7-14 试说明如何利用并查集来计算无向连通图中连通子图的个数。

1.以每个顶点为一个集合,初始化
2.边作为等价对,建立并查集
3.最后得到的集合个数就是连通子图的个数。


7-16 有n个结点的二叉搜索树具有多少种不同形态?

这个题和 问 在给定中序序列下的树,可以有多少种前序遍历。
答案是一样的1/(n+1)Cn/2n


7-18 在二叉搜索树上删除一个有两个子女的结点时,可以采用以下三种方法:
(1)用左子树Tl上具有最大关键码的结点X顶替,再递归地删除X。
(2)交替地用左子树Tl上具有最大关键码的结点和右子树Tr上具有最小关键码的结点顶替,再递归地删除适当的结点。
(3)用左子树Tl上具有最大关键码的结点或者用右子树Tr上具有最小关键码的结点顶替,再递归地删除适当的结点。可随机选择其中一个方案。


声明:均作为类函数加入,为减少篇幅,省略模板结构。并且假设所删除的结点一定是含有两个子女的。
(1)利用左子树上最大关键码结点代替。
remove(BstNode< Type > *ptr){
if(no child)
直接删除
BstNode< Type > *temp;
temp=Max(ptr->leftChild);
ptr->data=temp->data;
remove(temp);
}


(2)
设置一个参数来判断什么 顶替方案
7-1 设A={1,2,3},B={3,4,5},求下列结果:
(1) A+B (2)A*B (3)A-B
(4) A.Contains(1) (5)A.AddMember(1)
(6) A.DelMember(1) (7)A.Min()


(1)A={1,2,3,4,5}
(2)A={3}
(3)A={1,2}
(4)1
(5)A={1,2,3}
(6)A={2,3}
(7)1


7-2 试编写一个算法,打印一个有穷集合中的所有成员。要求使用集合抽像数据类型中的基本操作。如果集合中包含有子集合,各个子集合之间没有重复的元素,采用什么结构比较合适。

采用广义表的结构会比较好,就像广义表的遍历操作一样,采用递归。
template< class Type >class Set{
int Contains(const Type x);
int SubSet(Set< Type >& right);
int operator ==(Set< Type >&right);
int Elemtype();
Type GetData();
char GetName();
Set< Type >* GetSubSet();
Set< Type >* GetNext();
int IsEmpty();
};

ostream& operator<< (ostream& out,Set< Type >){
t.traverse(out,t);return out;
}

void traverse(ostream&out, Set< Type >s){
if(s.IsEmpty()==0){
if(s.Elemtype()==0)out<< s.GetName()<< '{';
else if(s.Elemtype()==1){
out<< s.GetData;
if(s.GetNext()!=NULL)out<< ',';
}
else{
traverse(s.GetSubSet());
if(s.GetNext()!=NULL)out<< ',';
}
traverse(s.GetNext());
}
else out<< '}';
}

除了最后一行外,全部照抄的参考答案。题目没意思,懒得做

7-3 当全集合可以映射成1到N之间的整数时,可以用位数组来表示它的任一子集合。当全集和是下列集合时,应当建立什么样的映射?
(1)整数0,1,...,99
(2)从n到m的所有整数,n<=m
(3) 整数n,n+2,n+4,...,n+2k
(4)字母'a','b','c',...,'z'
(5)两字母组成的字符串,其中每个字母取自'a','b','c',...,'z'.


假设不设置监哨点
(1) i={0,1,...,99}一一对应即可
(2) n->0,n+1->1,n+2->2,...,n+(m-n)->m-n 即可
(3) n->0,n+2->1,n+4->2,...,n+2k->k 即可
(4) 'a'->0,'b'->1,...,'z'->25
(5) 'aa'->0,'ab'->1,...,'zz'->675

7-7 给定一个用无序链表表示的集合,需要在其上执行operator+(),operator*(),operator-(),Contains(x),AddMember(x),DelMember(x),Min(),试写出它的类声明,并给出所有这些成员函数的实现。

思路:模仿教材中有序链表表示集合的类结构,写无序链表表示方法,无序链表在删除和加入时无序做过多操作,但是在求最大最小合并等操作里,要扫描全部链表。交并等集合运算时要注意是否有相同元素.
template < class Type >class SetList;
template< class Type >class SetNode{
//这部分参考教材
friend class SetList< Type >;
public:
SetNode(const Type& item)
:data(item),link(NULL);
private:
Type data;
SetNode< Type >* link;
};

template< class Type >class SetList{
//这部分参考教材
public:
SetList();
~SetList();
SetList< Type > &operator+(SetList< Type >& right);
SetList< Type > &operator*(SetList< Type >& right);
SetList< Type > &operator-(SetList< Type >& right);
int Contains(const Type &x);
int AddMember(const Type &x);
int DelMember(const Type &x);
Type & Min();
private:
SetNode< Type > *first,*last;
}

template< class Type >SetList< Type >& SetList< Type >
::operator+(SetList< Type >& right){
SetNode< Type > *pb=right.first->link; //目标链
SetNode< Type > *tmp=pa=first->link; //tmp暂存first
SetNode< Type > *pc=first; //新加入
int diff; //判断元素是否在A集合中出现
while(pb!=NULL){
diff=1; //初始置为 不在A集合中出现
for(pa=tmp;pa=pa->link;pa!=NULL)
if(pb->data==pa->data){
//如果相等,pa直接到链尾
pa=last->link;
diff=0;
}
if(diff==1)
pc=pc->link=pb; //加入pb
pb=pb->link;
}
pc->link=tmp;
//原来的A链加入新链的链尾
return * this;
}

template< class Type >SetList< Type >& SetList< Type >
::operator*(SetList< Type >& right){
SetNode< Type > *pb=right.first->link; //目标链
SetNode< Type > *tmp=pa=first->link; //tmp暂存first
SetNode< Type > *pc=first; //新加入
int diff; //判断元素是否在A集合中出现
while(pb!=NULL){
diff=1; //初始置为 不在A集合中出现
for(pa=tmp;pa=pa->link;pa!=NULL)
if(pb->data==pa->data){
//如果相等,pa直接到链尾
pa=last->link;
diff=0;
}
if(diff==0)
pc=pc->link=pb; //加入pb
pb=pb->link;
}
return * this;
}

template< class Type >SetList< Type >& SetList< Type >
::operator-(SetList< Type >& right){
SetNode< Type > *pb=right.first->link; //目标链
SetNode< Type > *pa=first; //this链
SetNode< Type > *tmp;
while(pb!=NULL){
for(pa=first;pa=pa->link;pa->link!=NULL)
if(pa->link->data==pb->data){
如果B中有元素与A相等,则删去A中的该元素
tmp=pa->link;
pa->link=pa->link-link;
delete tmp;
pa=last;
}
pb=pb->link;
}
return * this;
}

template< class Type >int SetList< Type >
::Contains(const Type &x){
SetNode< Type > *p=first->link;
while(p!=NULL)
if(p->data==x)
return 1;
else p=p->link;
return 0;
}

template< class Type >int SetList< Type >
::AddMember(const Type &x){
int i=Contains(x);
if(i==1)
return 0;
else{
last->link=new SetNode(x);
last=last->link;
return 1;
}
}

template< class Type >int SetList< Type >
::DelMember(const Type &x){
SetNode< Type > *p=first->link,*q=first;
while(p!=NULL){
if(p->data==x){
q->link=p->link;
if(p==last)last=q;
delete p;
return 1;
}
else{
p=p->link;
q=q->link;
}
}
return 0;
}

template< class Type >Type& SetList< Type >::Min(){
//求集合中的最小元素.
SetNode< Type > *p=first->link;
if(p==NULL){
//如果是空表返回错误;
cout<< "空表"<< endl;
return;
}
Type minnum=p->data;
p=p->link;
while(p!=NULL){
if(minnum > p->data)
minnum=p->data
p=p->link;
}
return minnum;
}