期末复习数据结构与算法练习题

Posted 神的孩子都在歌唱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了期末复习数据结构与算法练习题相关的知识,希望对你有一定的参考价值。

数据结构与算法考试习题

第一章 求算法复杂度与频率

设n为正整数,试确定下列各程序中前置以记号@的语句的频度和算法复杂度。
(1) i=1; k=0;
while( i <= n-1)
k+=10*i; @ n-1
i++;

分析:T(n)=O(n)

(2) i=1; k=0;
do
k+=10*i; @ n-1
i++;
while((i<=n-1);
分析:T(n)=O(n)

(3) i=1; k=0;
while(i<=n-1)
i++;
k+=10*i; @ n-1

分析:T(n)=O(n)

(4) k=0;
for(i=1; i<=n; i++)
for(j=i; j<=n; j++)
k++; @ n(n+1)/2

分析: T ( n ) = O ( n 2 ) T(n)= O(n^2) T(n)=O(n2)

(5) for(i=1; i<=n; i++)

for(j=1;j<=i; j++)

for(k=1; k<=j; k++)
X += delta; @ n(n+1)(n+2)/6


分析: T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)

(6) i=1; j=0;
while( i+j <= n)

if(i>j) @ n
j++;
else
i++;

分析:
  通过分析以上程序段,可将i+j看成一个控制循环次数的变量,且每执行一次循环,i+j的值加1。该程序段的主要时间消耗是while循环,而while循环共做了n次,所以该程序段的执行时间为:T(n)=O(n)

第二章 线性表

2.2 填空题。

(1) 在顺序表中插入或删除一个元素,需要平均移动 元素,具体移动的元素个数与 有关。
(2) 顺序表中逻辑上相邻的元素的物理位置 紧邻。单链表中逻辑上相邻的元素的物理位置 紧邻。
(3) 在单链表中,除了首元结点外,任一结点的存储位置由 指示。
(4) 在单链表中设置头结点的作用是 。

1、在顺序表中插入或删除一个元素,需要平均移动(表中一半)元素,具体移动的元素个数与(表长和该元素在表中的位置)有关。
2、顺序表中逻辑上相邻的元素的物理位置(必定)相邻。单链表中逻辑上相邻的元素的物理位置(不一定)相邻。
3、在单链表中,除了首元结点外,任一结点的存储位置由(其直接前驱结点的链域的值)指示。
4、在单链表中设置头结点的作用是(插入和删除元素不必进行特殊处理)。

2.3何时选用顺序表、何时选用链表作为线性表的存储结构为宜?

答:在实际应用中,应根据具体问题的要求和性质来选择顺序表或链表作为线性表的存储结构,通常有以下几方面的考虑:
 1.基于空间的考虑。当要求存储的线性表长度变化不大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表;反之,当线性表长度变化大,难以估计其存储规模时,采用动态链表作为存储结构为好。
 2.基于时间的考虑。若线性表的操作主要是进行查找,很少做插入和删除操作时,采用顺序表做存储结构为宜;反之, 若需要对线性表进行频繁地插入或删除等的操作时,宜采用链表做存储结构。并且,若链表的插入和删除主要发生在表的首尾两端,则采用尾指针表示的单循环链表为宜。

2.7 已知L是带表头结点的非空单链表,且P结点既不是首元结点,也不是尾元结点,试从下列提供的答案中选择合适的语句序列。

a. 删除P结点的直接后继结点的语句序列是____________________。
b. 删除P结点的直接前驱结点的语句序列是____________________。
c. 删除P结点的语句序列是____________________。
d. 删除首元结点的语句序列是____________________。

e. 删除尾元结点的语句序列是____________________。
(1) P=P->next;
(2) P->next=P;
(3) P->next=P->next->next;
(4) P=P->next->next;
(5) while(P!=NULL) P=P->next;
(6) while(Q->next!=NULL) P=Q; Q=Q->next;
(7) while(P->next!=Q) P=P->next;
(8) while(P->next->next!=Q) P=P->next;
(9) while(P->next->next!=NULL) P=P->next;
(10) Q=P;
(11) Q=P->next;
(12) P=L;
(13) L=L->next;
(14) free(Q);

解:a. (11) (3) (14)
b. (10) (12) (8) (3) (14)
c. (10) (12) (7) (3) (14)
d. (12) (11) (3) (14)
e. (9) (11) (3) (14)

2.8 已知P结点是某双向链表的中间结点,试从下列提供的答案中选择合适的语句序列。

