看到底是要选择什么样的链表,这类题就是看需要找到的元素的位置在哪即可,删除和插入都要找到其前一个位置
一、选择题
1、
答案:A
解释:非空且循环,所以尾结点的下一个是头结点
2、
答案:D
解释:D是s的前指针指向p,s的下一个为p的下一个,注意这个时候p所指的下一个还没变,p所指的下一个的前一个是s,p的下一个是s(这个时候p所指的下一个才发生了变化)。
3、
答案:C
解析:
删除p所指的结点也就是p,所以就是p的前一个的下一个指向p的下一个,p的下一个的前一个指向p的前一个
4、
答案:B
解析:
(1)尾指针的定义: 尾指针,指向最后一个元素
(2)仅有头指针的单链表虽然可以直接获得第一个元素,但是想要获得最后一个元素却需要遍历整个链表。而仅有尾指针的单链表给出的是带有尾结点的单循环链表,这样就可以直接得到最后一个元素,想要得到第一个元素只需要再遍历一个元素就可以
(3)单链表只能往前移动
问题实质是用最快的时间找到 最后一个元素和头元素,根据尾指针通过O(1)的时间找到最后一个元素,然后根据链表是单循环,往前移动一个元素就是头元素,综上所述答案为:仅有尾指针的单循环链表
5、
答案:D
解析:
(1)题目问的是哪一种方式更省时间
(2)在最后一个结点附近操作,所以要先找到最后一个结点附近
带头结点的双向循环链表,头结点的前驱即可找到最后一个结点,可以快速插入,再向前可以找到最后一二个结点快速删除
单链表找到链表尾部需要扫描整个链表
双链表找到链表尾部也需要扫描整个链表
单循环链表只有单向指针,找到链表尾部也需要扫描整个链表
6、
答案:C
7、
答案:C
解析:
(1)在最后一个结点之后插入一个结点需要找到最后一个结点,删除最后一个结点需要的是找到最后一个结点的前一个,所以最快的是C。
8、
答案:C
9、
答案:D
解析:
(1)在最后一个结点之后插入一个结点需要找到最后一个结点,删除最后一个结点需要的是找到最后一个结点的前一个,所以最快的是带表头附加结点的双循环链表,代表头附加结点而且双循环。
(2)问题出现在查找效率上,链表最常用的操作是在末尾插入节点和删除尾节点 在尾巴插入 删除操作。都需要知道他的前导,而单链表要查找到最有一个元素需要遍历全部链表,循环双链表直接可以查到前导。
10、
答案:D
解析:
在最后一个结点后插入或删除第一个结点,如果仅仅需要删除第一个结点的话,仅带头结点即可,但是在最后一个结点后插入,必须要找到最后一个元素,这个时候仅有尾指针的单循环链表最快。
11、
答案:B
解析:
删除最后一个的话,需要找到最后一个元素的前一个,因为是单链表,所以与长度有关。
删除单链表中第一个元素和在单链表第一个元素前插入一个新元素与头指针有关
12、
答案:C
解析:
删除第一个,找头指针,删除最后一个,找它的前一个,最后一个元素后面找最后一个元素。所以如果是循环链表的话是最快的,因为涉及头指针,最后一个元素的前一个,所以最好是循环双链表,这样前面和后面都好找。
13、
答案:B
14、
答案:D
解析:
right是后面,left是前一个
15、
答案:D
16、
答案:B
解析:
因为前面已经将ha的尾部和hb的头部相连了,所以就剩把hb的尾部和ha的头部相连了;
17、设有一个双向循环链表,每个结点中除有left、data和right三个域外,还增设了一个访问频度域freq,freq 的初值为零。每当链表进行一次查找操作后,被访问结点的频度域值便增1,同时调整链表中结点的次序,使链表按结点频度值非递增有序的次序排列。下列算法是符合上述要求的查找算法,请将该算法补充完整。 (4分)
typedef struct Node{
ElemType data;
struct Node *left;
struct Node *right;
intfreq;
} DNode;
DNode *locate_DList(DNode *&L, ElemType x)
{ //在表L中查找元素x,查找成功则调整结点频度域值及结点位置,并返回结点地址;
//查找不成功则返回NULL
DNode *p=L, *q;
if (L==NULL) return NULL;
while (p->data!=x && p->right!=L) p=p->right;
if (p->data!=x) return NULL;
p->freq++;
q=p->left;
while (q!=L && q->freq<=p->freq) q=q->left; //查找插入位置
if (q==L && q->freq<=p->freq) { //需将p结点插在头结点L前
//将p结点先从链表中摘下来
p->left->right=p->right;
p->right->left=p->left;
//将p结点插在L结点前
p->right=L;
p->left=L->left;
L->left->right=p;
L->left=p;
L=p;
}
else if (q!=p->left ) { //若q不是p的前驱,则需调整结点位置,将p结点插在q结点后
//将p结点先从链表中摘下来
p->left->right=p->right;
p->right->left=p->left;
______________ //将p结点插在q结点后
}
return p;
}
答案:C
解析:
(1)其实并没有这么难,因为题目中代码块里其实已经有了注释了,将p结点插在q结点后
,而且题目中说明了是双循环链表。
(2)right是右边,left是左边。
18、
答案:D
二、函数题
1、循环单链表区间删除 (15 分)
本题要求实现带头结点的循环单链表的创建和单链表的区间删除。L是一个带头结点的循环单链表,函数ListCreate_CL用于创建一个循环单链表,函数ListDelete_CL用于删除取值大于min小于max的链表元素。
函数接口定义:
Status ListCreate_CL(LinkList &CL);
void ListDelete_CL(LinkList &CL,ElemType min,ElemType max);
裁判测试程序样例:
//库函数头文件包含
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
//函数状态码定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int ElemType; //假设线性表中的元素均为整型
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList; //循环单链表类型定义与单链表定义相同,区别在尾节点next取值
Status ListCreate_CL(LinkList &CL);
void ListDelete_CL(LinkList &CL, ElemType min, ElemType max);
void ListPrint_CL(LinkList &CL)
{ //输出单链表,空表时输出Empty List。
LNode *p=CL->next; //p指向第一个元素结点
if(p==CL){
printf("Empty List");
return;
}
while(p!=CL)
{
if(p->next!=CL)
printf("%d ",p->data);
else
printf("%d",p->data);
p=p->next;
}
}
int main()
{
LinkList CL;
ElemType min,max;
if(ListCreate_CL(CL)!= OK)
{
printf("循环链表创建失败!!!\n");
return -1;
}
scanf("%d%d",&min,&max);
ListDelete_CL(CL,min,max);
ListPrint_CL(CL);
return 0;
}
输入格式: 第一行输入一个整数n,表示循环单链表中元素个数,接下来一行共n个整数,中间用空格隔开。第三行输入min和max。
输出格式: 输出删除后循环链表的各个元素,两个元素之间用空格隔开,最后一个元素后面没有空格。
输入样例:
6
1 2 3 4 5 6
2 5
输出样例:
1 2 5 6
答案
Status ListCreate_CL(LinkList &CL)
{
CL=(LinkList)malloc(sizeof(LNode));
if(!CL) exit(OVERFLOW);
CL->next=CL;
int n;
LinkList p,r;
p=CL;//因为插入某一个元素的话,要先找到这个元素的前一个;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
r=(LinkList)malloc(sizeof(LNode));
if(!r) exit(OVERFLOW);
scanf("%d",&r->data);
r->next=CL;
p->next=r;
p=r;
}
return OK;
}
void ListDelete_CL(LinkList &CL,ElemType min,ElemType max)
{
LinkList p,r;
p=CL;//不能是CL->next,因为可能删除的是第一个元素;
while(p->next!=CL)//注意判断结束的条件
{
if(p->next->data>min&&p->next->data<max)
{
r=p->next;
p->next=r->next;
free(r);
}
else p=p->next;//这里忘记了;
}
}
注意:
(1)在创建的时候,一定要看清题,是循环链表还是单链表
(2)在创建的时候,到底是否需要插入数据
(3)在删除的时候,如果不符合删除的条件的话,别忘记后移
(4)删除或者是插入,都不要忘记是找到这个元素的前一个元素
三、编程题
1、jmu-ds-循环单链表的基本运算
实现循环单链表的基本运算:初始化、插入、删除、求表的长度、判空、释放。
(1)初始化循环单链表L,输出(L->next==L)的逻辑值;
(2)依次采用尾插法插入元素:输入分两行数据,第一行是尾插法需要插入的字符数据的个数,第二行是具体插入的字符数据。
(3)输出循环单链表L;
(4)输出循环单链表L的长度;
(5)判断循环单链表L是否为空;
(6)输出循环单链表L的第3个元素;
(7)输出元素a的位置;
(8)在第4个元素位置上插入‘w’元素;
(9)输出循环单链表L;
(10)删除L的第5个元素;
(11)输出循环单链表L;
(12)释放循环单链表L。
输入格式:
两行数据,第一行是尾插法需要插入的字符数据的个数,第二行是具体插入的字符数据。
输出格式:
按照程序要求输出
输入样例:
5
a b c d e
输出样例:
1
a b c d e
5
no
c
1
a b c w d e
a b c w e
答案1:(推荐)
#include <bits/stdc++.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef char ElemType;
typedef int Status;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
Status InsertList(LinkList &L)
{
L=(LinkList)malloc(sizeof(LNode));
if(!L) exit(OVERFLOW);
L->next=L;
int n;
scanf("%d",&n);
getchar();
LinkList p,r;
p=L;
for(int i=0;i<n;i++)
{
r=(LinkList)malloc(sizeof(LNode));
if(!r) exit(OVERFLOW);
scanf(" %c",&r->data);//注意读取方式;
r->next=L;
p->next=r;
p=r;
}
return 0;
}
void PrintfList(LinkList L)
{
LinkList p;
p=L->next;
while(p!=L)
{
if(p==L->next) printf("%c",p->data);
else printf(" %c",p->data);//这里为啥空格必须在前面?
p=p->next;//这里又忘记了;
}
printf("\n");
}
void ListLength(LinkList L)
{
LinkList p;
int i=0;
p=L->next;
while(p!=L)
{
i++;
p=p->next;//这里又忘记了,啊啊啊啊;
}
printf("%d\n",i);
}
Status EmptyList(LinkList L)
{
if(L->next==L) printf("yes\n");
else printf("no\n");
return OK;
}
Status ShuchuList(LinkList L,int n)
{
LinkList p;
p=L->next;
int i=1;
while(p!=L&&i<n)
{
i++;
p=p->next;
}
if(p==L||i>n) return ERROR;
printf("%c\n",p->data);
return 0;
}
Status LocateList(LinkList L,ElemType a)
{
LinkList p;
p=L->next;
int i=1;
while(p->data!=a&&p!=L)
{
i++;
p=p->next;
}
if(p==L) return ERROR;
printf("%d\n",i);
return OK;
}
Status InsertList1(LinkList &L,int j,ElemType e)
{
LinkList p,r;
p=L;
int i=0;
//while(i<j-1&&p!=L)
while(i<j-1&&p)//找前一个,所以j要-1;
{
i++;
p=p->next;
}
if(p==L||i>j) return ERROR;
r=(LinkList)malloc(sizeof(LNode));
if(!r) exit(OVERFLOW);
r->data=e;//千万别忘记输入元素;
r->next=p->next;
p->next=r;
return OK;
}
Status DeleteList(LinkList &L,int n)
{
LinkList p,r;
p=L;
int i=0;
//while(i<j-1&&p!=L)
while(i<n-1&&p->next)//找前一个,所以j要-1;
{
i++;
p=p->next;
}
if(p->next==L||i>n) return ERROR;//这里记住谁和L进行比较
r=p->next;
p->next=r->next;
free(r);
return OK;
}
int main()
{
LinkList L;
InsertList(L);
printf("1\n");
PrintfList(L);
ListLength(L);
EmptyList(L);
ShuchuList(L,3);
LocateList(L,'a');
InsertList1(L,4,'w');
PrintfList(L);
DeleteList(L,5);
PrintfList(L);
free(L);
return 0;
}
注意:
1、总是错误的地方在答案中都有标注,总是忘记后移p;
2、注意输入的方法
(1)scanf之后换行,然后输出char类型的数据,所以需要getchar()
(2)因为是char类型的,之间还有空格,所以输入的时候需要空格+%c,这样等价于cin>>c;
具体的讲解在其它的博客中有详细讲解,可以自己去领会。
答案2:
#include <bits/stdc++.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int Status;
typedef char ElemType;
typedef struct LNode
{
ElemType data;
struct LNode* next;
} LNode,*LinkList;
//初始化单链表
Status InitList(LinkList &L)//这里有&号
{
L=(LinkList)malloc(sizeof(LNode));
if(!L) exit(OVERFLOW);
L->next=L;
return OK;
}
//输入(尾部插入)链表
Status InsertList(LinkList &L,int n)
{
LinkList p,r;
p=L;//等于的是L,因为是从开头插入,要先找到前一个元素;
for(int i=0; i<n; i++)
{
r=(LinkList)malloc(sizeof(LNode));
if(!r) exit(OVERFLOW);
scanf(" %c",&r->data);//这里注意读取的方式;
r->next=L;
p->next=r;
p=r;
}
return OK;
}
//输出链表
void PrintfLinkList(LinkList L)
{
LinkList p;
p=L->next;
while(p!=L)
{
if(p==L->next) printf("%c",p->data);
else printf(" %c",p->data);
p=p->next;//这一句千万别忘了啊!!!!!;
}
printf("\n");
}
//判断链表是否为空
Status EmptyLinkList(LinkList L)
{
if(L->next==L) return TRUE;
else return FALSE;
}
//输出单链表L的第n个元素
Status ElemLink(LinkList L,int i)
{
LinkList p;
p=L->next;
int j=1;
while(p!=L&&j<i)
{
p=p->next;
j++;
}
if(p==L||j>i) exit(OVERFLOW);
printf("%c\n",p->data);
return OK;
}
//输出某一个元素的位置
Status LocateLink(LinkList L,ElemType e)
{
LinkList p;
p=L->next;
int j=1;
while(p!=L)
{
if(p->data==e)
{
printf("%d\n",j);
return OK;
}
else
{
p=p->next;
j++;
}
}
return ERROR;
}
//在某一个位置上插入元素
Status InsertList_L(LinkList &L,int i,ElemType e)
{
LinkList p,r;
p=L;
int j=0;
while(p&&j<i-1)
{
p=p->next;
j++;
}
if(p==L||j>i-1) exit(OVERFLOW);
r=(LinkList)malloc(sizeof(LNode));
r->data=e;//千万别忘记输入元素;
r->next=p->next;
p->next=r;
return OK;
}
//删除L的第n个元素
Status DeleteList(LinkList &L,int i)
{
LinkList p,r;
p=L;
int j=0;
while((p->next)!=L&&j<i-1)
{
p=p->next;
j++;
}
if((p->next)==L||j>i-1) exit(OVERFLOW);
r=p->next;
p->next=r->next;
free(r);
}
int main()
{
LinkList L;
InitList(L);
int n;
scanf("%d",&n);
printf("1\n");
InsertList(L,n);
PrintfLinkList(L);
printf("%d\n",n);
if(EmptyLinkList(L)==TRUE) printf("yes\n");
else printf("no\n");
ElemLink(L,3);
LocateLink(L,'a');
InsertList_L(L,4,'w');
n++;//这里别忘了;
PrintfLinkList(L);
DeleteList(L,5);
PrintfLinkList(L);
free(L);
}
2、jmu-ds-小孩报数问题
有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。
输入格式:
第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用逗号”,”间隔
输出格式:
按人名输出小孩按顺序出列的顺序,每行输出一个人名
输入样例:
5
Xiaoming
Xiaohua
Xiaowang
Zhangsan
Lisi
2,3
输出样例:
Zhangsan
Xiaohua
Xiaoming
Xiaowang
Lisi
答案
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <string>
using namespace std;
queue<string> s;
int main()
{
int n,p,q,i;
scanf("%d",&n);
while(n--)
{
string a;
cin>>a;
s.push(a);
}
scanf("%d,%d",&p,&q);
for(i=1;i<p;i++)
{
s.push(s.front()); //处理从第几个孩子开始报数
s.pop();
}
i=1;
while(s.size()>0)
{
if(i%q==0) //报道第q个孩子弹出
{
cout<<s.front()<<endl;
s.pop();
}
else //否则压如队尾
{
s.push(s.front());
s.pop();
}
i++;
}
}