1.简述下列术语或概念
(1)顺序表的优缺点
1.顺序表的存储空间为静态分配,需要预先规定存储规模,存储空间大小开辟不好把握,但是顺序表的存储密度高,存储空间利用率高
2.顺序表是一种随机存储结构,可以在O(1)时间内完成对元素的存取,但是删除和插入操作时,需要移动插入位置后的所有元素
(2)单链表的优缺点
1.单链表的存储有静态存储和动态存储,动态存储时,只要内存尚有余额,不会产生溢出,但是存储密度会下降,存储空间利用率不高
2.单链表的元素存取需要从头结点开始逐个查找指定元素,但是插入和删除操作只需要修改指针指向即可
(3)单链表中的头指针、头结点和元素结点
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针,头结点是单链表中指向第一个元素结点的结点,元素结点是链表中除头结点以外的存储元素的结点
2.选择题
(1)某线性表中最常用的操作是存取序号为 i 的元素和在最后进行插入和删除运算,则采用(D)存储方式时间性能最好。
A .双向链表
B .双向循环链表
C .单向循环链表
D .顺序表
(2)下列选项中,(D)是链表不具有的特点。
A .插入和删除运算不需要移动元素
B .所需要的存储空间与线性表的长度成正比
C .不必事先估计存储空间大小
D .可以随机访问表中的任意元素
(3)在链表中最常用的操作是删除表中最后一个结点和在最后一个结点之后插入元素,则采用(C)最节省时间。
A .头指针的单向循环链表
B .双向链表
C .带尾指针的单向循环链表
D .带头指针双向循环链表
(4)下面关于线性表的叙述错误的是(D).
A .线性表采用顺序存储必须占用一片连续的存储空间
B .线性表采用链式存储不必占用一片连续的存储空间
C .线性表采用链式存储便于插入和删除操作的实现
D .线性表采用顺序存储便于插入和删除操作的实现
(5)设一条单链表的头指针变量为 head 且该链表没有头结点,则其判空条件是(A)。
A . head ==0
B . head -> next ==0
C . head -> next == head
D . head !=0
(6)设带有头结点的单向循环链表的头指针变量为 head ,则其判空条件是(B).
A . head == NULL
B . head -> next == NULL
C . head -> next == head
D . head != NULL
(7)设顺序线性表中有 n 个数据元素,则删除表中第 i 个元素需要移动(A)个元素。
A . n - i
B . n +1- i
C . n -1- i
D . i
(8)有一个含头结点的双向循环链表,头指针为 head ,则其为空的条件是(D)
B . head -> next == NULL
A . head -> prior == NULL
D . head -> next -> prior == NULL
C . head -> next == head
(9)在一个单链表中,删除指针 p 所指的后继结点,以下语句正确的是(D)。
A . p -> next = p -> next -> next ; free ( p -> next );
B . free ( p -> next ); p -> next = p -> next -> next ;
C . p = p -> next ;
D . s = p -> next ; p -> next = s -> next ; free ( s );
(10)设某顺序表中第一个元素的地址是 Base ,每个结点占 m 个单元,则第 i 个结点的地址为(A)。
A . Base +( i -1) x m
B . Base + i x m
C . Base - i x m
D . Base +( i +1) x m
3.已知顺序表 L 递增有序,写一算法,将元素 X 插入线性表的适当位置,以保持线性表的有序性。
#define MaxSize 100
typedef struct SqList{
int data[MaxSize];
int length;
}SqLsit;
void insert(SqList *list,int elem){
//顺序表递增有序
//判断数组长度
if(list->length == MaxSize){
return ; //数组已满,插入失败
}
//倒序遍历数组,
//如果要插入元素elem小于当前元素,则将当前元素后移一位
//如果要插入元素elem大于等于当前元素,则将当前元素后移一位后将elem赋值到当前位置
for(int i=list->length-1;i>=0;i--){
if(elem < list->Data[i]){
list->data[i+1] = list->data[i];
}else{
list->data[i+1] = list->data[i];
list->data[i] = elem;
list->length++;
return ; //插入成功
}
}
}
4.编写算法,从顺序表中删除自第 i 个元素开始的 k 个元素。
#define MaxSize 100
//定义顺序表
typedef struct SqList{
int data[MaxSize];
int length;
}SqList;
int delet_fromicountk(SqList *list,int i,int k){
//删除顺序表list中从i开始的往后k个元素
//首先判断i和k值的合法性
if(i>list->length || i<=0 || k<=0){
return 0; //插入失败
}
//分情况对顺序白哦进行删除操作
//两种情况
//i+K-1 < list->length 此时将后面元素前移
//i+k-1 >= list->length 此时删除i位后面所有元素
if(i+k-1 >= list->length){
//此时将顺序表长度减k即可
list->length = list->length - k;
}else{
int index = i;
for(int j=list->length-1;j>=i+k-1;j--){
list->data[index++] = list->data[j];
}
}
return 1; //执行成功
}
5.已知两个递增有序的顺序表 La 和 Lb 。编写算法将 La 和 Lb 合并成一个递减有序的顺序表,并将合并结果存放在 La 中(假设表 La 空间足够大,不会溢出)。要求:时间复杂度为 O ( n )。
#define MaxSize 100
typedef struct SqList{
int data[MaxSize];
int length;
}SqList;
void merge(Sqlist *l1,SqList *l2,SqList *l3){
//假设顺序表足够大,不会产生溢出
//使用双指针倒序遍历两个顺序表
int i = l1->length-1;
int j = l2->length-1;
int k = 0;
while(i>=0&&j>=0){
if(l1->data[i]>l2->data[j]){
l3->data[k] = l1->data[i];
i--;
}else{
l3->data[k] = l2->data[j];
j--;
}
k++;
}
//将剩余元素放入数组
if(i>=0){
while(i>=0){
l3->data[k++] = l1->data[i--];
}
}
if(j>=0){
while(j>=0){
l3->data[k++] = l1->data[i--];
}
}
return ;
}
6.某顺序表 L 中存放整型数据,编写算法,在时间复杂度 O ( n )、空间复杂度0(1)内,对表 L 进行重新排序,将奇数全部放在前面,偶数全部放在后面。
/*
* 某顺序表中存放整形数据,编写算法在O(n)时间复杂,
* O(1)空间复杂度内完成对顺序表的重新排序,将奇数
* 放在前面,偶数放在后面
*
* 思路:
* 设置双指针,指针移动,分别找到各自方向的偶
* 和奇数,然后对调
* */
void sortByOddAndEven(SqList *list){
int i = 0;
int j = list->length;
while(i>=j){
//指针指向情况有四种情况,分别对应四种动作:
//一偶一奇 对换
//一偶一偶 j--
//一奇一奇 i++
//一奇一偶 i++ j++
if(list->data[i]%2==0 && list->data[j]%2==1){
int temp = list->data[j];
list->data[j] = list->data[i];
list->data[i] = temp;
}else if(list->data[i]%2==0 && list->data[j]%2==0){
j--;
}else if(list->data[i]%2==1 && list->data[j]%2==1){
i++;
}else{
i++;
j++;
}
}
return ;
}
7.已知线性表中的元素(整数)以值递增有序排列,并以单链表作为存储结构。试写一高效算法,删除表中所有大于 mink 且小于 maxk 的元素(若表中存在这样的元素),分析算法的时间复杂度(注意: mink 和 maxk 是给定的两个参变量,它们的值为任意整数)。
/*
* 有一单链表,删除单链表中值处于(minx,maxx)之间的元素
*
* */
//定义单链表结构
typedef struct LNode{
int data; //数据域
struct LNode *next; //指针域
}LinkList;
void delete_between_minx_maxx(LinkList *list,int minx,int maxx){
//假设链表带头结点
struct LNode * s = list;
while(s->next!=NULL){
if(s->next->data >= minx && s->next->data <= maxx){
struct LNode *p = s->next;
//删除当前结点,即p结点
s->next = p->next;
free(p);
}
s = s->next;
}
}
8.假设两个按元素值递增有序排列的线性表 A 和 B 均以单链表作为存储结构,试编写算法,将表 A 和表 B 归并成一个按元素值递减有序排列的线性表 C ,并要求利用原表(即表 A 和表 B )的结点空间存放表 C 。
/*
* 两个单链表递增有序排列,将其合并为一个递减排序的有序单链表
* 要求:用原表的结点空间存储新的链表
* */
//定义链表结构体
typedef struct LNode{
int data; //数据域
struct LNode *next; //指针域
}LinkList;
void merge(LinkList *list1,LinkList *list2,LinkList *list3){
//思路:同时遍历两个单链表,链接成一个新的链表
struct LNode *A = list1->next;
struct LNode *B = list2->next;
struct LNode *C = list3;
while(A!=NULL && B!=NULL){
//比较AB结点元素的大小
if(A->data >= B->data){
C->next = A;
A = A->next;
}else{
C->next = B;
B = B->next;
}
C = C->next;
}
//将剩余结点加入C
while(A!=NULL){
C->next = A;
A = A->next;
C = C->next;
}
while(B!=NULL){
C->next = B;
B = B->next;
C = C->next;
}
return ;
}
9.已知由单链表表示的线性表中含有三类字符的数据元素(如字母字符、数字字符和其他字符),试编写算法,构造三个以循环链表表示的线性表,使每个表中只含同一类字符,且利用原表中的结点空间作为这三个表的结点空间,头结点可另辟空间。
/*
* 将单链表中的三种数据按元素类型分开,分成三个循环链表
* */
void split(LinkList *list,CycLinkList *c1,CycLinkList *c2,CycLinkList *c3){
struct LNode *a = list->next;
struct LNode *b = c1;
struct LNode *c = c2;
struct LNode *d = c3;
while(a!=NULL){
if(isdigit(a->data)){
b->next = a;
a = a->next;
b = b->next;
}else if(isalpha(a->data)){
c->next = a;
a = a->next;
c = c->next;
}else{
d->next = a;
a = a->next;
d = d->next;
}
}
b->next = c1;
c->next = c2;
d->next = c3;
return ;
}
10.设线性表 A =( a1 ,a2,…, am ), B =(b1,b2,…, bm.),试写一个按下列规则合并 A 、 B 为线性表 C 的算法,使得C =( a1 , b1 ,…, am , bm , b(m+1) ……, bn .),当 m ≤ n 时;或者 C =( a1 , b1 ,…, an, bn , a(n+1) ..,…, am ),当 m > n 时。线性表 A 、 B 、 C 均以单链表作为存储结构,且表 C 利用表 A 和表 B 中的结点空间构成。注意:单链表的长度值 m 和 n 均未显式存储。
/*
* 有连个单链表,交叉排列其元素,多余元素附加于末尾
* */
void merge(LinkList *list1,LinkList *list2,LinkList *list3){
struct LNode *a = list1->next;
struct LNode *b = list2->next;
struct LNode *c = list3;
int flag = 0;
while(a!=NULL && b!=NULL){
if(flag == 0){
struct LNode *p = a;
a = a->next;
c->next = p;
c = c->next;
flag = 1;
}else if(flag == 1){
struct LNode *p = b;
b = b->next;
c->next = p;
c = c->next;
flag = 0;
}
}
//将剩余元素附加到末尾
if(a!=NULL){
c->next = a;
}
if(b!=NULL){
c->next = b;
}
return ;
}
11.将一个用单链表表示的稀疏多项式分解成两个多项式,使这两个多项式中各自仅含奇次项或偶次项,并要求利用原链表中的结点空间来构成这两个链表。
/*
* 一个用单链表表示的稀疏多项式,分解为奇次项和偶次项两个单链表
* 要求:用原单链表的结点存储
* */
//定义多项式的单链表表示
typedef struct FuncLNode{
int a;
int index;
struct FuncLNode * next;
}FuncLinkList;
void split(FuncLinkList *list,FuncLinkList *oddItemList,FuncLinkList *evenItemList){
struct FuncLnode * s = list->next;
struct FuncLnode * op = oddItemList;
struct FuncLnode * ep = evenItemList;
while(s!=NULL){
if(s->index%2==0){
struct FuncLNode *p = s;
s = s->next;
op->next = p;
op = op->next;
}else{
struct FuncLNode *p = s;
s = s->next;
ep->next = p;
ep = ep->next;
}
}
op->next = NULL;
ep->next = NULL;
return ;
}
12.已知顺序表 L ,试设计一个时间和空间两方面尽可能高效的算法,将 L 中的整数序列循环左移 p (0< p < n )个位置,即将 L 中的数据序列( Xo , X ,,…, X ,_, X ,, X ,,,…, x .,)变换为( X ,, X ,… X , XX , X ,)。
/*
* 将顺序表中的元素循环左移p位
*
* */
void cycle_left_move(SqList *list,int p){
//思路:三步翻转法
// x1 x2 x3 x4 ... xn
// 翻转前p个元素 xp x(p-1) ... x1 x(p+1) x(p+2) ... xn
// 翻转后n-p个元素 xp x(p-1) ... x1 x xn x(n-1) ... x(p+1)
// 翻转整个序列 x(p+1) x(p+2) ... xn x x1 ... xp
for(int i=0;i<p;i++){
int temp = list->data[p-i-1];
list->data[p-i-1] = list->data[i];
list->data[i] = temp;
}
for(int i=p;p<list->length;i++){
int temp = list->data[list->length-i-1];
list->data[list->length-i-1] = list->data[i];
list->data[i] = temp;
}
for(int i=0;i<list->length;i++){
int temp = list->data[list->length-i-1];
list->data[list->length-i-1] = list->data[i];
list->data[i] = temp;
}
return ;
}
13.现有带头结点的单链表 L ,设计尽可能高效的算法,查找链表中倒数第 k 个结点( k 为正整数)。若查找成功,输出该结点 data 域的值。
/*
* 带头结点的单链表,查找并输出倒数第k个元素
* 要求尽可能高效
* */
void find_endk(LinkList *list,int k){
//思路:
// 快慢指针法,设置两个指针,一个指针比另一个指针走快k步,
// 当一个指针到达尾部时,另一个指针即指向倒数第k个元素
//
LNode * i = list->next; //快指针
LNode * j = list->next; //慢指针
while(k--){
//先让快指针走k步
i = i->next;
if(i==NULL){
return ; //链表元素不足k个
}
}
while(i!=NULL){
i = i->next;
j = j->next;
}
printf("%d",j->data);
return ;
}
如有错误,尽请指正,我会及时修改,如有问题,也可评论区评论