数据结构试题及评分解析

1.设A=(a1,a2,…,an),B=(b1,b2,…,bm)是两个递增有序的线性表(其中n、m均大于1),且所有数据元素均不相同。假设A、B均采用带头节点的单链表存放,设计一个尽可能高效的算法判断B是否为A的一个子序列,并分析你设计的算法的时间复杂度和空间复杂度。(15分)
1.(15分)解:采用二路归并思路,用p、q分别扫描有序单链表A、B,先找到第一个两者值相等的节点,然后在两者值相等时同步后移,如果B扫描完毕返回1,否则返回0。对应的算法如下:

int SubSeq(LinkList *A,LinkList *B)
{	LinkList *p=A->next,*q=B->next;
	while (p!=NULL && q!=NULL)	//找两个单链表中第一个值相同的节点
	{	if (p->data<q->data)
			p=p->next;
		else if (p->data>q->data)
			q=q->next;
		else
			break;
	}
	while (p!=NULL && q!=NULL && p->data==q->data)
	{	//当两者值相等时同步后移
		p=p->next;
		q=q->next;
	}
	if (q==NULL)			//当B中节点比较完毕返回1
		return 1;
	else					//否则返回0
		return 0;
}

本算法的时间复杂度为O(m+n),空间复杂度为O(1)。其中m、n分别为A、B单链表的长度。

2.假设二叉树b采用二叉链存储结构存储,试设计一个算法,输出该二叉树中从根节点出发的第一条最长的路径长度,并输出此路径上各节点的值。并分析你设计的算法的时间复杂度和空间复杂度。(15分)
3.2.(15分)解:由于二叉树中最长路径一定是从根节点到某个叶子节点的路径,可以求出所有叶子节点到根节点的逆路径,通过比较长度得出最长路径,可以采用多种解法。算法中用形参maxpath[]数组存放最长路径,maxpathlen存放最长路径长度。
解法1:对应的算法如下:

void MaxPath(BTNode *b,ElemType maxpath[],int &maxpathlen)
{	//maxpathlen的初值为0
	struct snode
	{	BTNode *node;			//存放当前节点指针
		int parent;				//存放双亲节点在队列中的位置
	} Qu[MaxSize];				//定义非循环队列
	ElemType path[MaxSize];		//存放一条路径
	int pathlen;				//存放一条路径长度
	int front,rear,p,i;			//定义队头和队尾指针
	front=rear=-1;				//置队列为空队列
	rear++;
	Qu[rear].node=b;			//根节点指针进进队
	Qu[rear].parent=-1;			//根节点没有双亲节点
	while (front<rear)			//队列不为空
	{	front++;
		b=Qu[front].node;		//队头出队列
		if (b->lchild==NULL && b->rchild==NULL)	//*b为叶子节点
		{	p=front;
			pathlen=0;
			while (Qu[p].parent!=-1)
			{	path[pathlen]=Qu[p].node->data;
				pathlen++;
				p=Qu[p].parent;
			}
			path[pathlen]=Qu[p].node->data;
			pathlen++;
			if (pathlen>maxpathlen)	//通过比较求最长路径
			{	for (i=0;i<pathlen;i++)
					maxpath[i]=path[i];
				maxpathlen=pathlen;
			}
		}
		if (b->lchild!=NULL)		//左孩子节点进队
		{	rear++;
			Qu[rear].node=b->lchild;
			Qu[rear].parent=front;
		}
		if (b->rchild!=NULL)		//右孩子节点进队
		{	rear++;
			Qu[rear].node=b->rchild;
			Qu[rear].parent=front;
		}
	}
}

本算法的时间复杂度为O(n),空间复杂度为O(n)。

  1. 假设一个无向图是非连通的,采用邻接表作为存储结构,试设计一个算法,输出图中各连通分量的节点序列。(10分)
  2. (10分)解:采用深度优先搜索遍历非连通图,并输出各连通分量节点序列的算法如下:
    int visited[MAXV]={0};
    DFSGraph(AGraph *G)
    { int i,j=1; //用j记录连通分量的序号
    for (i=0;in;i++)
    if (visited[i]==0)
    { printf(“第%d个连通分量节点序列:”,j++);
    DFS(G,i); //调用前面的深度优先遍历算法
    }
    }
    采用广度优先搜索遍历非连通图,并输出各连通分量节点序列的算法如下:
    int visited[MAXV]={0};
    BFSGraph(AGraph *G)
    { int i,j=1; //用j记录连通分量的序号
    for (i=0;in;i++)
    if (visited[i]==0)
    { printf(“第%d个连通分量节点序列:”,j++);
    BFS(G,i); //调用前面的广度优先遍历算法
    }
    }

题3
1.设A和B是两个结点个数分别为m和n的单链表(带头结点),其中元素递增有序。设计一个尽可能高效的算法求A和B的交集,要求不破坏A、B的结点,将交集存放在单链表C中。给出你所设计的算法的时间复杂度和空间复杂度。

  1. 设A和B是两个结点个数分别为m和n的单链表(带头结点),其中元素递增有序。设计一个尽可能高效的算法求A和B的交集,要求不破坏A、B的结点,将交集存放在单链表C中。给出你所设计的算法的时间复杂度和空间复杂度。
    解:算法如下:
void insertion(LinkList *A,LinkList *B,LinkList *&C)
{	LinkList *p=A->next,*q=B->next,*s,*t;
	C=(LinkList *)malloc(sizeof(LinkList));
	t=C;
	while (p!=NULL && q!=NULL)
	{	if (p->data==q->data)
		{	s=(LinkList *)malloc(sizeof(LinkList));
			s->data=p->data;
			t->next=s;
			t=s;
			p=p->next;
			q=q->next;
		}
		else if (p->data<q->data)
			p=p->next;
		else
			q=q->next;
	}
	t->next=NULL;
}

算法的时间复杂度为O(m+n),空间复杂度为O(MIN(m,n))。
【评分说明】算法为8分,算法的时间复杂度和空间复杂度各占1分。

  1. 假设二叉树b采用二叉链存储结构,设计一个算法void findparent(BTNode *b,ElemType x,BTNode *&p)求指定值为x的结点(假设这样的结点是唯一的)的双亲结点地址p,提示,根结点的双亲为NULL,若在b中未找到值为x的结点,p亦为NULL。
  2. 假设二叉树b采用二叉链存储结构,设计一个算法void findparent(BTNode *b,ElemType x,BTNode *&p)求指定值为x的结点的双亲结点p,提示,根结点的双亲为NULL,若未找到这样的结点,p亦为NULL。
    解:算法如下:
void findparent(BTNode *b,ElemType x,BTNode *&p)
{	if (b!=NULL)
	{	if (b->data==x) p=NULL;
		else if (b->lchild!=NULL && b->lchild->data==x)
			p=b;
		else if (b->rchild!=NULL && b->rchild->data==x)
			p=b;
		else
		{	findparent(b->lchild,x,p);
			if (p==NULL)
				findparent(b->rchild,x,p);
		}
	}
	else p=NULL;
}

【评分说明】本题有多种解法,相应给分。

题4
1.用单链表来存储集合,并假设这样的单链表中的节点递增有序,设计一个尽可能高效的算法求两个集合A和B的并集C。设A、B中分别有m和n个元素,分析你设计的算法的时间和空间复杂度。(15分)

  1. 解:由于单链表是递增有序的,可以采用归并算法提高求并集的效率,结果并集C采用尾插法建表。对应算法如下:
void Unionset(LinkList *A,LinkList *B,LinkList *&C)
{	LinkList *pa=A->next,*pb=B->next,*s,*r;
	C=(LinkList *)malloc(sizeof(LinkList));	//建立C的头节点
	r=C;										//r始终指向单链表C的尾节点
	while (pa!=NULL && pb!=NULL)
	{	if (pa->data<pb->data)					//仅复制*pa节点
		{	s=(LinkList *)malloc(sizeof(LinkList));
			s->data=pa->data;
			r->next=s; r=s;
			pa=pa->next;
		}
		else if (pa->data>pb->data)				//仅复制*pb节点
		{	s=(LinkList *)malloc(sizeof(LinkList));
			s->data=pb->data;
			r->next=s; r=s;
			pb=pb->next;
		}
		else
		{	s=(LinkList *)malloc(sizeof(LinkList));
			s->data=pa->data;
			r->next=s; r=s;
			pa=pa->next;
			pb=pb->next;
		}
	}
	while (pa!=NULL)				//复制A单链表的余下节点
	{	s=(LinkList *)malloc(sizeof(LinkList));
		s->data=pa->data;
		r->next=s; r=s;
		pa=pa->next;
	}
	while (pb!=NULL)				//复制B单链表的余下节点
	{	s=(LinkList *)malloc(sizeof(LinkList));
		s->data=pb->data;
		r->next=s; r=s;
		pb=pb->next;
	}
	r->next=NULL;
}

本算法的时间复杂度为O(m+n),空间复杂度为O(m+n),其中m、n分别为A、B单链表中的节点个数。
评分:算法占11分,时间和空间复杂度各占2分。

2.假设二叉树采用二叉链存储结构进行存储,假设每个节点值为单个字符且所有节点值不同,设计一个算法,输出二叉树b中第k的所有节点值,并分析你所设计算法的时间复杂度。(15分)
2. 解:采用先序序列,对应的算法如下:

void Dispk(BTNode *b,int k)
{
	Dispk1(b,k,1);
}
void Dispk1(BTNode *b,int k,int h)
{
	if (b!=NULL)
	{
		if (h==k) printf(%c “,b->data);
		Dispk1(b->lchild,k,h+1);
		Dispk1(b->rchild,k,h+1);
}
}

评分:采用先序、中序、后序和层次遍历均可,算法占13分,时间复杂度占2分。

说明:以上两题求算法的时间和空间复杂度只需给出结果,不必给出详细步骤。

  1. 图的邻接表是一种图的链式存储结构,请给出邻接表的完整类型定义。采用你所设计的邻接表作为存储结构,设计一个算法,求出无向图G的连通分量个数。(10分)
3. 解:邻接表的类型定义如下:
typedef char ElemType;
typedef int InfoType;
typedef struct ANode				//边的节点结构类型
{	int adjvex;						//该边的终点位置
	struct ANode *nextarc;			//指向下一条边的指针
	InfoType info;					//该边的相关信息,对于带权图可存放权值
} ArcNode;
typedef struct Vnode				//邻接表头节点的类型
{	ElemType data;					//顶点信息
	ArcNode *firstarc;				//指向第一条边
} VNode;
typedef VNode AdjList[MAXV];		//AdjList是邻接表类型
typedef struct
{	AdjList adjlist;				//邻接表
	int n,e;						//分别为图中顶点数和边数
} AGraph;							//声明图的邻接表类型
采用遍历方式求无向图G中连通分量个数。基于深度优先遍历的算法如下:
int visited[MAXV]={0};
int Getnum(AGraph *G)			//求图G的连通分量
{	int i,n=0,visited[MAXVEX];
	for (i=0;i<G->n;i++)
		if (visited[i]==0)
		{	DFS(G,i);			//对图G从顶点i开始进行深度优先遍历
			n++;				//调用DFS的次数即为连通分量数
		}
	return n;
}
基于广度优先遍历的算法如下:
int visited[MAXV]={0};
int Getnum1(AGraph *G)			//求图G的连通分量
{	int i,n=0,visited[MAXVEX];
	for (i=0;i<G->n;i++)
		if (visited[i]==0)
		{	BFS(G,i);			//对图G从顶点i开始进行广度优先遍历
			n++;				//调用BFS的次数即为连通分量数
		}
	return n;
}

评分:邻接表的类型定义占5分,求无向图G的连通分量个数的算法占5分。

第五套题

  1. (10分)某带头结点的非空单链表L中所有元素为整数,结点类型定义如下:
typedef struct node
{	int data;
	struct node *next;
} LinkNode;

设计一个尽可能高效的算法,将所有小于零的结点移到所有大于等于零的结点的前面。

  1. (10分)答案:
void Move(LinkNode *&L)
{	LinkNode *p=L->next,*pre=L;
     while (p!=NULL && p->data<0)	//跳过小于0的结点
     {      pre=p;p=pre->next;
     }
	while (p!=NULL)
	{	if (p->data<0)			//若*p结点值小于0
		{	pre->next=p->next;	//从链表中删除*p结点
			p->next=L->next;	//将*p结点插入到头结点之后
			L->next=p;
			p=pre->next;		//p指向*pre之后结点,pre不变
		}
		else					//若*p结点值不小于0
		{	pre=p;				//pre、p同步后移一个结点
			p=p->next;
		}
	}
}
  1. (15分)假设二叉树中有n个结点,每个结点值为单个字符,而且所有结点值均不相同,采用二叉链存储结构存储,其结点类型定义如下:
typedef struct node
{    char data;
     struct node *lchild,*rchild;
} BTNode;

请完成以下任务:
(1)设计一个算法,在二叉树b中查找x结点(指结点值为x的结点),若找到该结点,返回其地址,否则返回NULL。给出你设计的算法的时间复杂度。(8分)
(2)设计一个算法,利用(1)小题设计的算法输出二叉树b中x结点的所有子孙结点值。(7分)
2. (15分)答案:
(1)(7分)

BTNode *Findx(BTNode *b,char x)	//在二叉树b中查找x结点
{	BTNode *p;
	if (b==NULL) 
		return NULL;
	else
	{ 	if (b->data==x)
			return b;
		p=Findx(b->lchild,x);
		if (p!=NULL)
			return p;
		return Findx(b->rchild,x);
	}
}

算法的时间复杂度为O(n)(1分)。
(2)(7分)

void Sons(BTNode *b,char x)		//输出x结点的子孙,初始时b指向x结点
{	if (b!=NULL)
	{	if (b->data!=x)
			printf("%c ",b->data);
		Sons(b->lchild,x);
		Sons(b->rchild,x);
	}
}
void OutSons(BTNode *b,char x)	//输出二叉树b中x结点的所有子孙结点值
{	BNode *p= Findx(b,x);
	if (p!=NULL)
		Sons(p,x);
}

题6

1.设计一个算法delminnode(LinkList *&L),在带头结点的单链表L中删除所有结点值最小的结点(可能有多个结点值最小的结点)。

  1. 解:用p从头至尾扫描单链表,pre指向p结点的前驱,用minp保存值最小的结点指针,minpre指向minp结点的前驱。一面扫描,一面比较,将最小值的结点放到*minp中。算法如下:
void delminnode(LinkList *&L)
{
	LinkList *pre=L,*p=pre->next,*minp=p,*minpre=pre;
	ElemType mindata=p->data;
	while (p!=NULL && p->data<mindata)
	{	mindata=p->data;
		p=p->next;
	}
	p=pre->next;
	while (p!=NULL)	
	{
		if (p->data==mindata)
		{	pre->next=p->next;
			free(p);
		}
		pre=pre->next;
		p=pre->next;
	}
}

评分标准:根据算法的正确性评分,不考虑算法的时间复杂度。

2.假设二叉树采用二叉链存储结构存储,设计一个算法copy(BTNode *b,BTNode *&t),由二叉树b复制成另一棵二叉树t。
2.解:递归算法如下:

void copy(BTNode *b,BTNode *&t)
{
	BTNode *l,*r;
	if (b==NULL) t=NULL;
	else
	{
		t=(BTNode *)malloc(sizeof(BTNode));
		copy(b->lchild,l);
		copy(b->rchild,r);
		t->lchild=l;
		t->rchild=r;
	}
}

评分标准:根据算法的正确性评分,不考虑算法的时间复杂度。

  1. 假设一个无向图是非连通的,采用邻接表作为存储结构,试设计一个算法,输出图中各连通分量的节点序列。
  2. 解:采用深度优先搜索遍历非连通图,并输出各连通分量节点序列的算法如下:
DFSGraph(AGraph *G)
{	int i,j=1;							//用j记录连通分量的序号
	for (i=0;i<G->n;i++)
		if (visited[i]==0)
		{	printf("第%d个连通分量节点序列:",j++);
			DFS(G,i);					//调用深度优先遍历算法
		}
}

题9

  1. (15分)有两个递增有序表,所有元素为整数,均采用带头结点的单链表存储,结点类型定义如下:
typedef struct node
{	int data;
	struct node *next;
} LinkNode;

设计一个尽可能高效的算法,将两个递增有序单链表ha、hb合并为一个递减有序单链表hc,要求算法空间复杂度为O(1)。

  1. (15分)参考算法如下:
void merge(LinkNode *ha, LinkNode *hb, LinkNode *&hc)
{	LinkNode *pa=ha->next,*pb=hb->next,*q;
	free(hb);
	hc=ha; hc->next=NULL;			//hc利用ha的头结点,并设置为空
	while (pa!=NULL && pb!=NULL)	//扫描ha、hb的数据结点
	{	if (pa->data<pb->data)		//将较小结点采用头插法插入到hc中
		{	q=pa->next;
			pa->next=hc->next;
			hc->next=pa;
			pa=q;
		}
		else
		{	q=pb->next;
			pb->next=hc->next;
			hc->next=pb;
			pb=q;
		}
	}
	if (pb!=NULL) pa=pb;
	while (pa!=NULL)			//将没有扫描完的结点采用头插法插入到hc中
	{	q=pa->next;
		pa->next=hc->next;
		hc->next=pa;
		pa=q;
	}
}

评分说明:上述算法的时间复杂度为O(m+n)。若设计的算法时间复杂度为O(m*n),至少扣3分,若设计的算法空间复杂度不为O(1),扣2分。

2、(10分)设二叉树中每个结点存放单个字符,其结点类型如下:

typedef struct node
{   char  data;
    struct node *lchild,*rchild;
} BTNode;

设计一个算法求其中单分支的结点个数。
2、(10分)参考算法如下:

int singleodes(BTNode *b)
{	if (b==NULL) return 0; 
    	if ((b->lchild==NULL && b->rchild!=NULL) ||	//单分支的结点
		(b->lchild!=NULL && b->rchild==NULL)
		return singleodes(b->lchild)+ singleodes(b->rchild)+1;
	else
		return singleodes(b->lchild)+ singleodes(b->rchild);

评分说明:可以采用任意一种遍历方法。判断单分支的结点为3分。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yoo前端

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值