链表的基本概念
链表的基本操作
链表和结点的定义
/*
链表:定义结点
*/
typedef struct LNode *List; //将《结点的地址》类型重命名为List
struct LNode //定义《结点》类型
{
ElementType Data; //结点的数据
List Next; //下一个结点的地址
};
struct LNode L; //定义一个《结点》类型的变量,变量名为L
List PtrL; //定义一个《结点的地址》类型的变量,变量名为PtrL,即链表的头
计算链表的表长
/*
求表长
分析:遍历的方式
时间复杂度:O(n)
*/
int Length(List, PtrL0)
{
List p = PtrL; //将用于遍历的指针放到表头
int j = 0; //用于计数
while (p) //当p不为null时,即链表不为空,循环继续
{
p = p->Next; //让p指针沿着链表往后挪一位,即p指向下一个结点
j++; //当前p指向的是第j个结点
}
return j;
}
查找:按序查找
/*
查找:按序号查找
时间复杂度:O(n)
结果:返回第K个结点的指针
*/
List FindKth(int K, List PtrL)
{
List p = PtrL; //把p放在表头
int i = 1; //i表示第几个元素
while (p != NULL && i<K) //链表不为空 且 还没找到第K个元素
{
p = p->Next; //p往后挪一个结点
i++;
} //循环终止:链表为空或者i = K,即找到了
if (i == K)
return p; //找到第K个,返回指针
else
return NULL; //否则返回空
}
查找:按值查找
/*
查找:按值查找
时间复杂度:O(n)
结果:返回值为X的结点的指针
*/
List Find(ElementType X, List PtrL)
{
List p = PtrL;
while (p != NULL && p->Data!=X) //链表不为空 且 还没找到值=X的元素
p = p->next; //p往后挪一个结点
return p;
}
插入
/*
插入:在第i-1(1<=i<=n+1)个结点之后插入一个值为X的新结点
分析:
1.构造一个新结点,用s指向
2.找到链表的i-1个结点,用p指向
3.然后修改指针,插入结点(p后面跟着s)
①s->Next = p->Next;
②p->Next = s;
①和②不可以交换顺序,必须要先把新结点和后面连起来
如果交换顺序为②①,则无法找到中p原本后面那个结点的地址(原本是用p->next指向的)
结果:返回插入后的头指针
平均查找次数:n/2
时间复杂度:O(n)
*/
List insert( ElementType X, int i, List PtrL)
{
List p, s; //定义存放结点地址的变量,其中p变量用于存放第i-1个结点的地址,s变量用于存放新结点的地址
if(i == 1) //头插
{
s = (List)malloc(sizeof(struct LNode)); //为新的结点申请内存,并用s存放这块内存的首地址
s->Data = X; //给这个结点装上数据X
s->Next = PtrL; //把s装到链表的前面
return s;
}
p = FindKth (i-1, Ptrl); //找第i-1个结点
if (p == NULL) //第i-1个结点不存在
{
printf("参数i错误");
return NULL;
}
else //p存在,开始插入
{
s = (List)malloc(sizeof(struct LNode)); //为新的结点申请内存,并用s存放这块内存的首地址
s->Data = X; //给这个结点装上数据X
s->Next = p->Next; //插入,这一行与下一行不能颠倒顺序
p->Next = s;
return Ptrl;
}
}
/*
总结:
霸道的结点:每个结点有两个口袋,左口袋装着数据,右口袋装着下一个结点的地址
如果我的口袋里面装着你的地址,即使我是新来的,我也在你的前面
头插:虽然我是新来的,但是我右口袋里面装着你们一条街第一户的地址,那么我在你们这条街的前面
非头插:我要知道上一家才能把我家地址放到它右口袋啊,首先要知道有没有上一家,如果没有,插入失败
*/
删除
/*
删除:删除链表的第i(1<=i<=n)个位置上的结点
分析:
1.先找到链表的第i-1个结点,用p指向
2.再用指针s指向要被删除的结点(p的下一个结点)
3.然后修改指针,删除s所指的结点
4.最后释放s所指结点的空间 //不free卡死你
*/
List Delete(int i, List PtrL)
{
List p, s; //定义存放结点地址的变量,其中p变量用于存放第i-1个结点的地址,s变量用于存放第i个结点的地址
if(i == 1) //删除头结点
{
s = PtrL; //s存放的是头结点的地址,PtrL一直存放的是头结点的地址,所以要单独定义一个s,这步不能省略
if (PtrL != NULL) //链表不为空
PtrL = PtrL->Next; //删除操作
else
return NULL;
free(s); //释放被删结点的空间
return PtrL;
}
p = FindKth(i-1, PtrL); //查找第i-1个结点,并用p存放这个结点的地址
if (p == NULL) //第i-1个结点为空,没有第i个的地址,无法删除
{
printf("第%d个结点不存在", i-1);
return NULL;
}
else if (p->Next == NULL) //第i个结点为空,无法找到第i-1个后面的后面那个结点,无法删除
{
printf("第%d个结点不存在", i);
return NULL;
}
else
{
s = p->Next; //用s存放要被删除的结点地址
p->Next = s->Next; //删除操作
free(s); //释放被删除结点的空间
return PtrL;
}
}