参考书目:《数据结构教程(第五版)》 李春葆 主编
《2022年数据结构考研复习指导》 王道论坛 组编
《数据结构》上机实验(第二章) Ⅱ
1. L 1 = ( x 1 , x 2 , … , x n ) , L 2 = ( y 1 , y 2 , . . . , y m ) L1=(x_1,x_2,…,x_n),L2=(y_1,y_2,...,y_m) L1=(x1,x2,…,xn),L2=(y1,y2,...,ym),它们是两个线性表,采用带头结点的单链表存储,合并L1、L2,结果放在线性表L3中。
要求如下:
L
3
=
(
x
1
,
y
1
,
x
2
,
y
2
,
.
.
.
,
x
m
,
y
m
,
x
m
+
1
,
.
.
.
,
x
n
)
m
≤
n
L3=(x_1,y_1,x_2,y_2,...,x_m,y_m,x_{m+1},...,x_n)\;\;\;\;\;\;\;\;\;\;m≤n
L3=(x1,y1,x2,y2,...,xm,ym,xm+1,...,xn)m≤n
L
3
=
(
x
1
,
y
1
,
x
2
,
y
2
,
.
.
.
,
x
n
,
y
n
,
y
n
+
1
,
.
.
.
,
y
m
)
m
>
n
L3=(x_1,y_1,x_2,y_2,...,x_n,y_n,y_{n+1},...,y_m)\;\;\;\;\;\;\;\;\;\;m>n
L3=(x1,y1,x2,y2,...,xn,yn,yn+1,...,ym)m>n
L3仍采用单链表存储。
- 算法思路:定义一个变量用来判断奇数、偶数,当该数为奇数时,复制x,当该数为奇数时,复制y,从而使L3中的值交替。
void Merge(LinkList &L1, LinkList &L2,LinkList &L3)
{
LNode* p1 = L1->next, * p2 = L2->next, * p = L3, * r1;
int i = 1;
while (p1 != NULL && p2 != NULL)
{
if (i % 2 == 1) //i为奇数时,把L1中的值放入L3
{
r1 = p1;
p1 = p1->next;
i++; //i的计数值+1
}
else //i为偶数时,把L2中的值放入L3
{
r1 = p2;
p2 = p2->next;
i++; //i的计数值+1
}
r1->next = p->next; //插入
p->next = r1;
p = p->next; //指针后移
}
if (p2 == NULL) r1->next = p1; //m≤n
if (p1 == NULL) r1->next = p2; //m>n
}
程序分析:
- 运行结果:
- 时间复杂度:O(m+n);空间复杂度:O(m+n)。其中m、n分别是L1、L2的长度。
2. 已知由单链表L表示的线性表中含有3类字符的数据元素(如字母、字符、数字字符和其他字符)。设计一个算法构造3个循环单链表A、B、C,使每个循环单链表中只含同一类的字符,且利用原表中的结点空间作为这3个表的结点空间,头结点可另辟空间。
- 算法思想:先创建3个空的循环单链表,用p扫描单链表L的所有数据结点,将不同类型的结点采用头插法插入到相应的循环单链表中。
void Split(LNode* L, LNode*& A, LNode*& B, LNode* C)
{
LNode* p = L->next, * q;
A = L;
A->next = A;
B = (LNode*)malloc(sizeof(LNode));
B->next = B;
C = (LNode*)malloc(sizeof(LNode));
C->next = C;
while (p != NULL)
{
if (p->data >= 'A' && p->data <= 'Z' || p->data >= 'a' && p->data <= 'z')
{
q = p;
p = p->next;
q->next = A->next;
A->next = q;
}
else if(p->data >= '0' && p->data <= '9')
{
q = p;
p = p->next;
q->next = B->next;
B->next = q;
}
else
{
q = p;
p = p->next;
q->next = C->next;
C->next = q;
}
}
}
3. 已知带头结点的循环单链表L中至少有两个结点,每个结点的两个域为data和next,其中data的类型为整型。判断该链表中每个结点值是否小于其后续两个结点值之和。
- 解:用p扫描整个循环单链表L,一旦找到pー>data<pー>nextー>data+p->next->next->data条件不成立的结点p,则中止循环,返回假,否则继续扫描。当while循环正常结束时返回真。
bool fun(LinkList LA)
{
LNode* p1 = LA->next, * p2, * p3;
if (p1 != NULL && p1->next != NULL)
{
p2 = p1->next; //第一个后继结点
if (p2 != NULL && p2->next != NULL)
p3 = p2->next; //第二个后继节点
}
while (p3 != NULL)
{
if (p1->data < p2->data + p3->data)
{
p1 = p1->next;
p2 = p2->next;
p3 = p3->next;
}
else return false;
}
return true;
}
程序分析:
- 运行结果:
4. 设计一个算法判定单链表L(带头结点)是否是递增的。
bool IsIncrease(LinkList LA)
{
LNode* p = LA->next,*q;
if (p != NULL) q = p->next; //第一个后继结点
while (q != NULL)
{
if (q->data > p->data)
{
p = p->next;
q = q->next;
}
else return false;
}
return true;
}
程序分析:
- 运行结果:
5. 一个长度为N的整型数组A[1…N],给定整数X,请设计一个时间复杂度不超过 O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)的算法,查找出这个数组中所有两两之和等于X的整数对(每个元素只输出一次)。
算法思想:先用一种时间复杂度为 O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)的排序算法将A[1…N]从小到大排序,可以用快速排序(或二路归并等),然后分别从数组的小端(i=1)和大端(j=N)开始査找;若A[i]+A[j]<X,i++若A[i]+A[j]>X,j-;否则输出A[i]、A[j],然后i++,j–;直到i>j停止。)
参考书目:《2022年数据结构考研复习指导》王道论坛 组编
1. 设计一个算法,删除不带头结点的单链表L中所有值为x的结点。
- 不使用递归算法:
- 算法思路:单链表L不带头结点,但可以把首结点看作是“头结点”,按照有头结点的单链表进行操作,最后再判断首结点的值是否为x。
void DeleteEle(LinkList& L,int x) //删除不带头结点的单链表L中所有值为x的结点
{
LNode* pre = L, * p = L->next, * s = L, * p1; //pre和s都指向首结点,p指向第二个结点
while (p != NULL)
{
if (p->data == x) //如果该结点值为x
{
if (p->next == NULL) pre->next = NULL; //如果要删除的值在尾结点,则前一个结点的指针域为NULL
else pre->next = p->next; //如果要删除的值不在尾结点,则前一个结点的指针域指向该结点的下一个结点
p1 = p; //让p1指向要删除的结点
p = p->next; //p指针后移一个结点
free(p1); //释放要删除的结点
}
else //如果该结点值不为x
{
pre = pre->next; //pre指针和p指针同时后移
p = p->next;
}
}
if (s->data == x) //s指向首结点,如果首结点的值为x
{
L = L->next; //首结点后移
free(s); //释放首结点
}
}
程序分析:
- 运行结果:`
- 时间复杂度:O(n);空间复杂度:O(1)
- 使用递归算法:
void del(LinkList &L,ElemType x)
{
LNode* p;
if (L == NULL) return;
if (L->data == x)
{
p = L;
L = L->next;
free(p);
}
del(L->next, x);
}
程序分析:
- 算法需要借助一个递归工作栈,深度为O(n),时间复杂度为O(n)。因为L为引用类型,是直接对原链表进行操作的,在此基础上free(p)结点不会造成断链。
2. 在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一。
- 算法思路:参考第4题。
void DeleteEle(LinkList& L,int x)
{
LNode* pre = L, * p = L->next, * p1;
while (p != NULL)
{
if (p->data == x)
{
if (p->next == NULL) pre->next = NULL;
else pre->next = p->next;
p1 = p;
p = p->next;
free(p1);
}
else
{
pre = pre->next;
p = p->next;
}
}
}
程序分析:
- 运行结果:`
- 时间复杂度:O(n);空间复杂度:O(1)
3. 设L为带头结点的单链表,实现从尾到头反向输出每个结点的值。
- 算法思路:先让p指向L的第二个结点,让L变为只含头结点和首结点的单链表,采用头插法实现单链表的逆置。
void DiverseEle(LinkList& L)
{
LNode* p = L->next->next, * s = L, * pre;
L->next->next = NULL;
while (p != NULL)
{
pre = p;
p = p->next;
pre->next = s->next;
s->next = pre;
}
}
程序分析:
- 运行结果:
4. 在带头结点的单链表L中删除一个最小值结点(假设最小值结点是唯一的)
- 算法思路:link:第二题
void DeleteEle(LinkList &L)
{
LNode* premin = L, * min = L->next, * p=min, * s=premin;
while (p != NULL)
{
if (p->data >= min->data)
{
s = s->next;
p = p->next;
}
else
{
premin = s;
min = p;
}
}
if (min->next == NULL) premin->next = NULL;
else premin->next = min->next;
free(min);
}
程序分析:
- 运行结果:
5. 将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为O(1)。
算法思路:参考第6题。
6. 有一个带头结点的单链表L,使其元素递增有序。
- 算法思路:Link:第3题
7. 在一个带表头结点的单链表中所有元素结点的数据值无序,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素(若存在)。
void DeleteEle(LinkList& L, int x, int y)
{
LNode* pre = L, * p = L->next, * s;
while (p != NULL)
{
if (p->data > x && p->data < y)
{
if (p->next == NULL) pre->next = NULL;
else pre->next = p->next;
s = p;
p = p->next;
free(s);
}
else
{
pre = pre->next;
p = p->next;
}
}
}
DeleteEle(L, 0, 5);
程序分析:
- 运行结果:
8. 给定两个单链表,找出两个链表的公共结点。
- 算法思路:Link:第8题(1)
9. 按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(要求:不允许使用数组作为辅助空间)。
10. 将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A表中含有原表中序号为奇数的元素,而B表中含有原表中序号为偶数的元素,且保持其相对顺序不变。
- 算法思路:Link:最上面的第一题。
void DepList(LinkList& L1, LinkList& L2)
{
LNode* p1 = L1->next, * p2, * r = L2, * s;
while (p1->next != NULL)
{
p2 = p1->next;
s = p2;
if (p2->next == NULL) p1->next = NULL;
else p1->next = p2->next;
p1 = p2->next;
s->next = r->next;
r->next = s;
r = s;
}
r->next = NULL;
}
程序分析:
- 运行结果:
11. 设 C = { a 1 , b 1 , a 2 , b 2 , … , a n , b n } C=\{a_1,b_1,a_2,b_2,…,a_n,b_n\} C={a1,b1,a2,b2,…,an,bn}为线性表,采用带头结点的hc单链表存放,设计一个就地算法,将其拆分为两个线性表,使得 A = { a 1 , a 2 , . . . , a n } , B = { b n , … , b 2 , b 1 } A=\{a_1,a_2,...,a_n\},B=\{b_n,…,b_2,b_1\} A={a1,a2,...,an},B={bn,…,b2,b1}。
- 算法思路:Link:第1题
- 可采用上题的思路,二者的区别在于上题使用尾插法,而这题使用头插法。