NCHU-数据结构复习题

 


 
 

  

时间复杂度

  1. 下面程序的时间复杂度是 ------ O(m*n)
for(i=0;i<m;i++)
	for(j=0;j<n;j++)
		a[i][j] = 0;
  1. 下面程序的时间复杂度是 ------ O(n)
s = 1;
for(i=1;i<n;i++)
	s = s * i;
  1. 下面程序的时间复杂度是 ------ O(n2)
s = 0;
for(i=0;i<n;i++)
	for(j=0;j<n;j++)
		s += a[i][j];
  1. 下面程序的时间复杂度是 ------ O(log3n)
i = 1;
while(i <= n)
	i = i * 3;

 
 

逻辑图和逻辑结构

第一题
答案
 
 
第二题
B = (K, R)
K = {k1, k2, k3, k4, k5, k6, k7, k8, k9}
R = {<k1, k2>, <k1, k3>, <k3, k6>, <k3, k4>, <k4, k5>, <k6, k8>, <k6, k7>, <k8, k9>}

 
 

线性表

第一题
思路:因为线性表是非递减有序的,所以相同的元素必定会相邻分布。我们只需要按顺序遍历,依次查看后一个元素是否与之相同,若不相同则可以确定该元素不存在相同元素,可以进行下一个元素;反之,则相同,将后一个元素删除。重复操作直到遍历至线性表最后一个。

Status DeleteSame(SqList &L){//从线性表L中删除值相同的多余元素 
	int i;
	while(i <= ListLength(L)-1){
		GetElem(L, i, e1);//获取第i位元素e1 
		GetElem(L, i+1, e2);//获取第i+1位元素e2 
		if(e1 != e2)//元素不相同,进行下一个 
			i++;
		else//元素相同,删除一个 
			ListDelete(L, i+1, e);
	}
	return OK;
}

 
 
第二题
思想:将i+k-1起的所有元素依次移到i-1起的对应位置上。
i-1 -> i+k-1

Status DeleteK(SqList &L, int i, int k){//在线性表L中删除第i个元素起的k个元素 
	if(i<0 || k<0 || i+k-1>L.length)//i为逻辑位序 
		return INFEASIBLE;
	for(int j=i+k-1; j<=L.length-1; j++)//将i+k-1起的所有元素依次移到i-1起的对应位置上 
		L.elem[j-k] = L.elem[j];
	L.length = L.length - k;
	return OK;
} 

 
 
第三题
思路:区分出奇偶次,进行分别插入。

Status Divide_LinkedPoly(LinkedPoly &L, LinkedPoly &La, LinkedPoly &Lb){
	//将单向循环链表存储的稀疏多项式L拆成只含奇次项的La和只含偶次项的Lb 
	La = L;
	Lb = (LinkedPoly)malloc(sizeof(PolyNode));
	LinkedPoly p = L->next;
	LinkedPoly pa = La;
	LinkedPoly pb = Lb;
	while(p != L){
		if(p->data.exp % 2){//奇次项 
			pa->next = p;
			pa = p;
		}
		else{//偶次项 
			pb->next = p;
			pb = p;
		}
		p = p->next;//下一个多项式 
	}
	pa->next = La;//将La首尾相连,成循环链表 
	pb->next = Lb;//将Lb首尾相连,成循环链表 
	return OK;
} 

 
 
第四题
思想:按从小到大的顺序依次把 La 和 Lb 的元素插入新表的头部 Lc 处,最后处理 La 或 Lb 的剩余元素。

Status ListMergeOppose_L(LinkList &La, LinkList &Lb, LinkList &Lc){
	LinkList pa = La->next;
	LinkList pb = Lb->next;//pa和pb分别指向A,B的当前元素 
	Lc = La;
	Lc->next = NULL;
	while(pa && pb){
		if(pa->data <= pb->data){
			p = pa->next;
			pa->next = Lc->next;
			Lc->next = pa;//将La的元素插入新表的表头 
			pa = p;
		}
		else{
			p = pb->next;
			pb->next = Lc->next;
			Lc->next = pb;//将Lb的元素插入新表的表头 
			pb = p;
		}
	}
	while(pa){
		p = pa->next;
		pa->next = Lc->next;
		Lc->next = pa;//将La的剩余元素插入新表的表头 
		pa = p;
	}
	while(pb){
		p = pb->next;
		pb->next = Lc->next;
		Lc->next = pb;//将Lb的剩余元素插入新表的表头 
		pb = p;
	}
	free(Lb);
	return OK;
}

 
 
第五题
时间复杂度:O(n)。

Status Delete_Between(LinkList &L, int mink, int maxk){
	//删除链表L中值大于mink且小于maxk的所有元素 
	LinkList p = L, p;
	while(p->next->data <= mink)
		p = p->next;//p是最后一个不大于mink的元素 
	if(p->next){//如果还有比mink更大的元素 
		q = p->next;
		while(q->data < maxk){
			p->next = q->next;//q的第一个不小于maxk的元素 
			free(q);
			q = p->next;
		}
	}
	return OK;
}

 
 
第六题
思路:按位比较即可。

Status Compare_SqList(SqList LA, SqList LB){
	//比较字符表LA和LB,并用返回表示结果
	//值为正,表示LA>LB
	//值为负,表示LA<LB
	//值为零,表示LA=LB
	//设LA和LB中表长以后存储的字符均为空 
	for(int i=1; LA.elem[i]||LB.elem[i]; i++)
		if(LA.elem[i] != LB.elem[i])//出现字符不相同,则可以区分大小 
			return LA.elem[i] - LB.elem[i];
	return 0;
}

 
 

栈和队列

第一题
ABCD、ABDC、ACBD、ACDB、BACD、ADCB、BADC、BCAD、BCDA、BDCA、CBAD、CBDA、CDBA、DCBA
 
 
第二题
  队列Q采用顺序存储结构,设队头指针front指向队列头元素位置,队尾指针rear指向队列尾元素的下一个位置,队列的容量(即存储的空间大小)为MAXQSIZE。入队时,若Q.rear==MAXQSIZE,则会发生队列的上溢现象,即使队列中尚余有足够的空间,但元素却不能入队,出现了“假溢出”现象。
一般地,可用以下方法解决:
  (1)采用移动元素的方法。假定空余空间足够,每当有一个新元素入队,就将队列中已有的元素向队头移动一个位置。
  (2)每当删去一个队头元素,则可依次移动队列中的元素总是使front指针指向队列中的第一个位置。
  (3)采用循环队列方式。将队头、队尾看作是一个首尾相接的循环队列,此时队首仍在队尾之前,作插入和删除运算时仍遵循“先进先出”的原则。

 
 
第三题
Stack
 
 
第四题
第四题答案

 
 
第五题
char
 
 
第六题
队列逆置
 
 
第七题
  只设一个指针Q.rear指向队尾元素结点,存储结构的描述与单链队列一致。

Status InitCircleQueue(LinkQueue &Q){
	//初始化循环链表表示的队列Q 
	Q.rear = (QueuePtr)malloc(sizeof(QNode));
	if(!Q.rear)
		exit(OVERFLOW);
	Q.rear->next = Q.rear;//连成循环 
} 

Status EnCircleQueue(LinkQueue &Q, QElemType e){
	//把元素e插入循环链表表示的队列Q 
	p = (QueuePtr)malloc(sizeof(QNode));
	if(!p)
		exit(OVERFLOW);
	p->data = e;
	p->next = Q.rear->next;//直接把p加在Q.rear的后面 
	Q.rear->next = p;
	Q.rear = p;//修改尾指针 
}

Status DeCircleQueue(LinkQueue &Q, QElemType &e){
	//从循环链表表示的队列Q头部删除元素e 
	if(Q.rear->next == Q.rear)
		return INFEASIBLE;//队列已空 
	p = Q.rear->next->next;
	e = p->data;
	Q.rear->next->next = p->next;
	if(Q.rear == p)
		Q.rear = p->next;
	free(p);
	return OK;
} 


 
 
第八题
思路:利用栈:后进先出和队列:先进先出的特点,将输入的字符同时入栈和入队列。再进行出栈和出队列进行比较。

int Palindrome_Test(){
	//判别字符串是否回文序列,是则返回1,否则返回0 
	InitStack(S);//初始化栈 
	InitQueue(Q);//初始化队列 
	while((c=getchar()) != '@'){
		Push(S, c);//进栈 
		EnQueue(Q, c);//进队列 
	}
	while(!StackEmpty(S)){//利用栈:后进先出;队列:先进先出的特点进行比较 
		Pop(S, a);
		DeQueue(Q, b);
		if(a != b)
			return ERROR;
	}
	return OK;
} 

 
 

数组

第一题
链接
(1)按行优先存储:
A[0][0][0][0],A[0][0][0][1],A[0][0][1][0],A[0][0][1][1],
A[0][1][0][0],A[0][1][0][1],A[0][1][1][0],A[0][1][1][1],
A[1][0][0][0],A[1][0][0][1],A[1][0][1][0],A[1][0][1][1],
A[1][1][0][0],A[1][1][0][1],A[1][1][1][0],A[1][1][1][1]。
(2)按列优先存储:
A[0][0][0][0],A[1][0][0][0],A[0][1][0][0],A[1][1][0][0],
A[0][0][1][0],A[1][0][1][0],A[0][1][1][0],A[1][1][1][0],
A[0][0][0][1],A[1][0][0][1],A[0][1][0][1],A[1][1][0][1],
A[0][0][1][1],A[1][0][1][1],A[0][1][1][1],A[1][1][1][1]。
 
 
第二题
链接
答案1
答案2

 
 
第三题
  

void EMove_k(int A[n], int k){//把数组A的元素循环右移k位,只用一个辅助空间 
	for(int i=1;i<=k;i++)
		if(n%i==0 && k%i==0)//求n和k的最大公约数p 
			p = i;
	for(int i=0;i<p;i++){
		j = i;
		m = (i+k)%n;
		temp = A[i];
		while(m != i){//循环右移一步 
			A[j] = temp;
			temp = A[m];
			A[m] = A[j];
			j = m;
			m = (j+k)%n;
		}
		A[i] = temp;
	}
}

 
 
第四题
(1)算法1:依题意,先求出每行的最小值元素,放入 min[m] 之中,再求出每列的最大值元素,放入 max[n] 之中,若某元素既在 min[i] 中,又在 max[j] 中,则该元素 A[i][j] 便是马鞍点,找出所有这样的元素,即找到了所有马鞍点。

void Get_Saddle1(int A[m][n]){
	have = 0;
	for(int i=0;i<m;i++){//计算每行的最小值元素,放入min[m]
		min[i] = A[i][0];
		for(int j=1;j<n;j++)
			if(A[i][j] < min[i])
				min[i] = A[i][j];
	}
	for(int j=0;j<n;j++){//计算每列的最大值元素,放入max[m] 
		max[j] = A[0][j];
		for(int i=1;i<m;i++)
			if(A[i][j] > max[j])
				max[j] = A[i][j];
	}
	for(int i=0;i<m;i++)
		for(int j=0;j<n;j++)
			if(min[i] == max[j]){//判定是否为马鞍点 
				print(A[i][j]);//显示马鞍点 
				have = 1;
			}
	if(!have)
		print("没有鞍点\n");
}

(2)算法2:依题意,先求出某行的最小值元素 min,再判断该 min 是否是其所在列的最大值元素,如果是,则该元素便是马鞍点,找出每一行中所有这样的元素,即找到了所有马鞍点。

void Get_Saddle2(int A[m][n]){
	for(int i=0;i<m;i++){
		for(min=A[i][0],j=0;j<n;j++)
			if(A[i][j] < min)//求一行中的最小值 
				min = A[i][j];
		for(j=0;j<n;j++)
			if(A[i][j] == min){//判断该最小值是否为马鞍点 
				for(flag=1,k=0;k>m;k++)
					if(min < A[k][j])
						flag = 0;
				if(flag)
					print(A[i][j]);//显示马鞍点 
			}
	} 
}

 
 
第五题
链接

//稀疏矩阵的三元组顺序表存储表示
#define MAXSIZE 12500 //假设非零元素个数的最大值为12500 
typedef struct{
	int i,j;//该非零元的行下标和列下标 
	ElemType v;//非零元值 
}Teiple;
typedef struct{
	Triple data[MAXSIZE + 1];//非零元三元组,data[0]未用 
	int mu, nu, tu;//矩阵的行数、列数和非零元个数 
}TSMatrix;