a. 在P结点后插入S结点的语句序列是_______________________。
b. 在P结点前插入S结点的语句序列是_______________________。
c. 删除P结点的直接后继结点的语句序列是_______________________。
d. 删除P结点的直接前驱结点的语句序列是_______________________。
e. 删除P结点的语句序列是_______________________。
(1) P->next=P->next->next;
(2) P->priou=P->priou->priou;
(3) P->next=S;
(4) P->priou=S;
(5) S->next=P;
(6) S->priou=P;
(7) S->next=P->next;
(8) S->priou=P->priou;
(9) P->priou->next=P->next;
(10) P->priou->next=P;
(11) P->next->priou=P;
(12) P->next->priou=S;
(13) P->priou->next=S;
(14) P->next->priou=P->priou;
(15) Q=P->next;
(16) Q=P->priou;
(17) free§;
(18) free(Q);
解:a. (7) (6) (12)(3) (12和3的位置不能反,反了就错了)
b. (8) (5) (13)(4)
c. (15) (1) (11) (18)
d. (16) (2) (10) (18)
e. (14) (9) (17)

2.11 设顺序表va中的数据元素递增有序。试写一算法,将x插入到顺序表的适当位置上,以保持该表的有序性。

解:

Status InsertOrderList(SqList &va,ElemType x)

	//在非递减的顺序表va中插入元素x并使其仍成为顺序表的算法
	int i;
	if(va.length==va.listsize)return(OVERFLOW);
	for(i=va.length;i>0,x<va.elem[i-1];i--)
		va.elem[i]=va.elem[i-1];
	va.elem[i]=x;
	va.length++;
	return OK;

其他写法:
答:因已知顺序表L是递增有序表,所以只要从顺序表终端结点(设为i位置元素)开始向前寻找到第一个小于或等于x的元素位置i后插入该位置即可。
  在寻找过程中,由于大于x的元素都应放在x之后,所以可边寻找,边后移元素,当找到第一个小于或等于x的元素位置i时,该位置也空出来了。
  算法如下:

//顺序表存储结构如题2.7
    void InsertIncreaseList( Seqlist *L , Datatype x )
      
      int i;
      if ( L->length>=ListSize)
       Error(“overflow");
      for ( i=L -> length ; i>0 && L->data[ i-1 ] > x ; i--)
       L->data[ i ]=L->data[ i-1 ] ; // 比较并移动元素
      L->data[ i ] =x;
      L -> length++;
     

2.21 试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表 逆置为 。

解:

// 顺序表的逆置
Status ListOppose_Sq(SqList &L)

	int i;
	ElemType x;
	for(i=0;i<L.length/2;i++)
		x=L.elem[i];
		L.elem[i]=L.elem[L.length-1-i];
		L.elem[L.length-1-i]=x;
	
	return OK;

其他写法:
答:
  要将该表逆置,可以将表中的开始结点与终端结点互换,第二个结点与倒数第二个结点互换,如此反复,就可将整个表逆置了。算法如下:

// 顺序表结构定义同上题

 void ReverseList( Seqlist *L)
  
   DataType temp ; //设置临时空间用于存放data
   int i;
   for (i=0;i<L->length/2;i++)//L->length/2为整除运算
     temp = L->data[i]; //交换数据
     L -> data[ i ] = L -> data[ L -> length-1-i];
     L -> data[ L -> length - 1 - i ] = temp;
    
  

2.24 假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表)的结点空间构造C表。

解:

// 将合并逆置后的结果放在C表中,并删除B表
Status ListMergeOppose_L(LinkList &A,LinkList &B,LinkList &C)

	LinkList pa,pb,qa,qb;
	pa=A;
	pb=B;
	qa=pa;	// 保存pa的前驱指针
	qb=pb;	// 保存pb的前驱指针
	pa=pa->next;
	pb=pb->next;
	A->next=NULL;
	C=A;
	while(pa&&pb)
		if(pa->data<pb->data)
			qa=pa;
			pa=pa->next;
			qa->next=A->next;	//将当前最小结点插入A表表头
			A->next=qa;
		
		else
			qb=pb;
			pb=pb->next;
			qb->next=A->next;	//将当前最小结点插入A表表头
			A->next=qb;
		
	
	while(pa)
		qa=pa;
		pa=pa->next;
		qa->next=A->next;
		A->next=qa;
	
	while(pb)
		qb=pb;
		pb=pb->next;
		qb->next=A->next;
		A->next=qb;
	
	pb=B;
	free(pb);
	return OK;

其他写法:
解:

void reverse_merge(LinkList &A,LinkList &B,LinkList &C)//把元素递增排列的链表A和B合并为C,且C中元素递减排列,使用原空间

  pa=A->next;pb=B->next;pre=NULL; //pa和pb分别指向A,B的当前元素
  while(pa||pb)
  
    if(pa->data<pb->data||!pb)
    
      pc=pa;q=pa->next;pa->next=pre;pa=q; //将A的元素插入新表
    
    else
    
      pc=pb;q=pb->next;pb->next=pre;pb=q; //将B的元素插入新表
    
    pre=pc;
  
  C=A;A->next=pc; //构造新表头
//reverse_merge

分析:本算法的思想是,按从小到大的顺序依次把A和B的元素插入新表的头部pc处,最后处理A或B的剩余元素.

2.31 假设某个单向循环链表的长度大于1,且表中既无头结点也无头指针。已知s为指向链表中某个结点的指针,试编写算法在链表中删除指针s所指结点的前驱结点。

解:

// 在单循环链表S中删除S的前驱结点
Status ListDelete_CL(LinkList &S)

	LinkList p,q;
	if(S==S->next)return ERROR;
	q=S;
	p=S->next;
	while(p->next!=S)
		q=p;
		p=p->next;
	
	q->next=p->next;
	free(p);
	return OK;

其他写法:
解:
  已知指向这个结点的指针是s,那么要删除这个结点的直接前趋结点,就只要找到一个结点,它的指针域是指向s的直接前趋,然后用后删结点法,将结点*s的直接前趋结点删除即可。

算法如下:

 void DeleteNode( ListNode *s)
    //删除单循环链表中指定结点的直接前趋结点
      ListNode *p, *q;
      p=s;
      while( p->next->next!=s) 
       p=p->next;
      //删除结点
      q=p->next;
      p->next=q->next;
      free(p); //释放空间
    

注意:
  若单循环链表的长度等于1,则只要把表删空即可。

2.32 已知有一个单向循环链表,其每个结点中含三个域:pre,data和next,其中data为数据域,next为指向后继结点的指针域,prior也为指针域,但它的值为空(NULL),试编写算法将此单向循环链表改为双向循环链表,即使prior成为指向前驱结点的指针域。

解:

// 建立一个空的循环链表
Status InitList_DL(DuLinkList &L)

	L=(DuLinkList)malloc(sizeof(DuLNode));
	if(!L) exit(OVERFLOW);
	L->pre=NULL;
	L->next=L;
	return OK;

	// 向循环链表中插入一个结点
Status ListInsert_DL(DuLinkList &L,ElemType e)

	DuLinkList p;
	p=(DuLinkList)malloc(sizeof(DuLNode));
	if(!p) return ERROR;
	p->data=e;
	p->next=L->next;
	L->next=p;
	return OK;

	// 将单循环链表改成双向链表
Status ListCirToDu(DuLinkList &L)

	DuLinkList p,q;
	q=L;
	p=L->next;
	while(p!=L)
		p->pre=q;
		q=p;
		p=p->next;
	
	if(p==L) p->pre=q;
	return OK;

其他写法:
Status DuLNode_Pre(DuLinkList &L)//完成双向循环链表结点的pre域

  for(p=L;!p->next->pre;p=p->next) p->next->pre=p;
  return OK;
//DuLNode_Pre 

第3章 栈和队列

3.2 简述栈和线性表的差别。

解:线性表是具有相同特性的数据元素的一个有限序列。栈是限定仅在表尾进行插入或删除操作的线性表。

3.3 写出下列程序段的输出结果(栈的元素类型SElemType为char)。

void main()
	
		Stack S;
		char x,y;
		InitStack(S);
		x= ‘c’; y= ‘k’;
		Push(S,x);	Push(S, ‘a’);	Push(S,y);
		Pop(S,x);	Push(S, ‘t’);	Push(S,x);
		Pop(S,x);	Push(S, ‘s’);
		while(!StackEmpty(S))  Pop(S,y); printf(y); 
		printf(x);
	

解:stack
void main( )
Stack S;
Char x,y;
InitStack(S);
x=’c’;y=’k’; //x=‘c’, y=‘k’
Push(S,x); //x=‘c’,y=‘k’,S=“c”
Push(S,’a’); //x=‘c’,y=‘k’,S=“ac”
Push(S,y); //x=‘c’, y=‘k’, S=“kac”
Pop(S,x); //x=‘k’,y=‘k’,S=“ac”
Push(S,’t’); //x=‘k’,y=‘k’,S=“tac”
Push(S,x); //x=‘k’,y=‘k’,S=“ktac”
Pop(S,x); //x=‘k’,y=‘k’ S=“tac”
Push(S,’s’); //x=‘k’,y=‘k’ S=“stac”
while(!StackEmpty(S))

