线性表的链式表示和实现(带头结点的单链表)---> j集(无重复元素),A=A∪(B∩C),并使求解结构A仍保持递增,构造C=A∪B,利用O(1)删除某节点,逆置,判断有无环,若有环找到入环第一个结点

C语言之线性表的链式表示和实现(带头结点的单链表)

①无重复元素的交集

②已知递增有序的单链表A,B和C分别存储了一个集合,设计算法实现:A=A∪(B∩C),并使求解结构A仍保持递增。要求算法的时间复杂度为O(|A|+|B|+|C|)。

③假设有两个按元素递增有序排列的线性表A和B,均以单链表作存储结构,请编写算法将A表和B表归并成一个按元素递减有序排列(允许表中含有值相同的元素)的线性表C,并要求利用原表(A表和B表)的结点空间构造C表;

④利用O(1)的空间给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点;

⑤逆置,反转;

⑥判断两个单链表是否相交,如果相交则返回第一个相交的结点,如果没有相交,则返回NULL,判断的是最后一个结点是否是同一个结点,通过结点的地址判断,不能通过data判断,得到两条链表的长度,m和n(假设m>n),再定义两个指针pa和pb分别指向两条链表的开头,让长链表的指针先走,m-n步,然后两个指针再一起走,相遇的第一个结点就是第一个交点,如果不相遇则不相交,适用于没有环的链表,如果一个有环一个没有环则不相交,分别将两个链表的环断开(都没有环),判断最后一个节点是否相同,最后需要把断开的结点再接上(不能修改链表的结构),都有环,且相交,断开环,然后按照没有环的处理,处理完成后再把断开处接好;

⑦找到单链表中倒数第k个结点。

⑧判断单链表中是否有环,如果有,则返回相遇的结点,如果没有,则返回NULL,快慢指针;

⑨如果单链表中有环,找到入环的第一个结点

头文件(链表的函数说明):

#pragma once
//带头结点的单链表,单链表的标记为尾结点的next为NULL

typedef  struct LNode
{
	int data;//数据域
	struct LNode *next;//后继的地址,节点
}LNode,*LinkList;

//初始化
void InitList(LinkList plist);

//插入,在pos位置处插入value值
void Insert(LinkList plist,int pos,int value);

//头插
void Insert_Head(LinkList plist,int value);

//尾插
void Insert_Tail(LinkList plist,int value);

//查找,在plist中查找关键字key,找到返回地址,失败返回NULL
LinkList Search(LinkList plist,int key);

//查找(根据位置找到结点),在plist中pos,找到返回地址,失败返回NULL
LinkList Get_Node_pos(LinkList plist,int pos);

//删除
void Delete_Key(LinkList plist,int key);

//删除,在pos位置
void Delete_Pos(LinkList plist,int pos);

//头删
void Delete_Head(LinkList plist);

//尾删
void Delete_Tail(LinkList plist);

//统计数据节点个数
int GetLength(LinkList plist);

//判空
int IsEmpty(LinkList plist);

//输出
void Show(LinkList plist);

//清空
void Clear(LinkList plist);

//销毁
void Destory(LinkList plist);

链表的函数功能实现

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"LinkList.h"


static LinkList _Apply_Node(int value,LinkList next)//新节点的value,next
{
	LinkList s=(LinkList)malloc(sizeof(LNode));
	assert(s != NULL);
	if(s == NULL)
	{
		printf("Apply Space Fail\n");
		return NULL;
	}

	s->data = value;
	s->next = next;

	return s;
}

//初始化
void InitList(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("Singly LinkList Initialization Failed\n");//链表初始化失败
		return;
	}
	plist->next = NULL;//单链表结尾标记
}

//插入,在pos位置处插入value值
void Insert(LinkList plist,int pos,int value)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return;
	}

	//判断pos位置是否合法
	if(pos < 0 || pos > GetLength(plist))
	{
		printf("Insert Fail:Pos Is Error!\n");
		return;
	}

	LinkList p = Get_Node_pos(plist,pos);
	p->next = _Apply_Node(value,p->next);
}

//头插,在头结点的下一个节点插入value
void Insert_Head(LinkList plist,int value)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	plist->next = _Apply_Node(value,plist->next);
}

//尾插,,在尾结点的处插入value
void Insert_Tail(LinkList plist,int value)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}
	
	Insert(plist,GetLength(plist),value);
}

//查找(根据位置找到结点),在plist中pos,找到返回地址,失败返回NULL
LinkList Get_Node_pos(LinkList plist,int pos)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return  NULL;
	}

	if(pos < 0)
	{
		return NULL;
	}

	LinkList p = plist;

	while (pos && p != NULL)
	{
		p = p->next;
		pos--;
	}
	return p;
}

//查找,在plist中查找关键字key,找到返回地址,失败返回NULL
LinkList Search(LinkList plist,int key)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return NULL;
	}
	LinkList s=(LinkList)malloc(sizeof(LNode));
	s = plist->next;
	while (s->next != NULL)
	{
		if(s->data == key)
		{
			break;
		}
		s=s->next;
	}
	return s;
}

//删除,按key值删除
void Delete_Key(LinkList plist,int key)
{
	assert(plist != NULL);
	LinkList s = (LinkList)malloc(sizeof(LNode));
	s = plist;
	while (s->next != NULL)
	{
		if(s->next->data == key)
		{
			LinkList q = s->next;//要删除的结点
			s->next = q->next;
			free(q);
			break;
		}
		s=s->next;
	}
}

//删除,在pos位置
void Delete_Pos(LinkList plist,int pos)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	int length = GetLength(plist);
	//判断pos位置是否合法
	if(pos < 1 || pos > length)
	{
		printf("Deleting a Location Is Not Legal!\n");
		return;
	}

	LinkList p = Get_Node_pos(plist,pos - 1);//找到要删除的结点的前一个结点
	LinkList q = p->next;//q为要删除的那个结点
	
	p->next = q->next;
	free(q);
}

//头删除,
void Delete_Head(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	LinkList q = plist->next;
	if(q != NULL)
	{
		plist->next = q->next;  
	}
	
	free(q);	
}

//尾删除,
void Delete_Tail(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	Delete_Pos(plist,GetLength(plist));
}

//统计链表数据节点个数(包括头结点,下标从0开始结算)
int GetLength(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return 0;
	}
	int length = 0;
	for(LinkList p = plist->next;p != NULL;p = p->next)
	{
		length++;
	}
	return length;
}

//判空,有头结点,是空链
int IsEmpty(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The HeadNode Is NULL\n");
		return 1;
	}

	if(plist->next == NULL)
	{
		printf("The FirstNode Is NULL\n");
		return 2;
	}
	return 0;
}

//输出
void Show(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	for(LinkList p = plist->next;p != NULL;p = p->next)
	{
		printf("%d -->",p->data);
	}
	printf("NULL\n");
}

//清空
void Clear(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}
	while (!IsEmpty(plist))
	{
		Delete_Head(plist);
	}
}

//销毁
void Destory(LinkList plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		printf("The Single LinkList is NULL\n");
		return;
	}

	free(plist->next);
	plist->next=NULL;
}

主函数(用于测试和与链表相关操作功能函数的实现)

#include<stdio.h>
#include"LInkList.h"
#include<assert.h>
#include<stdlib.h>

//无重复元素
void Intersection_AB(LinkList pA,LinkList pB)
{
	assert(pA != NULL || pB != NULL);
	if(IsEmpty(pA) || IsEmpty(pB) )
	{
		printf("Intersection_AB:Empty LinkList\n");
		return ;
	}

	LinkList pa = pA->next,pb = pB->next;

	int i = 1;

	while (pa != NULL && pb != NULL )
	{
		if(pa->data == pb->data)
		{		
			pa = pa->next;
			pb = pb->next;
		}
		else if(pa->data < pb->data)
		{
			pa = pa->next;
		}
		else
		{
			Insert(pA,i - 1 ,pb->data);
			pb = pb->next;
		}
		i++;
	}
}

//已知递增有序的单链表A,B和C分别存储了一个集合,设计算法实现:
//A=A∪(B∩C),并使求解结构A仍保持递增。
//要求算法的时间复杂度为O(|A|+|B|+|C|)。

void Union_A_and_B_deal_C(LinkList pA,LinkList pB,LinkList pC)
{
	assert(pA !=NULL || pB != NULL || pC != NULL);
	if(IsEmpty(pA) || IsEmpty(pB) || IsEmpty(pC))
	{
		printf("Union_A_and_B_deal_C:Empty LinkList\n");
		return ;
	}

	Intersection_AB(pB,pC);
	LinkList pa = pA->next,pb = pB->next;
	
	int i = 0;

	while (pa != NULL && pb != NULL)
	{
		i++;
		if(pa->data == pb->data)
		{
			pb = pb->next;
			pa = pa->next;
		}
		else if(pb->data < pa->data)
		{
			Insert(pA,i - 1,pb->data);
			pb = pb->next;
		}
		else 
		{
			pa = pa->next;
		}
	}

	while (pb != NULL)
	{
		Insert_Tail(pA,pb->data);
		pb = pb->next;
	}
	
}

//假设有两个按元素递增有序排列的线性表A和B,均以单链表作存储结构,
//请编写算法将A表和B表归并成一个按元素递减有序排列(允许表中含有值相同的元素)的线性表C,
//并要求利用原表(A表和B表)的结点空间构造C表
void Union_DecreC_MergeAB(LinkList pA,LinkList pB,LinkList pC)
{
	assert(pA !=NULL || pB != NULL || pC != NULL);
	if(IsEmpty(pA)|| IsEmpty(pB) || IsEmpty(pC) )
	{
		printf("Union_DecreC_MergeAB:An empty list!\n");
		return ;
	}

	LinkList pa = pA->next,pb = pB->next;
	while (pa != NULL && pb != NULL)
	{
		if(pa->data < pb->data)
		{
			Insert_Head(pC,pa->data);
			pa = pa->next;
		}
		else
		{
			Insert_Head(pC,pb->data);
			pb = pb->next;
		}
	}

	while (pa != NULL)
	{
		Insert_Head(pC,pa->data);
		pa = pa->next;
	}

	while (pb  != NULL)
	{
		Insert_Head(pC,pb->data);
		pb = pb->next;
	}
}

//利用O(1)的空间
//给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点
int DeleteNodep(LinkList pA,LinkList p)
{
	assert(pA != NULL || p != NULL);
	if(IsEmpty(pA) || p != NULL)
	{
		printf("DeleteNodep:Empty LinkList\n");
		return 0;
	}

	if(p->next == NULL)
	{
		Delete_Tail(pA);
		return 1;//尾结点
	}
	
	LinkList q = p->next;
	p->data = q->data;
	p->next = q->next;

	free(q);

	return 1;
}

//逆置,反转
void Reverse(LinkList pA)
{
	assert(pA != NULL);
	if(IsEmpty(pA))
	{
		printf("Reverse:Empty LinkList\n");
		return ;
	}

	LinkList p = pA->next,q = p;
	pA->next = NULL;
	while (p)
	{
		q = p;
		p = p->next;
		q->next = pA->next;
		pA->next = q;
	}														
}

//判断两个单链表是否相交,如果相交则返回第一个相交的结点,如果没有相交,则返回NULL
//判断的是最后一个结点是否是同一个结点,通过结点的地址判断,不能通过data判断
//得到两条链表的长度,m和n(假设m>n),再定义两个指针pa和pb分别指向两条链表的开头,让长链表的指针先走
//m-n步,然后两个指针再一起走,相遇的第一个结点就是第一个交点,如果不相遇则不相交
//适用于没有环的链表
//如果一个有环一个没有环则不相交,分别将两个链表的环断开(都没有环),判断最后一个节点是否相同,最后需要把断开的结点再接上(不能修改链表的结构)
//都有环,且相交,断开环,然后按照没有环的处理,处理完成后再把断开处接好
LinkList Intersection_pA_pB(LinkList pA,LinkList pB)
{
	assert(pA != NULL || pB != NULL);
	if(IsEmpty(pA) || IsEmpty(pB))
	{
		printf("Intersection_pA_pB:Empty LinkList\n");
		return NULL;
	}

	int length_pA = GetLength(pA);
	int length_pB = GetLength(pB);

	LinkList pa = pA,pb = pB;

	if(length_pA > length_pB)
	{
		for(int i = 0;i < length_pA - length_pB;i++)
		{
			pa = pa->next;
		}
	}
	else
	{
		for(int i = 0;i< length_pB - length_pA ;i++)
		{
			pb = pb->next;
		}
	}

	while (pa != NULL)
	{
		if(pa == pb)
		{
			return pa;
		}

		pa = pa->next;
		pb = pb->next;	
	}
	
	return NULL;
}

//找到单链表中倒数第k个结点
LinkList Find_k_Node(LinkList pA,int k)
{
	assert(pA != NULL);
	if(IsEmpty(pA) || k < 0)
	{
		printf("Find_k_Node:A is an empty list or  pos is not legal\n");
		return NULL;
	}

	LinkList p = pA,q = pA;
	for(int i = 0;i < k && p != NULL;i++)//限制了k的位置
	{
		p = p->next;
	}

	if(p == NULL)
	{
		printf("Find_k_Node:The value of k is greater than the length of the list\n");
		return NULL;
	}

	while (p != NULL)
	{
		p = p->next;
		q = q->next;
	}
	return q;
}

//判断单链表中是否有环,如果有,则返回相遇的结点,如果没有,则返回NULL
//快慢指针
LinkList Loop(LinkList pA)
{
	if(IsEmpty(pA))
	{
		printf("Loop:Empty LinkList\n");
		return NULL;
	}
	LinkList p = pA;//快指针
	LinkList q = pA;//慢指针

	while (p != NULL && p->next != NULL)//p->next != NULL保证快指针一次走两个结点不为NULL
	{
		p = p->next->next;
		q = q->next;

		if(p == q)//快指针和慢指针相遇,表示有环
		{
			return p;
		}
	}
	return NULL;
}


//如果单链表中有环,找到入环的第一个结点
LinkList IsLoop(LinkList pA)
{
	LinkList s = Loop(pA);
	if(s == NULL)
	{
		printf("There are no rings in the list\n");
		return NULL;
	}

	LinkList p = pA;
	LinkList q = s;
	while (p != q)
	{
		p = p->next;
		q = q->next;
	}

	return p;//入环的第一个结点
}


int main()
{
	LNode pA,pB,pC;

	InitList(&pA);
	InitList(&pB);
	InitList(&pC);
	Insert_Tail(&pA,1);Insert_Tail(&pA,3);
	Insert_Tail(&pA,6);Insert_Tail(&pA,11);
	Insert_Tail(&pA,12);
	
	Insert_Tail(&pB,1);Insert_Tail(&pB,3);
	Insert_Tail(&pB,4);Insert_Tail(&pB,6);
	Insert_Tail(&pB,10);

	Insert_Tail(&pC,1);Insert_Tail(&pC,2);
	Insert_Tail(&pC,3);Insert_Tail(&pC,4);
	Insert_Tail(&pC,10);

	printf("单链表A:");
	Show(&pA);
	printf("单链表B:");
	Show(&pB);
	printf("单链表C:");
	Show(&pC);

	//Union_A_B(&pA,&pB);
	/*
	Reverse(&pA);
	Show(&pA);

	
	LinkList s = Intersection_pA_pB(&pA,&pB);
	if(s == NULL)
	{
		printf("Two Single LinkLists Not Intersect\n");
	}
	else
	{
		printf("Two Single LinkLists Intersect\n");
	}
	
	LinkList s = Find_k_Node(&pA,2);
	if(s == NULL)
	{
		printf("The penultimate k position in the list is not valid\n");
	}
	else
	{
		printf("The last %d in the list is %d",2,s->data);
	}
	

	

	Union_DecreC_MergeAB(&pA,&pB,&pC);
	printf("合并A和B后,单链表C:");
	Show(&pC);
	

	
	

	Intersection_AB(&pB,&pC);
	printf("Intersection_AB:The intersection of two sets:\n");
	Show(&pB);
	

	Union_A_and_B_deal_C(&pA,&pB,&pC);
	printf("Union_A_and_B_deal_C:A=A∪(B∩C),并使求解结构A仍保持递增:\n");
	Show(&pA);
	

	ReverseInsert(&pA);
	Show(&pA);
	
	
	LinkList s = Loop(&pA);
	if(s == NULL)
	{
		printf("There are no rings in the list\n");
	}
	else
	{
		printf("There are rings in the list\n");
	}
	
	IsLoop(&pA);
	*/

	Destory(&pA);
	Destory(&pB);
	Destory(&pC);
	return 0;
}

 关于带头结点链表的逆置还有一种方法,就是利用新开辟的空间,原链表实现头插;比较简单,头插函数已经实现,直接调用即可,这里没有实现。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值