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