一些单链表的题目(3)

本文介绍了多种链表操作的算法实现,包括判断循环双链表是否对称、将两个循环单链表链接、删除循环单链表最小元素、按访问频度排序双向链表以及查找链表中第k个结点。此外,还涵盖了寻找链表环的入口点、改变链表结构等操作,涉及链表的基本操作和高级应用。
摘要由CSDN通过智能技术生成

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

思路:让p从左向右扫描,q从右向左扫描,直到它们指向同一个结点或相邻结点为止。(p==q,p->next=q或q->next=p)。若它们所指结点值相同,则继续进行下去,否则返回0。

typedef struct DNode{ //定义双链表结点的类型
	ElemType data; //数据域
	struct DNode *prior, *next; //前驱和后继指针
}DNode, *DLinklist;

int Symmetry(DLinklist L){
	DNode *p=L->next, *q=L->prior;
	while(p!=q || q->next!=p){
		if(p->data == q->data){
			p = p->next;
			q = q->next;
		}
		else
			return 0;
	}
	return 1;
}

 

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

思路:将h1的尾指针指向h2,将h2的尾指针指向h1

LinkList Link(LinkList &h1, LinkList &h2){
	LNode *r1, *r2; //r1和r2分别指向两个链表的尾指针
	r1 = h1;
	while(r1->next!=h1) //寻找h1的尾结点
		r1 = r1->next;
	r2 = h2;
	while(r2->next!=h2) //寻找h2的尾结点
		r2 = r2->next;
	r1->next = h2; //将h2链接到h1后
	r2->next = h1; //令h2的尾结点指向h1
	return h1;
}

 

19、题目:设有一个带头结点的循环单链表,其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表为空,再删除头结点。

思路:对于循环单链表L,在不空时循环;每循环一次查找一个最小结点(由minp指向最小值结点,minpre指向其前驱结点)并删除它。最后释放头结点。时间复杂度为O(n²)

typedef struct node{ //链表结点的结构定义
	int data; //数据域
	struct node *next; //指针域
}LNode, *LinkList;

void Min_Delete(LinkList &L){
	LNode *pre, *p, *minpre, *minp;
	while(L->next!=L){ //表不为空,循环
		pre=L;
		p=L->next; //p为工作指针,pre指向其前驱
		minpre=pre;
		minp=p; //minp指向最小值结点,minpre指向其前驱
		while(p!=L){ //循环一趟,查找最小值结点
			if(p->data<minp->data){ 
				minp=p;
				minpre=pre;
			}
			p=p->next; //查找下一个结点
			pre=pre->next;
		}
		//找到最小值结点,输出并删除
		printf("%d", minp->data);
		minpre->next=minp->next;
		free(minp);
	}
	//释放头结点
	free(L);
}

 

20、题目:设有一头指针为L的带有表头结点的非循环双向链表,其每个结点中除有prior(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq。在链表被起用前,其值均初始化为零。每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点的最后,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型。

DLinklist Locate(DLinklist &L, ElemType x){
	DNode *p=L->next, *q;
	while(p!=NULL && p->data!=x)
		p = p->next; //查找值为x的结点
	if(!p){
		printf("不存在值为x的结点\n");
		exit(0);
	} else{
		p->freq++;
		if(p->next!=NULL)
			p->next->pred = p->pred;
			p->pred->next = p->next; //将p结点从链表上摘下,这包括p->next==NULL
		q = p->pred;
		if(q!=L && q->freq<=p->freq;)
			q = q->pred; //找到q比p频率更高的结点,然后将p插在q之后
		p->next = q->next;
		q->next->pred = p; //将p结点插入,一定是排在同频率的第一个
		p->pred = q;
		q->next = p;
	}
	return p; //返回值为x的结点的指针
}

 

21、

算法的详细实现步骤如下:
①count=0,p和q指向链表表头结点的下一个结点。
②若p为空,转⑤。
③若count等于k,则q指向下一个结点;否则,count=count+1.
④p指向下一个结点,转②
⑤若count等于k,则查找成功,输出该结点的data域的值,返回1;否则,说明k值超过了线性表的长度,查找失败,返回0.
⑥算法结束。

typedef int ElemType; //链表数据的类型定义
typedef struct LNode{ //链表结点的结构定义
	ElemType data; //结点数据
	struct LNode *link; //结点链接指针
}LNode, *LinkList;
int Search_k(LinkList list, int k){
	LNode *p=list->link, *q=list->link; //指针p、q指示第一个结点
	int count = 0;
	while(p!=NULL){ //遍历链表直到最后一个结点
		if(count<k) 
			count++; //计数,若count<k,p结点继续向后移动,q结点不动
		else
			q = q->link; //若count=k,p和q结点同时向后移动
		p = p->link;
	}
	if(count<k)
		return 0; //查找失败返回0
	else{
		printf("%d", q->data); //查找成功,输出该结点的值,并返回1
		return 1;
	}
}

 

22、

//定义链表
typedef struct Node{
	char data;
	struct Node *next;
}SNode;
//求链表的表长
int listlen(SNode *head){
	int len = 0;
	while(head->next!=NULL){
		len++;
		head = head->next;
	}
	return len;
}
//方法一:
SNode* find_addr(SNode *str1, SNode *str2){
	SNode *p, *q;
	int m, n;
	m = listlen(str1); //求str1的长度
	n = listlen(str2); //求str2的长度
	for(p=str1; m>n; m--) //若m>n,使p指向链表的第m-n+1个结点
		p = p->next;
	for(q=str2; m<n, n--) //若m<n,使q指向链表的第n-m+1个结点
		q = q->next;
	while(p->next!=NULL && p->next!=q->next){ //链表不为空,且没有找到共同后缀,将指针p个q同步往后移
		p = p->next;
		q = q->next;
	}
	return p->next; //返回共同后缀的起始地址
}
//方法二:
SNode* find_addr(SNode *str1, SNode *str2){
	LNode *longlenth, *shortlength;
	int m, n, dist;
	m = listlen(str1);
	n = listlen(str2);
	if(m>=n){ //
		longstr = str1;
		shortstr = str2;
		dist = m-n; //
	} else{ //
		longstr = str2;
		shortstr = str1;
		dist = n-m; //
	}
	while(dist--){ //
		longstr = longstr->next;
	}
	while(longstr->next!=NULL && longstr->next!=shortstr->next){
		longstr = longstr->next;
		shortstr = shortstr->next;
	}
	return longstr->next;
}
//时间复杂度为O(m+n)

 

23、

/*
用空间换时间。使用辅助数组记录链表中已出现的数值,从而只需对链表进行一趟扫描。
因为|data|<=n,故辅助数组q的大小为n+1,各元素的初值均为0.依次扫描链表中的各结点,同时检查q[|data|]的值。
若为0,则保留该结点,并令q[|data|]=1;否则,删除该结点
*/
typedef struct node{
	int data;
	struct node *link;
}NODE;
typedef NODE *PNODE;
void func(PNODE h, int n){
	PNODE p=h, r;
	int *q, m;
	q = (int *)malloc(sizeof(n+1)); //申请n+1个位置的辅助空间
	for(int i=0; i<n+1; i++) //数组元素的初值都置为0
		*(q+i)=0;
	while(p->link!=NULL){
		if(p->link->data > 0) //取结点的data的绝对值,并赋值给m
			m=p->link->data;
		else
			m=-p->link->data;
		if(*(q+m)==0){ //判断该结点的data是否已经出现过
			*(q+m)=1;
			p=p->link;
		} else{ //之前出现过,删除该结点
			r=p->link;
			p->link=r->link;
			free(r);
		}
	}
	free(q);
}

 

24、题目:设计一个算法完成以下功能:判断一个链表是否有环,如果有,找出环的入口点并返回,否则返回NULL。

/*
设置快慢两个指针分别为fast和slow,初始时都指向head指针。
slow每次走一步,即slow=slow->next,fast每次走两步,即fast=fast->next->next。
经过若干次操作后,如果两个指针在环上相遇,说明链表有环;否则,反之。
设头结点到环的入口点距离为a,入口点沿环的方向到相遇点距离为x,环长为r,相遇时fast绕过了n圈。
则有2(a+x)=a+n*r+x,即a=n*r-x,即从头结点到环的入口点的距离等于n倍的环长减去环的入口点到相遇点的距离。
可以设置两个指针,一个指向head,一个指向相遇点,两个指针同步移动(均为一次走一步),相遇点即为环的入口点。
*/

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode;
LNode *FindLoopStart(LNode *head){
	LNode *fast=head, *slow=head; //设置快慢两个指针
	while(slow!=NULL && fast->next!=NULL){
		slow=slow->next; //慢指针每次走一步
		fast=fast->next->next; //快指针每次走两步
		if(slow==fast) break; //两个指针相遇,说明链表有闭环
	}
	if(slow==NULL || fast->next==NULL)
		return NULL; //链表没有环,返回NULL
	LNode *p=head, *q=slow; //分别指向开始点、相遇点
	while(p!=q){
		p=p->next;
		q=q->next;
	}
	return p; //返回入口点
}

 

25、

/*
①找出链表L的中间结点,设置两个指针p和q,指针p每次走一步,指针q每次走两步,当指针q到达链尾时,指针p正好在链表的中间结点。
②然后将L的后半段结点原地逆置。
③从单链表前后两段中依次各取一个结点,按要求重新排。
*/
void change_List(NODE *h){
	NODE *p, *q, *r, *s;
	p=q=h;
	while(q->next!=NULL){ //寻找中间结点
		p=p->next; //p每次走一步
		q=q->next->next; //q每次走两步
	}
	q=p->next;
	p->next=NULL;
	while(q!=NULL){ //将链表后半段逆置
		r=q->next; //r指向后半段的下一个结点,保证不断链
		q->next=p->next; //利用头插法
		p->next=q;
		q=r;
	}
	s=h->next;
	q=p->next;
	p->next=NULL;
	while(q!=NULL){ //将链表后半段的结点插入到指定的位置
		r=q->next; //r指向后半段的下一个结点,保证不断链
		q->next=s->next; //将q所指结点插入到s所指结点之后
		s->next=q; 
		s=q->next; //s指向前半段的下一个插入点
		q=r;
	}
}

 

友情链接:

一些单链表的题目(1)

一些单链表的题目(2)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值