Pop(S,y); //依次为y=‘s’,y=‘t’,y=‘a’,y=‘c’
printf(y); //打印依次为s,t,a,c

Printf(x);//x=‘k’

所以最后应该是stack

3.12 写出以下程序段的输出结果(队列中的元素类型QElemType为char)。

void main()
	
		Queue Q;
		InitQueue(Q);
		char x= ‘e’, y= ‘c’;
		EnQueue(Q, ‘h’);
		EnQueue(Q, ‘r’);
		EnQueue(Q, y);
		DeQueue(Q, x);
		EnQueue(Q, x);
		DeQueue(Q, x);
		EnQueue(Q, ‘a’);
		While(!QueueEmpty(Q))
		
			DeQueue(Q,y);
			cout<<y;
		
		cout<<x;
	

解:char

3.28 假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点(注意不设头指针),试编写相应的队列初始化、入队列和出队列的算法。

解:

typedef int ElemType;
typedef struct NodeType
	ElemType data;
	NodeType *next;
QNode,*QPtr;
typedef struct
	QPtr rear;
	int size;
Queue;
Status InitQueue(Queue& q)

	q.rear=NULL;
	q.size=0;
	return OK;

Status EnQueue(Queue& q,ElemType e)

	QPtr p;
	p=new QNode;
	if(!p) return FALSE;
	p->data=e;
	if(!q.rear)
		q.rear=p;
		p->next=q.rear;
	
	else
		p->next=q.rear->next;
		q.rear->next=p;
		q.rear=p;
	
	q.size++;
	return OK;

Status DeQueue(Queue& q,ElemType& e)

	QPtr p;
	if(q.size==0)return FALSE;
	if(q.size==1)
		p=q.rear;
		e=p->data;
		q.rear=NULL;
		delete p;
	
	else
		p=q.rear->next;
		e=p->data;
		q.rear->next=p->next;
		delete p;
	
	q.size--;
	return OK;

其他解法:

3.13 假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素站点(注意不设头指针) ,试编写相应的置空队、判队空 、入队和出队等算法。

解:算法如下:

//先定义链队结构:
 typedef struct queuenode
   Datatype data;
   struct queuenode *next;
  QueueNode; //以上是结点类型的定义
 typedef struct
   queuenode *rear;
  LinkQueue; //只设一个指向队尾元素的指针

(1)置空队

void InitQueue( LinkQueue *Q)
    //置空队:就是使头结点成为队尾元素
    QueueNode *s;
    Q->rear = Q->rear->next;//将队尾指针指向头结点
    while (Q->rear!=Q->rear->next)//当队列非空,将队中元素逐个出队
     s=Q->rear->next;
      Q->rear->next=s->next;
      free(s);
     //回收结点空间
   

(2)判队空

int EmptyQueue( LinkQueue *Q)
    //判队空
    //当头结点的next指针指向自己时为空队
    return Q->rear->next->next==Q->rear->next;
   

(3)入队

void EnQueue( LinkQueue *Q, Datatype x)
    //入队
    //也就是在尾结点处插入元素
    QueueNode *p=(QueueNode *) malloc (sizeof(QueueNode));//申请新结点
    p->data=x; p->next=Q->rear->next;//初始化新结点并链入
    Q-rear->next=p; 
    Q->rear=p;//将尾指针移至新结点
   

(4)出队

Datatype DeQueue( LinkQueue *Q)
   //出队,把头结点之后的元素摘下
    Datatype t;
    QueueNode *p;
    if(EmptyQueue( Q ))
      Error("Queue underflow");
    p=Q->rear->next->next; //p指向将要摘下的结点
    x=p->data; //保存结点中数据
    if (p==Q->rear)
     //当队列中只有一个结点时,p结点出队后,要将队尾指针指向头结点
      Q->rear = Q->rear->next; Q以上是关于期末复习数据结构与算法练习题的主要内容,如果未能解决你的问题,请参考以下文章

操作系统期末复习题

笔记总结计算机系期末复习专业课学习算法与其他笔记

体系结构 习题/题库/期末复习/考研复习

体系结构 习题/题库/期末复习/考研复习

体系结构 习题/题库/期末复习/考研复习

体系结构 习题/题库/期末复习/考研复习