【前面使用的所有链表的定义在第29节】
试题1:
设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。
首先来看非递归算法,暴力遍历:
- int Del(LinkList &L,ElemType x){
- //此函数实现删除链表中为x的元素
- LNode *p,*q;
- p = L; //p指向头结点
- q = L->next; //q指向首元结点
- while(q->next!=NULL){
- if(q->data == x){
- p->next = q->next;
- free(q);
- q = p->next;
- }
- else{
- p = p->next;
- q = q->next;
- }
- }
- return 0;
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- Del(L, 3);
- PrintList(L);
- return 0;
- }
然后看递归算法:
- int Del(LinkList &L,ElemType x){
- //此函数实现递归删除链表中为x的元素
- LNode *p;
- if(L==NULL)
- return 0;
- if(L->data==x){
- p = L;
- L = L->next;
- free(p);
- Del(L, x);
- }
- else{
- Del(L->next, x);
- }
- return 0;
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- Del(L, 3);
- PrintList(L);
- return 0;
- }
这里分析一下怎么递归的:从头开始,如果首元结点就是被删掉的x那很显然;如果不是,这时候执行Del(L->next, x),如果第2个是被删掉的元素,进入if(L->next->data==x)分支,此时p=L实际上执行p=L->next,p指向了第2个被删除的结点;第2行L = L->next执行的是L->next=L->next->next,把首元结点的next域修改为指向第3个结点;然后free(p)释放空间,最后继续执行Del(L,x)相当于Del(L->next, x),这时L->next是第3个结点,依次递归。
试题2:(与题1差不多)
试题3:L是带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值
此题可以使用栈来解决,这里主要提书上的递归方法:
- void AdversePrint(LinkList L){
- //此函数实现反向打印链表L的元素
- if(L!=NULL){
- AdversePrint(L->next); //递归
- printf("[%d] ", L->data); //打印当前元素
- }
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- AdversePrint(L->next); //带头结点的链表,这里L->next指向首元结点
- return 0;
- }
试题4:编写在带头结点的单链表L中删除一个最小值结点的高效算法。
(注意不要写野指针!)
- void Deletexmin(LinkList &L){
- //此函数实现删除链表最小值元素
- LNode *p, *q, *m, *n;
- int a;
- p = L; //p指向头结点
- q = L->next; //q指向首元结点
- a = q->data; //首元结点元素赋给a
- m = p;
- n = q;
- while (q->next != NULL){
- q = q->next; //指针后移
- p = p->next; //指针后移
- if(q->data < a){
- a = q->data; //更新a
- m = p; //记录当前最小值的前驱结点
- n = q; //记录当前最小值结点
- }
- }
- m->next = n->next;
- free(n);
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- Deletexmin(L);
- PrintList(L);
- return 0;
- }
试题5:将单链表的元素就地逆置
利用头插法重新建立链表:
- void ReverseL(LinkList &L){
- //此函数实现逆置单链表所有元素
- LNode *p, *q;
- p = L;
- q = L->next; //q指向首元结点
- p = q;
- q = q->next; //q指向第二个结点
- p->next = NULL;
- if(q==NULL)
- return ;
- else{
- while (q!=NULL){
- p = q->next; //p移向首元结点
- q->next = L->next;
- L->next = q; //修改头结点指针,头插法
- q = p;
- }
- }
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- ReverseL(L);
- PrintList(L);
- return 0;
- }
试题6:有一个带头结点的单链表L,设计算法使其递增有序。
采用插入排序的思想:
- void SortL(LinkList &L){
- //此函数实现递增排序单链表所有元素
- LNode *p, *q, *r;
- p = L->next; //p工作在排好序的链表
- q = p->next; //q是待排序元素,即第二个结点,断开之后的第一个结点
- r = q->next; //r在q之后
- p->next = NULL; //断开
- while(q!=NULL){
- r = q->next;
- p = L;
- while(q->data > p->next->data && p->next!=NULL){
- p = p->next;
- }
- q->next = p->next;
- p->next = q;
- q = r;
- }
- }
-
- int main(){
- LinkList L;
- InitList(L);
- Create(L);
- PrintList(L);
- SortL(L);
- PrintList(L);
- return 0;
- }
输出:
- 请输入要输入元素的个数:5
- 请输入第1元素的值:5
- 请输入第2元素的值:3
- 请输入第3元素的值:1
- 请输入第4元素的值:2
- 请输入第5元素的值:4
- 当前单链表的所有元素:[5] [3] [1] [2] [4]
- 当前单链表的所有元素:[1] [2] [3] [4] [5]
试题7:(修改试题1的参数条件if(q->data == x)即可)
试题8:求两个链表的公共链表
如果有公共链表,一定是这样的Y型拓扑结构:

- int LengthL(LinkList L){
- //此函数实现求解链表长度
- LNode *p;
- p = L;
- int i = 0;
- while(p->next!=NULL){
- p = p->next;
- i++;
- }
- return i;
- }
-
- LinkList Search_1st_common(LinkList L1,LinkList L2){
- //此函数实现查找两个链表的公共结点
- int L1length = LengthL(L1);
- int L2length = LengthL(L2);
- printf("%d", LengthL(L1));
- printf("%d", LengthL(L2));
-
- int delta = 0; //记录长链表比短链表多多少
- LNode *longList, *shortList;
-
- if(L1length > L2length){
- longList = L1->next; //L1更长,longList指向L1首元结点
- shortList = L2->next;
- delta = L1length - L2length;
- }
- else{
- longList = L2->next; //L2更长,longList指向L2首元结点
- shortList = L1->next;
- delta = L2length - L1length;
- }
- printf("%d", longList->data);
- printf("%d", shortList->data);
- printf("%d", delta);
-
- for (int i = 0; i < delta;i++){ // 移动longList指针至delta位置
- longList = longList->next;
- }
- printf("%d", longList->data);
-
- //经过以上移动,此时longList指针与shortList指针指的剩余长度一样
- while (longList!=shortList && longList != NULL){
- longList = longList->next;
- shortList = shortList->next;
- }
- return longList;
- }
-
- void PrintResult(LinkList L){
- if(L == NULL)
- printf("公共结点不存在!");
- else
- PrintList(L);
- }
-
- int main(){
- LinkList L1,L2;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- InitList(L2);
- Create(L2);
- PrintList(L2);
- PrintResult(Search_1st_common(L1, L2));
- return 0;
- }
输出:
- 请输入要输入元素的个数:2
- 请输入第1元素的值:1
- 请输入第2元素的值:2
- 当前单链表的所有元素:[1] [2]
- 请输入要输入元素的个数:5
- 请输入第1元素的值:3
- 请输入第2元素的值:4
- 请输入第3元素的值:5
- 请输入第4元素的值:6
- 请输入第5元素的值:7
- 当前单链表的所有元素:[3] [4] [5] [6] [7]
- 253136公共结点不存在!
-
- 请输入要输入元素的个数:2
- 请输入第1元素的值:1
- 请输入第2元素的值:2
- 当前单链表的所有元素:[1] [2]
- 请输入要输入元素的个数:3
- 请输入第1元素的值:3
- 请输入第2元素的值:4
- 请输入第3元素的值:5
- 当前单链表的所有元素:[3] [4] [5]
- 233114公共结点不存在!
在写这段代码的时候踩了一个坑:大家观察下面两段代码:
- void Search_1st_common(){
- //此函数实现查找两个链表的公共结点
- int L1length = 3;
- int L2length = 5;
- int delta = 0; //记录长链表比短链表多多少
-
- if(L1length > L2length){
- delta = L1length - L2length;
- }
- else{
- delta = L2length - L1length;
- }
- printf("%d", delta);
- }
-
- int main(){
- Search_1st_common();
- return 0;
- }
- void Search_1st_common(){
- //此函数实现查找两个链表的公共结点
- int L1length = 3;
- int L2length = 5;
- int delta = 0; //记录长链表比短链表多多少
-
- if(L1length > L2length){
- int delta = L1length - L2length;
- }
- else{
- int delta = L2length - L1length;
- }
- printf("%d", delta);
- }
-
- int main(){
- Search_1st_common();
- return 0;
- }
打印delta的结果分别是2和0.后一段代码中if...else...结构体内相当于重新定义了一个delta,当结构体执行结束后又被释放掉了,所以delta的返回结果是0.
试题9:试写出算法:按递增次序输出单链表中各节点的数据元素,并释放结点所占的内存空间。
此题可以仿照试题4.不多解释。
- void Printelement(LinkList &L){
- //此函数实现按递增次序输出单链表数据元素,并释放结点所占的存储空间
- LNode *p, *q; // q记录了每次遍历的最小值结点的前驱
- int min;
- while(L->next!=NULL){ //最后结束条件是L删成空
- p = L;
- q = L;
- min = p->next->data; //第1个元素
- while(p->next != NULL){
- if(p->next->data < min){
- min = p->next->data;
- q = p;
- }
- p = p->next;
- }
- printf("[%d]", q->next->data);
- p = q->next;
- q->next = p->next;
- free(p);
- }
- }
-
- int main(){
- LinkList L1;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- Printelement(L1);
- return 0;
- }
试题10:将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数的元素。
这里不采用书上写的设置访问序号变量。由于有周期性,适当控制指针即可:
- void Printelement(LinkList &L){
- //此函数实现将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数的元素。
- LNode *p, *q, *r; //r指针工作在新链表中
- LinkList L0; //L0相当于题目中的B
- InitList(L0);
- p = L->next;
- q = p->next; //q指向第2个结点
- r = L0;
- while(p!=NULL&&q!=NULL){
- r->next = q;
- p->next = q->next;
- if(p->next == NULL)
- break;
- p = p->next;
- if(q->next == NULL)
- break;
- q = p->next;
- r = r->next;
- r->next = NULL;
- }
- PrintList(L);
- PrintList(L0);
- }
-
- int main(){
- LinkList L1;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- Printelement(L1);
- return 0;
- }
输出:
- 当前单链表的所有元素:[1] [2] [3] [4] [5] [6]
- 当前单链表的所有元素:[1] [3] [5]
- 当前单链表的所有元素:[2] [4] [6]
-
- 当前单链表的所有元素:[1] [2] [3] [4] [5] [6] [7]
- 当前单链表的所有元素:[1] [3] [5] [7]
- 当前单链表的所有元素:[2] [4] [6]
试题11:设
为线性表,采用带头结点的hc单链表存放,设计一个就地算法,将其拆分成两个线性表,使得
,
此题跟第10题很像,修改指针即可。
- void Printelement(LinkList &L){
- //此函数实现将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数倒序的元素。
- LNode *p, *q, *r; //r指针工作在新链表中
- LinkList L0; //L0相当于题目中的B
- InitList(L0);
- p = L->next;
- q = p->next; //q指向第2个结点
- r = L0->next;
- while(p!=NULL&&q!=NULL){
- L0->next = q; //头插法
- p->next = q->next;
- q->next = r;
- if(p->next == NULL)
- break;
- p = p->next;
- if(p->next == NULL)
- break;
- q = p->next;
- r = L0->next;
- }
- PrintList(L);
- PrintList(L0);
- }
-
- int main(){
- LinkList L1;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- Printelement(L1);
- return 0;
- }
输出:
- 当前单链表的所有元素:[1] [2] [3] [4] [5] [6]
- 当前单链表的所有元素:[1] [3] [5]
- 当前单链表的所有元素:[6] [4] [2]
试题12:在一个递增有序的单链表中,删除所有数值相同的元素,使表中不再有重复的元素。
- void DeleteSame(LinkList &L){
- //此函数实现把递增单链表的重复元素删除
- LNode *p, *q;
- p = L->next; //第一个元素
- q = p->next;
- while(q!=NULL){
- if(p->data == q->data){
- q = q->next;
- free(p->next);
- p->next = q;
- }
- else{
- p = p->next;
- q = q->next;
- }
- }
- }
-
- int main(){
- LinkList L1;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- DeleteSame(L1);
- PrintList(L1);
- return 0;
- }
输出:
- 当前单链表的所有元素:[1] [2] [2] [3] [3] [4] [4] [5]
- 当前单链表的所有元素:[1] [2] [3] [4] [5]
试题13:假设有两个按元素值递增次序排列的线性表,以单链表形式存储。编写算法把这两个单链表归并为一个按元素值递减的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。
此题属于简单题,最后使用头插法倒序即可。
- LinkList Merge(LinkList L1,LinkList L2){
- //此函数实现把两个递增的单链表合并成递减的单链表
- LNode *p, *q; //L1指针
- LNode *m, *n; //L2指针
- LNode *r; //新表,头插法
- LinkList L;
- InitList(L); //初始化
- p = L1->next; //L1第1个结点
- m = L2->next; //L2第1个结点
- q = p->next; //L1第2个结点
- n = m->next; //L2第2个结点
- r = L->next;
- while(L1->next!=NULL && L2->next!=NULL){
- if(p->data < m->data){
- L->next = p;
- p->next = r;
- p = q;
- r = L->next;
- if(q == NULL)
- break;
- q = q->next;
- }
- else{
- L->next = m;
- m->next = r;
- m = n;
- r = L->next;
- if(n == NULL)
- break;
- n = n->next;
- }
- }
- if(L1->next == NULL){
- while(L2->next!=NULL){
- L->next = m;
- m->next = r;
- m = n;
- r = L->next;
- if(n == NULL)
- break;
- n = n->next;
- }
- }
- else{
- while(L1->next!=NULL){
- L->next = p;
- p->next = r;
- p = q;
- r = L->next;
- if(q == NULL)
- break;
- q = q->next;
- }
- }
- return L;
- }
-
-
- int main(){
- LinkList L1, L2;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- InitList(L2);
- Create(L2);
- PrintList(L2);
- PrintList(Merge(L1,L2));
- return 0;
- }
输出:
- 请输入要输入元素的个数:3
- 请输入第1元素的值:1
- 请输入第2元素的值:2
- 请输入第3元素的值:4
- 当前单链表的所有元素:[1] [2] [4]
- 请输入要输入元素的个数:2
- 请输入第1元素的值:1
- 请输入第2元素的值:2
- 当前单链表的所有元素:[1] [2]
- 当前单链表的所有元素:[4] [2] [2] [1] [1]
试题14:设A和B是两个单链表(带头结点),其中元素递增有序,设计一个算法从A和B中公共元素产生单链表C,要求不破坏A,B的结点。
- LinkList Intersection(LinkList L1,LinkList L2){
- //此函数实现产生单链表,包含两个链表的公共元素
- LNode *p; //L1指针
- LNode *q; //L2指针
- LNode *r; //新表的指针
- LinkList L;
- InitList(L);
- p = L1->next;
- q = L2->next;
- r = L;
- while(p!=NULL && q!=NULL){
- if(p->data < q->data){
- p = p->next;
- }
- else if(p->data = q->data){
- LNode *s = (LNode *)malloc(sizeof(LNode));
- s->data = p->data;
- s->next = NULL;
- r->next = s;
- r = r->next;
- p = p->next; //后移
- q = q->next;
- }
- else{
- q = q->next;
- }
- }
- return L;
- }
-
- int main(){
- LinkList L1, L2;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- InitList(L2);
- Create(L2);
- PrintList(L2);
- PrintList(Intersection(L1,L2));
- return 0;
- }
输出:
- 请输入要输入元素的个数:4
- 请输入第1元素的值:1
- 请输入第2元素的值:2
- 请输入第3元素的值:3
- 请输入第4元素的值:4
- 当前单链表的所有元素:[1] [2] [3] [4]
- 请输入要输入元素的个数:4
- 请输入第1元素的值:2
- 请输入第2元素的值:3
- 请输入第3元素的值:4
- 请输入第4元素的值:5
- 当前单链表的所有元素:[2] [3] [4] [5]
- 当前单链表的所有元素:[2] [3] [4]
试题15:此题要求同14,但要求合并链表存储在A中。
- LinkList Intersection(LinkList L1,LinkList L2){
- //此函数实现产生单链表,包含两个链表的公共元素
- LNode *p, *m; // L1指针,m保存待删结点的前驱,p待删结点
- LNode *q; //L2指针
- m = L1;
- p = L1->next;
- q = L2->next;
-
- while(p!=NULL && q!=NULL){
- if(p->data < q->data){
- m->next = p->next;
- free(p);
- p = m->next;
-
- }
- else if(p->data == q->data){
- p = p->next;
- q = q->next;
- m = m->next;
- }
- else{
- q = q->next;
- }
- }
-
- if(q == NULL){
- while(p!=NULL){
- m->next = p->next;
- free(p);
- p = m->next;
- }
- }
- return L1;
- }
-
- int main(){
- LinkList L1, L2;
- InitList(L1);
- Create(L1);
- PrintList(L1);
- InitList(L2);
- Create(L2);
- PrintList(L2);
- PrintList(Intersection(L1,L2));
- return 0;
- }
输出:
- 当前单链表的所有元素:[3] [5] [7] [9]
- 当前单链表的所有元素:[4] [5] [8] [9]
- 当前单链表的所有元素:[5] [9]
-
- 当前单链表的所有元素:[2] [3] [4] [5]
- 当前单链表的所有元素:[1] [2] [3] [4]
- 当前单链表的所有元素:[2] [3] [4]
-
- 当前单链表的所有元素:[5] [6] [7] [8]
- 当前单链表的所有元素:[1] [2] [3] [4]
- 当前单链表的所有元素: