1、设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。
非递归常规解(带头结点):
//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
int x;
LNode *p = L->next; //p用来遍历单链表
LNode *q; //q用来暂时替代p,然后将结点free
LNode * pre = L;
printf("请输入删除重复值:");
scanf("%d", &x);
while(p != NULL)
{
if(p->data == x)
{
q = p;
pre->next = q->next;
p = p->next;
free(q);
}
else
{
pre = p; //pre要一直为p的前驱
p = p->next;
}
}
}
递归解(带头结点):
//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
LNode *p = L->next;
LNode *pre = L;
LNode *q;
int x;
printf("请输入x的值:");
scanf("%d", &x);
if(p == NULL)
return;
if(p->data == x)
{
pre->next = p->next;
q = p;
free(q);
Del_X_3(L);
}
else
Del_X_3(L->next);
}
递归解(不带头结点):
//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
LNode *p;
int x;
printf("请输入x的值:");
scanf("%d", &x);
if(L == NULL)
return;
if(L->data == x)
{
p = L;
L = L->next;
free(p);
Del_X_3(L);
}
else
Del_X_3(L->next);
}
2、在带头结点的单链表中,删除所有值为x的结点,并释放空间。
非递归解(推荐):
//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
int x;
LNode *p = L->next; //p用来遍历单链表
LNode *q; //q用来暂时替代p,然后将结点free
LNode * pre = L;
printf("请输入删除重复值:");
scanf("%d", &x);
while(p != NULL)
{
if(p->data == x)
{
q = p;
pre->next = q->next;
p = p->next;
free(q);
}
else
{
pre = p; //pre要一直为p的前驱
p = p->next;
}
}
}
递归解:
//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
LNode *p = L->next;
LNode *pre = L;
LNode *q;
int x;
printf("请输入x的值:");
scanf("%d", &x);
if(p == NULL)
return;
if(p->data == x)
{
pre->next = p->next;
q = p;
free(q);
Del_X_3(L);
}
else
Del_X_3(L->next);
}
3、设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
暴力解1,时间复杂度O(n^2)
//从尾到头输出每个结点的值
void R_Print(LinkList L)
{
LNode *p = L->next, *r = NULL; //r用来限制每次遍历的长度
while(p->next != r)
{
p = p->next;
if(p->next == r)
{
printf("%d", p->data);
r = p;
p = L->next;
}
}
printf("%d", p->data); //输出第一个元素(头结点之后第一个结点)
}
暴力解2
void R_Print(LinkList L)
{
LNode *p = L->next, *r = NULL;
while(p)
{
if(p->next == r)
{
printf("%d", p->data);
r = p;
p = L->next;
}
else
p = p->next;
}
}
也可以用栈,这里不写了。
4、在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。
//删除唯一最小值结点(带头结点)
void Delete_Min(LinkList &L)
{
LNode *p = L->next; //p用来遍历结点,与q作大小比较
LNode *q = p; //q指向当前最小结点
LNode *pre = L; //pre始终指向q的前驱结点
while(p->next != NULL)
{
if(q->data > p->next->data)
{
pre = p;
q = p->next;
}
p = p->next;
}
pre->next = q->next;
free(q);
}
5、将带头结点的单链表就地逆置,“就地”指辅助空间复杂度为O(1)。
将 头结点 和 第一个元素结点 看作整体一起断开,再用头插法:
//就地逆置(带头结点)
void Reverse(LinkList &L)
{
LNode *p = L->next; //p指向头结点后一个结点
LNode *r = p->next; //r指向p结点后一个结点,用来当断链后的第一个结点
LNode *q; //暂时替代r
p->next = NULL; //将p结点之后断开
while(r)
{
q = r; //用q结点暂存r结点来使用头插法
r = r->next; //趁未断链时将r向后移动
q->next = L->next;
L->next = q;
}
}
将头结点断开,再用头插法:
//就地逆置(带头结点)
void Reverse(LinkList &L)
{
LNode *p = L->next; //p指向头结点后一个结点
LNode *r;
L->next = NULL;
while(p)
{
r = p->next;
p->next = L->next; //p结点指向头结点的后继
L->next = p;
p = r;
}
}
6、带头结点的单链表L,设计一个算法使其元素递增有序。
算法和上一题相似,算法很巧妙,建议大家把这题好好琢磨一下。
//递增排序(带头结点)
void Sort(LinkList &L)
{
LNode *p = L->next; //用来指向插入元素
LNode *r = p->next;
LNode *pre;
p->next = NULL;
p = r;
while(p)
{
r = p->next;
pre = L; //pre每次都要从首个元素结点与p结点作比较
while(pre->next != NULL && pre->next->data < p->data)
pre = pre->next; //判断p插入的位置,并使pre指向插入位置的前驱结点
p->next = pre->next;
pre->next = p;
p = r;
}
}
7、在带头结点的无序单链表中,编写一个函数,删除所有介于给定的两个值之间的元素。
//删除结点元素值在(s,t)内的结点
void RangeDelete(LinkList &L)
{
int s, t;
LNode *p = L->next, *q;
LNode *pre = L;
printf("请输入(s,t)的值(空格隔开):");
scanf("%d %d", &s, &t);
while(p)
{
q = p; //q用来暂存p结点
p = p->next; //趁p还未改变时将p往后移动
if(q->data < t && q->data > s)
{
pre->next = q->next;
free(q);
}
else
pre = q; //不满足条件则将pre往后移
}
}
改进(更直观)
void Reverse(LinkList &L)
{
LNode *p = L->next;
LNode *q;
LNode *pre = L;
while(p)
{
if(p->data < t && p->data >s)
{
q = p;
p = p->next;
pre->next = q->next;
free(q);
}
else
{
pre = p;
p = p->next;
}
}
}
8、给定两个单链表,编写算法找出两个链表的公共结点。
虽然有点长,但是挺简单的。
LinkList Search_1st_Common(LinkList L1, LinkList L2)
{
int num = 0;
int len1 = Length(L1), len2 = Length(L2); //计算两个表的表长
LNode *longlist, *shortlist; //指向两个链表
if(len1 >= len2)
{
longlist = L1->next;
shortlist = L2->next;
dist = len1 - len2; //表长之差
}
else
{
longlist = L2->next;
shortlist = L1->next;
dist = len2 - len1;
}
while(dist--)
longlist = longlist->next;
while(longlist)
{
if(shortlist->data == longlist->data)
{
printf("%d", shortlist->data);
num++;
}
longlist = longlist->next;
shortlist = shortlist->next;
}
if(num == 0)
printf("无公共结点!");
}
9、给定一个带头结点的单链表,设head为头指针,结构结点为(data,next),data为整型元素,next为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(不允许使用数组作为辅助空间)。
可以先对单链表排序,再输出,也可以直接暴力解:每次遍历出一个最小值并输出,再接着遍历,直到全部输出完成。
//递增输出并释放空间
void Min_Delete(LinkList &L)
{
while(L->next != NULL) //当元素结点还存在时一直循环
{
LNode *p = L->next;
LNode *pre = L; //指向q的前驱结点
LNode *q = p; //指向最小值的结点
while(p->next != NULL)
{
if(q->data > p->next->data)
{
q = p->next;
pre = p;
}
p = p->next;
}
printf("%d ",q->data);
pre->next = q->next;
free(q); //每次循环找到最小值结点并释放
}
}
10、将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A中含有原表中序号为奇数的元素,B中含有原表中序号为偶数的元素,且保持相对位置不变。
这是我的暴力解,有点过于繁琐,我本来想用while里面装两次分配结点,中间用if隔开,这样完成一次就是偶数,不需要判断次数,但是不小心搞复杂了。大家可能看不太懂,在草稿纸上跟一边就明白了,道理和前几题差不多,我把整个源码都放上来了,接口可以自己调整一下。
#include <iostream>
using namespace std;
typedef struct LNode{ //定义单链表结点类型
int data; //数据域:每个结点存放一个数据元素
struct LNode *next; //指针域:指针指向下一个结点
}LNode, *LinkList;
//拆为A、B两表,A中含有原表中序号为奇数的元素,B为偶数
LinkList DisCreate_1(LinkList &A)
{
int num = 0; //用来记录当前发现了几个结点
LinkList B;
B = (LinkList)malloc(sizeof(LNode));
B->next =NULL;
LNode *p = A->next;
num++; //发现第一个结点
LNode *pre = A; //pre指向p的前驱结点
LNode *q, *r = B; //r用来指向B链表的尾部
while(p->next)
{
q = p;
p = p->next;
num++; //又发现结点
pre->next = p;
r->next = q;
q->next = NULL;
r = q;
if(p->next)
{
pre = p;
p= p->next;
num++; //又发现结点
}
}
if(num % 2 == 1)
{
r->next = p; //最后一个结点位序为偶数时不需要执行这两句
pre->next =NULL;
}
while(B->next)
{
printf("%d", B->next->data);
B = B->next;
}
printf("\n");
while(A->next)
{
printf("%d",A->next->data);
A = A->next;
}
return B;
}
int main()
{
LinkList A; //声明一个指向单链表的指针
int x;
A = (LinkList)malloc(sizeof(LNode)); //建立头结点
A->next = NULL;
LNode *s,*r = A;
printf("请输入插入的值:\n"); //建立表尾指针
scanf("%d", &x);
while(x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL; //表尾指针后继置空
DisCreate_1(A);
return 0;
}
标准答案的确比我的简单,直接判断这一次识别的结点的奇偶性,而且不进行断链操作:
//拆为A、B两表,A中含有原表中序号为奇数的元素,B为偶数
LinkList DisCreate_1(LinkList &A)
{
int i = 0; //用来记录A中结点的序号
LinkList B;
B = (LinkList)malloc(sizeof(LNode));
B->next =NULL;
LNode *ra = A, *rb = B; //分别指向A表和B表的尾结点
LNode *p = A->next;
A->next = NULL; //将A断开,相当于置新
while(p)
{
i++;
if(i % 2 == 0)
{
rb->next = p;
rb = p;
}
else
{
ra->next = p;
ra = p;
}
p = p->next;
}
ra->next = NULL;
rb->next = NULL;
return B;
}
11、设C={a1,b1,a2,b2,……,an,bn}为线性表,采用带头结点的hc单链表存放,设计一个就地算法,将其拆为A={a1,a2,……,an}和B{bn,……,b2,b1}。
暴力解:这题很简单,按照上题的两种方法将C拆为A和B,之后再将B逆置即可,奉上源码:
#include <iostream>
using namespace std;
typedef struct LNode{ //定义单链表结点类型
int data; //数据域:每个结点存放一个数据元素
struct LNode *next; //指针域:指针指向下一个结点
}LNode, *LinkList;
LinkList DisCreate_2(LinkList &C)
{
int i = 0;
LinkList B, A;
B = (LinkList)malloc(sizeof(LNode));
A = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
A->next = NULL;
LNode *ra = A, *rb = B, *p = C->next;
while(p)
{
i++;
if(i % 2 == 1)
{
ra->next = p;
ra = p;
}
if(i % 2 == 0)
{
rb->next = p;
rb = p;
}
p = p->next;
}
ra->next = NULL;
rb->next = NULL;
//接下来对B链表进行逆置
LNode *q = B->next->next;
B->next->next = NULL;
LNode *r;
while(q)
{
r=q;
q = q->next;
r->next = B->next;
B->next = r;
p = r;
}
/*//也可以这样逆置
void Reverse(LinkList &L)
{
LNode *p = L->next; //p指向头结点后一个结点
LNode *r;
L->next = NULL;
while(p)
{
r = p->next;
p->next = L->next; //p结点指向头结点的后继
L->next = p;
p = r;
}
} */
//下面进行输出
while(A->next)
{
printf("%d", A->next->data);
A = A->next;
}
printf("\n");
while(B->next)
{
printf("%d", B->next->data);
B = B->next;
}
}
int main()
{
LinkList C; //声明一个指向单链表的指针
int x;
C = (LinkList)malloc(sizeof(LNode)); //建立头结点
C->next = NULL;
LNode *s,*r = C;
printf("请输入插入的值:\n"); //建立表尾指针
scanf("%d", &x);
while(x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL; //表尾指针后继置空
DisCreate_2(C);
return 0;
}
标准答案:将属于B链表的结点头插法建表,不想写了。
12、在一个递增有序的单链表中,有数值相同的元素存在。设计算法去掉相同的元素(保留一个)。
刚开始因为收到顺序表里类似题的影响,所以思维局限了,想的是记录第一个相等值的点和最后一个相等值的后驱点,然后链接,但是很麻烦。
所以改变思路,可以遍历链表,当该结点值等于后驱结点时,将后驱结点删除,不等时后移。
LinkList Del_Same(LinkList &L)
{
LNode *p = L->next;
LNode *q;
while(p->next)
{
if(p->data == p->next->data) //当后一个结点值与前一个相等时,将后一个删除
{
q = p->next;
p->next = p->next->next;
free(q); //也可以不释放q,因为链已经改变
}
else
p = p->next;
}
}
13、假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。编写算法将这两个单链表归并为一个按元素递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。
第一次程序出了bug,打印完Lc中排好序的元素后,一直在重复打印最后一个结点的值,最后发现时因为最后一个结点的next指向了自己
LinkList MergeList(LinkList &La, LinkList &Lb)
{
LinkList Lc;
LNode *qa, *qb; //pb和pa的前置指针
LNode *pa = La->next, *pb = Lb->next, *pc;
Lc = pc = La;
_____________
while(pa && pb)
{
if(pa->data < pb->data) //若pa结点值小于pb结点,则对pa结点进行头插法
{
qa = pa; //用qa保存pa的前置结点
pa = pa->next; //将pa指针向后移
qa->next = Lc->next;
Lc->next = qa;
pc = qa; //pc始终指向Lc->next
}
else if(pa->data > pb->data)
{
qb = pb; //用qb保存pb的前置结点
pb = pb->next; //将pb指针向后移
qb->next = Lc->next;
Lc->next = qb;
pc = qb;
}
else //相等时取La中的元素,并删除Lb中的结点
{
qa = pa;
pa = pa->next;
qa->next = Lc->next;
Lc->next = qa;
pc = qa;
qb = pb;
pb = pb->next;
free(qb);
}
}
while(pa)
{
qa = pa;
pa = pa->next;
qa->next = Lc->next;
Lc->next = qa;
pc = qa;
}
while(pb)
{
qb = pb;
pb = pb->next;
qb->next = Lc->next;
Lc->next = qb;
pc = qb;
}
free(Lb);
while(pc)
{
printf("%d", pc->data);
pc = pc->next;
}
}
于是进行了修改:在下划线处增加一条Lc->next = NULL;即可
14、设A和B是两个带头结点的单链表,其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表C,要求不破坏A、B的结点。
void Get_Common(LinkList &La, LinkList &Lb)
{
LinkList Lc = La;
LNode *pa = La->next, *pb = Lb->next;
LNode *s, *r = La; //*s用来申请新的结点空间,*r用来使用尾插法
while(pa && pb)
{
if(pa->data < pb->data)
pa = pa->next;
else if(pa->data > pb->data)
pb = pb->next;
else
{
s = (LinkList)malloc(sizeof(LNode));
s->data = pa->data;
r->next = s;
r = s;
pa = pa->next;
pb = pb->next;
}
}
r->next = NULL;
while(Lc->next)
{
printf("%d", Lc->next->data);
Lc = Lc->next;
}
}
15、已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存于A链表中。
进行比较,把较小的结点释放,因为后续肯定不会有与之相等的结点。
void Union(LinkList &La, LinkList &Lb)
{
LNode *pa = La->next, *pb = Lb->next;
LNode *r = La; //*s用来申请新的结点空间,*r用来使用尾插法
LNode *u; //用来释放结点
while(pa && pb)
{
if(pa->data < pb->data)
{
u = pa;
pa = pa->next;
free(u);
}
else if(pa->data > pb->data)
{
u = pb;
pb = pb->next;
free(u);
}
else
{
r->next = pa;
r = pa;
pa = pa->next;
u = pb;
pb = pb->next;
free(u);
}
}
while(pa) //A未遍历完,B已遍历完时释放A中的所有剩余结点
{
u = pa;
pa = pa->next;
free(u);
}
while(pa) //B未遍历完,A已遍历完时释放A中的所有剩余结点
{
u = pb;
pb = pb->next;
free(u);
}
r->next = NULL;
while(La->next)
{
printf("%d", La->next->data);
La = La->next;
}
}
16、两个整数序列A=a1,a2,a3,~,an和B=b1,b2,b3,~,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列。
#include <iostream>
using namespace std;
typedef struct LNode{ //定义单链表结点类型
int data; //数据域:每个结点存放一个数据元素
struct LNode *next; //指针域:指针指向下一个结点
}LNode, *LinkList;
LinkList List_TailInsert(LinkList &L)
{
int x;
L = (LinkList)malloc(sizeof(LNode)); //建立头结点
L->next = NULL;
LNode *s,*r = L;
printf("请输入插入的值:\n"); //建立表尾指针
scanf("%d", &x);
while(x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL; //表尾指针后继置空
return L; //返回头结点L
}
int Pattern(LinkList &La, LinkList &Lb)
{
LNode *pa = La->next, *pb = Lb->next, *q = La->next; //q指向pa的前置结点
while(pb && pa) //若括号里只有pb,则匹配失败时为死循环
{
if(pb->data == pa->data) //A链和B链当前元素值相同则比较下一个元素
{
pb = pb->next;
pa = pa->next;
}
else
{
q = q->next;
pa = q; //从A上次开始进行比较的结点的下一个结点开始比较
pb = Lb->next; //B从头开始匹配
}
}
if(pb == NULL)
return 1;
return 0;
}
int main()
{
int flag;
LinkList La; //声明一个指向单链表的指针
LinkList Lb;
List_TailInsert(La);
List_TailInsert(Lb);
if(flag = Pattern(La, Lb))
printf("B是A的子序列!");
else
printf("B不是A的子序列!");
return 0;
}