王道数据结构课后代码题p40 第13——18(c语言代码实现)

目录

13.假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。

14.设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和 B中的公共元素产生单链表 C,要求不破坏A、B的结点。

 15.已知两个链表A和 B分别表示两个集合,其元素递增排列。编制函数,求 A与B的交集,并存放于A链表中。

16.两个整数序列A=a1,..,am和B=b1,..,bn已经存入两个单链表中,设计一个算法,判断序列 B是否是序列 A 的连续子序列。

17.设计一个算法用于判断带头结点的循环双链表是否对称 

 18.有两个循环单链表,链表头指针分别为 h1 和 h2,编写一个函数将链表 h2 链接到链h1 之后,要求链接后的链表仍保持循环链表形式。


13.假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。

 本题代码如下

void hebing(linklist* A, linklist* B)
{
	lnode* r, * pa = (*A)->next, * pb = (*B)->next;
	(*A)->next = NULL;
	while (pa && pb)//当两个链表都不为空时
	{
		if (pa->data <= pb->data)
		{
			r = pa->next;//r暂存pa的后继节点指针
			pa->next = (*A)->next;//头插法一次插入到A中
			(*A)->next = pa;
			pa = r;//使pa恢复为当前待比较的结点
		}
		else
		{
			r = pb->next;
			pb->next = (*A)->next;
			(*A)->next = pb;
			pb = r;
		}
}
		if (pa)//通常情况会剩下一个链表非空
			pb = pa;
		while (pb)
		{
			r = pb->next;
			pb->next = (*A)->next;
			(*A)->next = pb;
			pb = r;
		}
	free(*B);
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int n = 5;
int a[5] = { 1,3,5,7,9 };
int b[5] = { 2,4,6,8,10 };
void buildlinklist(linklist* L,int arr[],int n)
{
	*L = (lnode*)malloc(sizeof(lnode));
	(*L)->next = NULL;
	lnode* s = *L, * r = *L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		s = (lnode*)malloc(sizeof(lnode));
		s->data = arr[i];
		s->next = r->next;
		r->next = s;
		r = s;
	}
	r->next = NULL;
}
void hebing(linklist* A, linklist* B)
{
	lnode* r, * pa = (*A)->next, * pb = (*B)->next;
	(*A)->next = NULL;
	while (pa && pb)//当两个链表都不为空时
	{
		if (pa->data <= pb->data)
		{
			r = pa->next;//r暂存pa的后继节点指针
			pa->next = (*A)->next;//头插法一次插入到A中
			(*A)->next = pa;
			pa = r;//使pa恢复为当前待比较的结点
		}
		else
		{
			r = pb->next;
			pb->next = (*A)->next;
			(*A)->next = pb;
			pb = r;
		}
}
		if (pa)//通常情况会剩下一个链表非空
			pb = pa;
		while (pb)
		{
			r = pb->next;
			pb->next = (*A)->next;
			(*A)->next = pb;
			pb = r;
		}
	free(*B);
}
void print(linklist* L)
{
	lnode* p = (*L)->next;
	while (p)
	{
		printf("%d ", p->data);
		p = p->next;
	}
}
int main()
{
	linklist A, B;
	buildlinklist(&A, a, n);
	buildlinklist(&B, b, n);
	printf("单链表A为:");
	print(&A);
	printf("\n单链表B为:");
	print(&B);
	hebing(&A, &B);
	printf("\n合并后的单链表为:");
	print(&A);

14.设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和 B中的公共元素产生单链表 C,要求不破坏A、B的结点。

 表A、B都有序,可从第一个元素起依次比较A、B两表的元素,若元素值不相等,则值小的指针往后移,若元素值相等,则创建一个值等于两结点的元素值的新结点,使用尾插法插入到新的链表中,并将两个原表指针后移一位,直到其中一个链表遍历到表尾

本题代码如下

linklist identical(linklist* A, linklist* B)//将两个链表的公共元素合并产生新链表
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* r;
	lnode* s;
	lnode* C = (lnode*)malloc(sizeof(lnode));
	C->next = NULL;
	r = C;
	while (ra && rb)//循环跳出条件(有一个为空就跳出)
	{
		if (ra->data < rb->data)//若A的当前元素较小,后移指针
			ra = ra->next;
		else if (ra->data > rb->data)//若B的当前元素较小,后移指针
			rb = rb->data;
		else//找到公共结点
		{
			s = (lnode*)malloc(sizeof(lnode));//创建一个结点
			s->data = ra->data;//复制产生的结点*s
			r->next = s;
			r = s;
			ra = ra->next;//A和B继续向后遍历
			rb = rb->next;
		}
	}
	r->next = NULL;//C的表尾结点置空
	return C;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int n = 5;
int a[5] = { 1,2,3,4,5 };
int b[5] = { 2,3,4,5,6 };
void buildlinklist(linklist *L,int arr[],int n)//创建链表
{
	*L = (lnode*)malloc(sizeof(lnode));
	(*L)->next = NULL;
	lnode* s = *L, * r = *L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		s = (lnode*)malloc(sizeof(lnode));
		s->data = arr[i];
		s->next = r->next;
		r->next = s;
		r = s;
	}
	r->next = NULL;
}
linklist identical(linklist* A, linklist* B)//将两个链表的公共元素合并产生新链表
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* r;
	lnode* s;
	lnode* C = (lnode*)malloc(sizeof(lnode));
	C->next = NULL;
	r = C;
	while (ra && rb)//循环跳出条件(有一个为空就跳出)
	{
		if (ra->data < rb->data)//若A的当前元素较小,后移指针
			ra = ra->next;
		else if (ra->data > rb->data)//若B的当前元素较小,后移指针
			rb = rb->data;
		else//找到公共结点
		{
			s = (lnode*)malloc(sizeof(lnode));//创建一个结点
			s->data = ra->data;//复制产生的结点*s
			r->next = s;
			r = s;
			ra = ra->next;//A和B继续向后遍历
			rb = rb->next;
		}
	}
	r->next = NULL;//C的表尾结点置空
	return C;
}
void print(linklist* L)//输出单链表
{
	lnode* k = (*L)->next;
	while (k)
	{
		printf("->%d", k->data);
		k = k->next;
	}
}
int main()
{
	linklist A,B;
	buildlinklist(&A,a,n);
	buildlinklist(&B,b,n);
	printf("A链表为:");
	print(&A);
	printf("\nB链表为:");
	print(&B);
	linklist C=identical(&A, &B);
	printf("\n合并后的链表为:");
	print(&C);
	return 0;
}

还可以用另外一种写法(暴力求解)

linklist combine(linklist* A, linklist* B)
{
	lnode* C = (lnode*)malloc(sizeof(lnode));
	(*C).next = NULL;
	lnode *rc = C;
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* p = ra, * q;
	while (p)//让ra与rb一个一个比较
	{
		p = ra->next;
		while (rb)
		{
			if (ra->data == rb->data)//ra的值等于rb的值时
			{
				ra->next = rc->next;//ra链接到rc的后面
				rc->next = ra;
				rc = ra;
				break;//直接跳出
			}
			else
				rb = rb->next;
		}
		ra = p;
		rb = (*B)->next;
	}
	return C;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int a[5] = { 1,2,3,4,5 };
int sza = 5;
int szb = 6;
int b[6] = { 2,3,4,5,7,6 };
void buildlinklist(linklist* L,int c[],int sz)
{
	*L = (lnode*)malloc(sizeof(lnode));
	(*L)->next = NULL;
	lnode* s, * r = *L;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		s = (lnode*)malloc(sizeof(lnode));
		s->data = c[i];
		s->next = r->next;
		r->next = s;
		r = s;
	}
	r->next = NULL;
}
linklist combine(linklist* A, linklist* B)
{
	lnode* C = (lnode*)malloc(sizeof(lnode));
	(*C).next = NULL;
	lnode *rc = C;
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* p = ra, * q;
	while (p)//让ra与rb一个一个比较
	{
		p = ra->next;
		while (rb)
		{
			if (ra->data == rb->data)//ra的值等于rb的值时
			{
				ra->next = rc->next;//ra链接到rc的后面
				rc->next = ra;
				rc = ra;
				break;//直接跳出
			}
			else
				rb = rb->next;
		}
		ra = p;
		rb = (*B)->next;
	}
	return C;
}
void print(linklist* L)
{
	lnode* k = (*L)->next;
	while (k)
	{
		printf("%d ", k->data);
		k = k->next;
	}
}
int main()
{
	linklist A, B;
	buildlinklist(&A,a,sza);
	print(&A);
	printf("\n");
	buildlinklist(&B, b, szb);
	print(&B);
	linklist C=combine(&A, &B);
	printf("\n");
	print(&C);
	return 0;
}

 15.已知两个链表A和 B分别表示两个集合,其元素递增排列。编制函数,求 A与B的交集,并存放于A链表中。

 采用归并的思想,设置两个工作指针ra,rb,对两个链表进行归并扫描,只有同时出现在两集合中的元素才链接到结果表中且仅保留一个,其他的结点全部释放。当一个链表遍历完毕后,释放另一个表中剩下的全部结点