void TSMatrix_Add(TSMatrix A, TSMatrix B, TSMatrix &C){//三元组顺序表表示的稀疏矩阵加法 
	C.mu = A.mu;
	C.nu = A.nu;
	C.tu = 0;
	pa = 1;
	pb = 1;
	pc = 1;
	for(row=1;row<=A.mu;row++){//对居中的每一行进行相加 
		while(A.data[pa].i < row && pa <= A.tu)
			pa++;
		while(B.data[pb].i < row && pb <= B.tu)
			pb++;
		while(A.data[pa].i == row && B.data[pb].i == row && pa <= A.tu && pb <= B.tu){//行列相等的元素 
			if(A.data[pa].j == B.data[pb].j){
				ce = A.data[pa].e + B.data[pb].e;
				if(ce){//和不为0 
					C.data[pc].i = row;
					C.data[pc].j = A.data[pa].j;
					C.data[pc].e = ce;
					pa++;
					pb++;
					pc++;
				}
			}
			else if(A.data[pa].j > B.data[pb].j){
				C.data[pc].i = row;
				C.data[pc].j = B.data[pb].j;
				C.data[pc].e = B.data[pb].e;
				pb++;
				pc++;
			}
			else{
				C.data[pc].i = row;
				C.data[pc].j = A.data[pa].j;
				C.data[pc].e = A.data[pa].e;
				pa++;
				pc++;
			}
		}
		while(A.data[pa]==row && pa<=A.tu){//插入A中剩余的元素(第row行) 
			C.data[pc].i = row;
			C.data[pc].j = A.data[pa].j;
			C.data[pc].e = A.data[pa].e;
			pa++;
			pc++;
		}
		while(B.data[pb]==row && pb<=B.tu){//插入B中剩余的元素(第row行) 
			C.data[pc].i = row;
			C.data[pc].j = B.data[pb].j;
			C.data[pc].e = B.data[pb].e;
			pb++;
			pc++;
		}
	}
	C.tu = pc;
} 

 
 

第一题
KMP详解
next数组

 
 

排序

第一题
  可以把排序算法的输出解释为对一个待排序的下标求一种排列,使得序列中的元素按照升序排序。例如,待排序序列是 (a1, a2, … , an),则输出是这些元素的一个排序。因此,对于一个任意的 n 个元素的序列排序后,可能的输出有 n! 个,即有 n! 个不同的比较路径。在排序过程中,每次比较会有两种情况出现,若整个排序需要进行 t 次比较,则会出现 2t 种情况,于是有:22 ≥ n!,即 t ≥ log2(n!)。当待排序元素个数 n 非常大时,有 t ≥ log2(n!) ≈ nlog2n。
 
 
第二题
(1)直接插入排序
初始关键字:[503] 017 512 061 908 170 897 275 653 426 154
i=2:(017)[017 503] 512 061 908 170 897 275 653 426 154
i=3:(512)[017 503 512] 061 908 170 897 275 653 426 154
i=4:(061)[017 061 503 512] 908 170 897 275 653 426 154
i=5:(908)[017 061 503 512 908] 170 897 275 653 426 154
i=6:(170)[017 061 170 503 512 908] 897 275 653 426 154
i=7:(897)[017 061 170 503 512 897 908] 275 653 426 154
i=8:(275)[017 061 170 275 503 512 897 908] 653 426 154
i=9:(653)[017 061 170 275 503 512 653 897 908] 426 154
i=10:(426)[017 061 170 275 426 503 512 653 897 908] 154
i=11:(154)[017 061 154 170 275 426 503 512 653 897 908]

(2)希尔排序(增量 d[1]=5 )
 初始关键字:503 017 512 061 908 170 897 275 653 426 154
一趟排序结果:154 017 275 061 426 170 897 512 653 908 503
二趟排序结果:154 017 275 061 426 170 503 512 653 908 897
三趟排序结果:017 061 154 170 275 426 503 512 653 897 908

(3)快速排序
 初始关键字: 503   017   512   061   908   170   897   275   653   426   154
一次划分结果: 154   017   426   061   275   170  [503]  897   653   908   512
分别快排结果: 061   017  [154]  426   275   170  [503]  897   653   908   512
       [017] [061] [154]  426   275   170  [503]  897   653   908   512
       [017] [061] [154]  170   275  [426] [503]  897   653   908   512
       [017] [061] [154] [170] [275] [426] [503]  897   653   908   512
       [017] [061] [154] [170] [275] [426] [503]  512   653  [897] [908]
  排序结果:[017] [061] [154] [170] [275] [426] [503] [512] [653] [897] [908]

(4)堆排序
 初始关键字:503 017 512 061 908 170 897 275 653 426  154
   初始堆:908 653 897 503 426 170 512 275 061 017  154
输出堆顶 908:154 653 897 503 426 170 512 275 061 017 [908]
调整后的新堆:897 653 512 503 426 170 154 275 061 017 [908]
输出堆顶 897:017 653 512 503 426 170 154 275 061 [897 908]
调整后的新堆:653 503 512 275 426 170 154 017 061 [897 908]
输出堆顶 653:061 503 512 275 426 170 154 017 [653 897 908]
调整后的新堆:512 503 170 275 426 061 154 017 [653 897 908]
输出堆顶 512:017 503 170 275 426 061 154 [512 653 897 908]
调整后的新堆:503 426 170 275 017 061 154 [512 653 897 908]
输出堆顶 503:154 426 170 275 017 061 [503 512 653 897 908]
调整后的新堆:426 275 170 154 017 061 [503 512 653 897 908]
输出堆顶 426:061 275 170 154 017 [426 503 512 653 897 908]
调整后的新堆:275 154 170 061 017 [426 503 512 653 897 908]
输出堆顶 275:017 154 170 061 [275 426 503 512 653 897 908]
调整后的新堆:170 154 017 061 [275 426 503 512 653 897 908]
输出堆顶 170:061 154 017 [170 275 426 503 512 653 897 908]
调整后的新堆:154 061 017 [170 275 426 503 512 653 897 908]
输出堆顶 154:017 061 [154 170 275 426 503 512 653 897 908]
调整后的新堆:061 017 [154 170 275 426 503 512 653 897 908]
输出堆顶 061:017 [061 154 170 275 426 503 512 653 897 908]
  排序结果:[017 061 154 170 275 426 503 512 653 897 908]

(5)归并排序
 初始关键字:[503] [017] [512] [061] [908] [170] [897] [275] [653] [426] [154]
一趟归并之后:[017   503] [061   512] [170   908] [275   897] [426   653] [154]
二趟归并之后:[017   061   503   512] [170   275   897   908] [154   426   653]
三趟归并之后:[017   061   170   275   503   512   897   908] [154   426   653]
四趟归并之后:[017   061   154   170   275   426   503   512   653   897   908]

(6)基数排序
初始关键字: 503 017 512 061 908 170 897 275 653 426 154
第一趟分配:Q[0] Q[1] Q[2] Q[3] Q[4] Q[5] Q[6] Q[7] Q[8] Q[9]
第一趟分配:170  061  512  503  154  275  426  017  908
                 653          897
第一趟收集:170 061 512 503 653 154 275 426 017 897 908
第二趟分配:Q[0] Q[1] Q[2] Q[3] Q[4] Q[5] Q[6] Q[7] Q[8] Q[9]
第二趟分配:503  512  426       653  061  170  897
      908  017          154     275
第二趟收集:503 908 512 017 426 653 154 061 170 275 897
第三趟分配:Q[0] Q[1] Q[2] Q[3] Q[4] Q[5] Q[6] Q[7] Q[8] Q[9]
      017  154  275    426  503  653    897  908
      061  170          512
第三趟收集:017 061 154 170 275 426 503 512 653 897 908

 
 
第三题
  采用基数排序方法时间复杂度最佳。因为这里英文单词的长度相等,且英文单词是由 26 个字母组成的,满足进行基数排序的条件,同时,依题意,m<<n,基数排序的时间复杂性由 O(m(n+rm)) 变成 O(n),因此时间复杂性最佳。
 
 
第四题
(1)当每个记录本身的信息量很大时,应尽量减少记录的移动,直接插入、冒泡和简单选择排序的平均时间复杂度为 O(n2),但简单选择排序中记录移动的次数最少,所以采用简单选择排序为佳。

(2)在直接插入、冒泡和简单选择排序中,直接插入和冒泡排序是稳定的,且两者在关键码呈基本正序时都居于最好时间复 O(n),因此可从中任选一个方法。

(3)就平均时间性能而言,基于比较和移动的排序方法中快速排序最佳。

(4)快速排序在最坏情况的时间复杂度为 O(n2),而堆排序和二路归并排序最坏情况的时间复杂度为 O(nlog2n),其中堆排序不稳定,所以应选择二路归并排序。

(5)按照关键码的结构,采用基数排序为好。

 
 
第五题
思路:类似快排思路。

#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef struct{
	ElemType *elem;//存储空间基址 
	int length;//当前长度 
	int listsize;//当前分配的存储容量以一数据元素存储长度为单位 
}SqList;

void partition(SqList &L){
	int i = 1, j = L.length - 1;
	while(i < j){
		while(i<j && L.elem[i]%2!=0)
			i++;
		while(i<j && L.elem[j]%2==0)
			j--;
		if(i < j)
			L.elem[i]<-->L.elem[j];
	}
} 

 
 
第六题