本题代码如下

linklist Union(linklist* A, linklist* B)
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* r;//r作为释放的结点
	lnode* rc =*A;
	while (ra && rb)
	{
		if (ra->data == rb->data)//交集并入结果表中
		{
			rc->next = ra;//A中结点链接到结果表中
			rc = ra;
			ra = ra->next;
			r = rb;//B中的释放
			rb = rb->next;
			free(r);
		}
		else if (ra->data < rb->data)//若A中当前结点小于B中当前结点值
		{
			r = ra;
			ra = ra->next;//后移A
			free(r);
		}
		else//若B中当前结点小于A中当前结点值
		{
			r = rb;
			rb = rb->next;//后移B
			free(r);
		}
	}
	while (ra)//B遍历完,A没有遍历完
	{
		r = ra;
		ra = ra->next;
		free(r);//释放A中剩余结点
	}
	while (rb)//A遍历完,B没有遍历完
	{
		r = rb;
		rb = rb->next;
		free(r);//释放B中剩余结点
	}
	rc->next = NULL; //结果表的表尾结点置空
	free(*B);//释放B的表头结点
	return *A;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int n = 5;
int a[5] = { 1,2,3,4,5 };
int b[3] = { 2,3,4 };
void buildlinklist(linklist *L,int arr[],int n)//创建链表
{
	*L = (lnode*)malloc(sizeof(lnode));
	(*L)->next = NULL;
	lnode* s = *L, * r = *L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		s = (lnode*)malloc(sizeof(lnode));
		s->data = arr[i];
		s->next = r->next;
		r->next = s;
		r = s;
	}
	r->next = NULL;
}
linklist Union(linklist* A, linklist* B)
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* r;//r作为释放的结点
	lnode* rc =*A;
	while (ra && rb)
	{
		if (ra->data == rb->data)//交集并入结果表中
		{
			rc->next = ra;//A中结点链接到结果表中
			rc = ra;
			ra = ra->next;
			r = rb;//B中的释放
			rb = rb->next;
			free(r);
		}
		else if (ra->data < rb->data)//若A中当前结点小于B中当前结点值
		{
			r = ra;
			ra = ra->next;//后移A
			free(r);
		}
		else//若B中当前结点小于A中当前结点值
		{
			r = rb;
			rb = rb->next;//后移B
			free(r);
		}
	}
	while (ra)//B遍历完,A没有遍历完
	{
		r = ra;
		ra = ra->next;
		free(r);//释放A中剩余结点
	}
	while (rb)//A遍历完,B没有遍历完
	{
		r = rb;
		rb = rb->next;
		free(r);//释放B中剩余结点
	}
	rc->next = NULL; //结果表的表尾结点置空
	free(*B);//释放B的表头结点
	return *A;
}
void print(linklist* L)//输出单链表
{
	lnode* k = (*L)->next;
	while (k)
	{
		printf("%d ", k->data);
		k = k->next;
	}
}
int main()
{
	linklist A,B;
	buildlinklist(&A,a,n);
	buildlinklist(&B,b,n);
	printf("A链表为:");
	print(&A);
	printf("\nB链表为:");
	print(&B);
	linklist C=Union(&A, &B);
	printf("\n合并后的链表为:");
	print(&C);
	return 0;
}