void Divide(int L[], int n){//将L中值为负的记录调到非负的记录之前 
	low = 0;
	high = n - 1;
	while(low < high){
		while(low<high && L[high]>=0)//以0作为虚拟枢轴 
			high--;
		L[low]<-->L[high];
		while(low<high && L[low]<0)
			low++;
		L[low]<-->L[high];
	}
}

 
 
第七题
  设立三个指针 i、j、k,其中:j 表示当前元素,i 以前的元素全部为红色,k 以后的元素全部为蓝色。可以根据 j 的颜色,把其交换到序列的前部或后部。

typedef enum {RED, WHITE, BLUE} color;//三种颜色 
void Flag_Arrange(color a[], int n){
	//把由三种颜色组成的序列重排为按照红、白、蓝的顺序排列
	i = 0;
	j = 0;
	k = n - 1;
	while(j <= k){
		switch(a[j]){
			case RED:
				a[i]<-->a[j];
				i++;
				j++;
				break;
			case WHITE:
				j++;
				break;
			case BLUE:
				a[j]<-->a[k];
				k--;
		}
	} 
	
}

 
 

查找

第一题
折半查找判定树
查找成功的平均查找长度:ASL = (1+22+34+48+55) / 10 = 74 / 20 = 3.7
查找失败时最多与关键字的比较次数为 5。
 
 
第二题
二叉排序树:
二叉排序树
删除72:
删除72

 
 
第三题
第三题答案

 
 
第四题
第四题答案

 
 
第五题
第五题答案

 
 
第六题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

int last = 0, flag = 1;
int Is_BSTree(BiTree T){
	//判断二叉树T是否二叉排序树,是则返回1,否则返回0 
	if(T->lchild && flag)
		Is_BSTree(T->lchild);
	if(T->data < last)//与其中序前驱相比较 
		flag = 0;
	last = T->data;
	if(T->rchild && flag)
		Is_BSTree(T->rchild);
	return flag;
}

 
 
第七题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

void Print_NLT(BiTree T, int x){//从大到小输出二叉排序树T中所有不小于x的元素 
	if(T->lchild)
		Print_NLT(T->lchild);
	if(T->data < x)//当遇到小于x的元素时立即结束运行 
		exit();
	printf("%d", T->data);
	if(T->rchild)
		Print_NLT(T->rchild);//先有后左的中序遍历 
}

 
 

第一题
  二叉树的先根序列与等价树的先根序列相同,二叉树的中序序列对应着树的后根序列。先转化成二叉树,再通过二叉树转换成树。(左儿子右兄弟,逆用
第一题答案

 
 
第二题
  当 n=2 时,要使其成为最优二叉树,必须使两个结点都成为叶子结点。
  设 n=k 时成立,则当 n=k+1 时,要使其成为最优,必须用 k 个结点的哈夫曼树与第 k+1 个结点组成一个新的最优二叉树,所以 n=k+1 时也成立。
 
 
第三题
A:1010  B:00  C:10000  D:1001  E:11  F:10001  G:01  H:1011
第三题答案

 
 
第四题

#define MAX_TREE_SIZE 100	//二叉树的最大结点数 
typedef TElemType SqBiTree[MAX_TREE_SIZE];//1号单元存储根结点 

void PreOrder_Sq(SqBiTree BT){//先序遍历二叉树BT 
	InitStack(S);
	p = 1;//p指示当前结点的位置 
	while(p<=n && !StackEmpty(S)){
		while(p <= n){
			Visit(BT[p]);
			Push(S, p);
			p = 2 * p;
		}
		if(!StackEmpty(S)){//栈非空,遍历右子树 
			Pop(S, p);
			p = p * 2 + 1;
		}
	}
}

void PreOrder_Sq_Recursion(SqBiTree BT, int t, int n){//先序遍历二叉树(递归) 
	if(t <= n){
		Visit[BT[t]];
		PreOrder_Sq_Recursion(BT, t*2, n);
		PreOrder_Sq_Recursion(BT, t*2+1, n);
	}
}

 
 
第五题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

int LeadCount_BiTree(BiTree T){//求二叉树中叶子结点的数目 
	if(!T)//空树,没有叶子 
		return 0;
	else
		if(!T->lchild && !T->rchild)//叶子结点 
			return 1;
		else//左、右子树的叶子数相加 
			return LeadCount_BiTree(T->lchild) + LeadCount_BiTree(T->rchild);
} 

 
 
第六题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

int NodeCount_BiTree(BiTree T){//求二叉树中所有结点的数目 
	if(!T)//空树,没有叶子 
		return 0;
	else{
		int num1 = NodeCount_BiTree(T->lchild);
		int num2 = NodeCount_BiTree(T->rchild);
		return num1 + num2 + 1;
	}
} 

 
 
第七题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

int BranchCount_BiTree(BiTree T){//求二叉树中所有单分支结点数目 
	if(!T)//空树,没有叶子 
		return 0;
	else{
		int num1 = NodeCount_BiTree(T->lchild);
		int num2 = NodeCount_BiTree(T->rchild);
		if((!T->lchild && T->rchild) || (T->lchild && !T->rchild))
			return num1 + num2 + 1;
		else
			return num1 + num2;
	}
} 

 
 
第八题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

void Revolute_BiTree(BiTree T){//交换所有结点的左右子树 
	Swap(T->lchild, T->rchild);//交换左右子树 
	if(T->lchild)
		Revolute_BiTree(T->lchild);//左子树交换自己的左右子树 
	if(T->rchild)
		Revolute_BiTree(T->rchild);//右子树交换自己的左右子树 
}

 
 
第九题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

int Get_Sub_Depth(BiTree T, int x){//求二叉树中以值为x的结点为根的子树深度 
	if(T->data == x){
		printf(Get_Depth(T));//找到了值为x的结点,求其深度 
		return 0;
	}
	else{
		if(T->lchild)//在左子树中继续寻找 
			Get_Sub_Depth(T->lchild, x);
		if(T->rchild)//在右子树中继续寻找 
			Get_Sub_Depth(T->rchild, x);
	}
}

int Get_Depth(BiTree T){//求子树深度的递归算法 
	if(!T)
		return 0;
	else{
		m = Get_Depth(T->lchild);
		n = Get_Depth(T->rchild);
		return (m>n?m:n) + 1;
	}
}

 
 
第十题

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

void NR_PreOrder(BiTree bt){//非递归先序遍历二叉树 
	InitStack(S);
	if(!bt)
		return;
	p = bt;
	while(p || !StackEmpty(S)){
		while(p){//到达最左边的结点 
			Visit(p->data);//访问结点的数据域 
			Push(S, p);//将当前指针p压栈 
			p = p->lchild;//指针指向p的左孩子 
		}
		if(!StackEmpty(S)){
			Pop(S, p);//从栈中弹出栈顶元素 
			p = p->rchild;//指针指向p的右孩子结点 
		}
	}
}

 
 
第十一题

void CreateBiTree2(BiTree &T,char str[]){//建立二叉树链表(非递归)广义表 
	
	BiTree S[1000];//存放父节点 
	int flag = 0;//存入为左孩子还是右孩子,1为左孩子,2为右孩子 
	int k = 0;//记入前一个父节点的存放位置 
	BiTree p;
	
	
	p = (BiTree)malloc(sizeof(BiTNode));//申请根节点的空间 
	p->data = str[0];
	p->lchild = NULL;
	p->rchild = NULL;
	
	int i;
	for(i=1;str[i] != '#';i++){
		//printf("1\n");
		if(str[i] == '('){//将父节点存放入数组中,并且下一个存放左孩子 
			flag = 1;
			S[k++] = p;
		}
		else if(str[i] == ','){//下一个为存放右孩子 
			flag = 2;
		}
		else if(str[i] == ')'){//将一个父节点出数组 
			k--;
		}
		else{
			
			p = (BiTree)malloc(sizeof(BiTNode));//申请一个孩子的空间 
			p->data = str[i];
			p->lchild = NULL;
			p->rchild = NULL;
			if(flag == 1){//左孩子 
				S[k-1]->lchild = p;
			}
			else if(flag == 2){//右孩子 
				S[k-1]->rchild = p;
			}
		}
	}
	T = S[0];//将根返回 
}

void Level_Order(BiTree BT){//按层次、非递归遍历二叉树 
	if(!BT)
		return;
	InitQueue(Q);
	p = BT;//初始化 
	EnQueue(Q, p);//访问根结点,并将根结点入队 
	while(!QueueEmpty(Q)){//当队非空时重复执行下列操作 
		DeQueue(Q, p);//出队 
		Visit(p);
		if(!p->lchild)
			EnQueue(Q, p->lchild);//处理左孩子 
		if(!p->rchild)
			EnQueue(Q, p->rchild);//处理右孩子 
	}
}

 
 
第十二题
  假设此二叉树总结点数为 n,度为 0 的结点数为 n0,度为 1 的结点数为 n1,度为 2 的结点数为 n2,则:n = n0 + n1 + n2(1)
  在二叉树中,除了根结点外,其余结点都有一个分支进入,设 B 为分支总数,则:n = B + 1,由于这些分支是由度为 1 或度为 2 的结点射出的,所以有:B = n1 + 2n2,于是有:n = n1 + 2n2 + 1(2)
  由(1)和(2)可得:n0 = n2 + 1
  证毕。
 
 
第十三题
  假设此二叉树的深度为 k,根据二叉树性质 2 及完全二叉树的定义得到:2k-1-1<n≤2k-1,即 2k-1≤n<2k
  于是 k-1≤log2n<k
  即 k = ⌊log2n⌋ + 1。
 
 
第十四题
(1)第 H 层上的结点数目为 kH-1

(2)如果 p 是其双亲的最小的孩子(右孩子),则 p 减去根结点的一个结点,应是 k 的整数倍,该整数即为所在的组数,每一组为一棵满 k 叉树,正好应为双亲结点的编号。如果 p 是其双亲的最大的孩子(左孩子),则 p+k-1 为其最小的弟弟,再减去一个根结点,除以 k,即为其双亲结点的编号。
  综合来说,对于 p 是左孩子的情况,i=(p+k-1)/k;对于 p 是右孩子的情况,i=(p-1)/k,如果左孩子的编号为 p,则其右孩子编号必为 p+k-1 ,所以,其双亲结点的编号为 i=⌊(p+k-2)/k⌋,向下取整。

(3)结点 p 的右孩子的编号为 kp+1,左孩子的编号为 kp+1-k+1=k(p-1)+2,第 i 个孩子的编号为 k(p-1)+2+i-1=kp-k+i+1

(4)当(p-1)%k != 0时,结点 p 有右兄弟,其右兄弟的编号为 p+1
 
 
第十五题
  根据树的定义,在一棵树中,除树根结点外,每个结点有且仅有一个前驱结点,也就是说,每个结点与指向它的一个分支一一对应,所以除树根结点之外的结点树等于所有结点的分支数,即度数,从而可得树中的结点数等于所有结点的度数加 1。总结点数为:1+n1+2n2+3n3+…+knk
  度为 0 的结点数应为总结点数减去度不为 0 的结点数的总和,即
n0 = 1+n1+2n2+3n3+…+knk-(n1+n2+n3+…+nk)
n 0 = 1 + ∑ i = 1 k ( i − 1 ) n i n_{0}=1+\sum ^{k}_{i=1}\left( i-1\right) n_{i} n0=1+i=1k(i1)ni
 
 
第十六题
第十六题答案

 
 
第十七题
第十七题答案

 
 
第十八题
第十八题答案

 
 

第一题
(1)
ID(1)=2, OD(1)=0;  ID(2)=1, OD(2)=3;  ID(3)=2, OD(3)=0;  
ID(4)=1, OD(4)=2;  ID(5)=0, OD(5)=2;  ID(6)=2, OD(6)=1;  
(2)
邻接矩阵
(3)
邻接表
(4)
逆邻接表
(5)
五个强连通分量:V1、V2、V3、V4、V5
 
 
第二题
第二题答案

 
 
第三题
数学归纳法
  当 n=2 时,图G连通只需一条边,命题成立。
  设 n=k(k>2) 时命题成立,即当 n=k 时,图G至少有 k-1 条边。
  当 n=k+1(k2) 时,由上述假设可知,图G中已有 k 个顶点是连通的,且至少有 k-1 条边。现增加一个顶点 p 后,若 p 与图中已有的 k 个顶点没有边,显然 p 与图 G 不连通。若要求 k+1 个顶点组成的图是连通图,p 必须与图 G 原有的 k 个顶点中的任何一个顶点有边相连,即 k 条边就可使图连通。因此,当 n=k+1 时,图 G 至少有 k 条边,命题成立。证毕。

 
 
第四题
普里姆(Prim)算法
普里姆算法1
普里姆算法2
克鲁斯卡尔(Kruskal)算法
克鲁斯卡尔算法1
克鲁斯卡尔算法2

 
 
第五题
第五题答案

 
 
第六题
(1)
第六题答案1
(2)
关键活动:<1, 3>、❤️, 2>、<2, 5>、<5, 6>,这些关键活动中任何一个提前完成,整个工程就能提前完成。
第六题答案2

 
 
第七题
关键路径只有一条:(α,G,H,K,J,E,ω)
第八题答案

 
 
第八题
第八题答案

 
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值