16.两个整数序列A=a1,..,am和B=b1,..,bn已经存入两个单链表中,设计一个算法,判断序列 B是否是序列 A 的连续子序列。

  因为两个整数序列已存入两个链表中,操作从两个链表的第一个结点开始,若对应数据相等,则后移指针;若对应数据不等,则A链表从上次开始比较结点的后继开始,B 链表仍从第一个结点开始比较,直到 B 链表到尾表示匹配成功。A 链表到尾而 B 链表未到尾表示失败操作中应记住 A链表每次的开始结点,以便下次匹配时好从其后继开始。

本题代码如下

int pattern(linklist* A, linklist* B)//相当于模式匹配
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* p = ra;//p为暂存每一趟A的开始结点
	while (ra&&rb)
	{
		if (ra->data == rb->data)//结点值相同就向后遍历
		{
			ra = ra->next;
			rb = rb->next;
		}
		else
		{
			p = p->next;
			ra = p;//A链表从新的开始比较结点
			rb = (*B)->next;//重新从B的第一个开始变脸
		}
	}
		if (rb == NULL)
			return 1;
		else
			return 0;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int a[5] = { 1,2,3,4,5 };
int b[3] = { 2,3,4 };
void buildlinklist(linklist *L,int arr[],int x)//创建链表
{
	*L = (lnode*)malloc(sizeof(lnode));
	(*L)->next = NULL;
	lnode* s = *L, * r = *L;
	int i = 0;
	for (i = 0; i < x; i++)
	{
		s = (lnode*)malloc(sizeof(lnode));
		s->data = arr[i];
		s->next = r->next;
		r->next = s;
		r = s;
	}
	r->next = NULL;
}
int pattern(linklist* A, linklist* B)//相当于模式匹配
{
	lnode* ra = (*A)->next, * rb = (*B)->next;
	lnode* p = ra;//p为暂存每一趟A的开始结点
	while (ra&&rb)
	{
		if (ra->data == rb->data)//结点值相同就向后遍历
		{
			ra = ra->next;
			rb = rb->next;
		}
		else
		{
			p = p->next;
			ra = p;//A链表从新的开始比较结点
			rb = (*B)->next;//重新从B的第一个开始变脸
		}
	}
		if (rb == NULL)
			return 1;
		else
			return 0;
}
void print(linklist* L)//输出单链表
{
	lnode* k = (*L)->next;
	while (k)
	{
		printf("%d ", k->data);
		k = k->next;
	}
}
int main()
{
	linklist A,B;
	buildlinklist(&A,a,5);
	buildlinklist(&B,b,3);
	printf("A链表为:");
	print(&A);
	printf("\nB链表为:");
	print(&B);
	int ret=pattern(&A, &B);
	if (ret == 1)
	{
		printf("\nB是A的子序列");
	}
	else
	{
		printf("\nB不是A的子序列");
	}
	return 0;
}

17.设计一个算法用于判断带头结点的循环双链表是否对称 

 补充循环双链表的知识:循环双链表是一种链表数据结构,在链表的基础上增加了头尾相连的循环特性,即链表的最后一个节点指向第一个节点,同时每个节点除了储存下一个节点的指针外还储存前一个节点的指针,这样可以实现在链表两端快速插入和删除元素的操作。

与普通双链表相比,循环双链表的特点是最后一个节点的 next 指针指向第一个节点,第一个节点的 prior指针指向最后一个节点,这样就构成了一个环形结构。循环双向链表可以作为一种序列容器,可以支持在任意位置插入和删除节点,并且可以通过指向任意节点的指针在 O(1) 时间内访问该节点前一个和后一个节点。

在循环双链表L中,某结点*p为尾结点时,p->next==L;当循环双链表为空表时,其头结点的prior域和next域都等于L。

循环双链表的判空条件为:L->prior==L;

                                           L->next==L;

本题算法思想:让p从左向右扫描,q从右向左扫描,直到他们指向同一结点或相邻结点为止,若他们值相同,则继续进行下去,否则返回0。若比较全部相等,则返回1。

本题需要注意的是循环的跳出条件(p!=q是处理结点个数为奇数的,q->next!=p是判断结点个数为偶数。

偶数情况:

奇数情况:

本题代码如下

int symmetry(linklist* L)//判断循环双链表是否对称
{
	lnode* p = (*L)->next, * q = (*L)->prior;
	while (p!=q && q->next!= p)/* ***注意这里的跳出条件(p!=q是处理结点个数为奇数的,q->next!=p是判断结点个数为偶数)*/
	{
		if (p->data != q->data)
		{
			return 0;
		}
			p = p->next;
			q = q->prior;
	}
		return 1;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* prior;
	struct lnode* next;
}lnode,*linklist;
int n = 5;
int a[5] = { 1,2,3,2,1 };
void buildlinklist(linklist* L)//建立循环双链表
{
	(*L)->next =*L;//初始化头结点
	(*L)->prior =*L;
	lnode * r = *L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		lnode *s = (lnode*)malloc(sizeof(lnode));//创建新结点
		s->data = a[i];
		s->next = r->next;//插入新结点
		s->prior = r;
		r->next->prior= s;
		r->next = s;
		r = s;
	}
}
int symmetry(linklist* L)//判断循环双链表是否对称
{
	lnode* p = (*L)->next, * q = (*L)->prior;
	while (p!=q && q->next!= p)
	{
		if (p->data != q->data)
		{
			return 0;
		}
			p = p->next;
			q = q->prior;
	}
		return 1;
}
void print(linklist* L)//输出链表
{
	lnode* k = (*L)->next;
	while (k!=*L)
	{
		printf("%d ", k->data);
		k = k->next;
	}
}
int main()
{
	linklist L=(lnode*)malloc(sizeof(lnode));//创建头结点
	L->next = L;
	L->prior = L;
	buildlinklist(&L);//构建循环双链表
	printf("原始单链表为:");
	print(&L);
	int ret = symmetry(&L);
	if (ret == 1)
	{
		printf("\n带头结点的循环双链表对称");
	}
	else
	{
		printf("带头结点的循环双链表不对称");
	}
	return 0;
}

 18.有两个循环单链表,链表头指针分别为 h1 和 h2,编写一个函数将链表 h2 链接到链h1 之后,要求链接后的链表仍保持循环链表形式。

 本题代码如下

linklist lianjie(linklist *h1, linklist *h2)//两个循环单链表链接
{
	lnode* p, * q;
	p = (*h1)->next;
	while (p->next != *h1)//寻找h1的尾结点
		p = p->next;
	q = (*h2)->next;
	while (q->next!=*h2)//寻找h2的尾结点
		q = q->next;
	p->next =(*h2)->next;//将h2链接到h1的后面
	q->next =*h1;//令h2的尾结点指向h1
	return h1;
}

完整测试代码

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
	int data;
	struct lnode* next;
}lnode,*linklist;
int n = 5;
int a1[5]={ 1,2,3,4,5 };
int a2[5]={ 6,7,8,9,10 };
void buildlinklist(linklist* L,int arr[],int n)//建立循环单链表
{
	(*L)->next = *L;
	lnode* r = *L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		lnode* s = (lnode*)malloc(sizeof(lnode));
		s->data = arr[i];
		r->next = s;
		r = r->next;
	}
	r->next = *L;
}
linklist lianjie(linklist *h1, linklist *h2)//两个循环单链表链接
{
	lnode* p, * q;
	p = (*h1)->next;
	while (p->next != *h1)//寻找h1的尾结点
		p = p->next;
	q = (*h2)->next;
	while (q->next!=*h2)//寻找h2的尾结点
		q = q->next;
	p->next =(*h2)->next;//将h2链接到h1的后面
	q->next =*h1;//令h2的尾结点指向h1
	return h1;
}
void print(linklist *L)//输出循环单链表
{
	lnode* k = (*L)->next;
	while (k != *L)
	{
		printf("%d ",k->data);
		k = k->next;
	}
}
int main()
{
	linklist h1=(lnode*)malloc(sizeof(lnode));
	linklist h2 = (lnode*)malloc(sizeof(lnode));
	buildlinklist(&h1,a1,n);
	buildlinklist(&h2,a2,n);
	printf("原始h1链表为:");
	print(&h1);
	printf("\n原始h2链表为:");
	print(&h2);
	lianjie(&h1, &h2);
	printf("\n链接后的链表为:");
	print(&h1);
	return 0;
}

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力敲代码的小火龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值