概念
链表是一种物理存储结构上非连续、非顺序的存储结构,物理存储位置是随机的。数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。
物理存储位置是随机的,通过指针来表示数据之间的逻辑关系。
链表的结点
链表中每个数据的存储都由以下两部分组成:
1.数据元素本身,其所在的区域称为数据域;
2.指向直接后继元素的指针,所在的区域称为指针域;
每个结点都由数据域和指针域两部分组成
用结构体来表示结点 ,定义如下
typedef int SLTDataType;//数据类型
typedef struct SListNode
{
SLTDataType _data;//数据域
struct SListNode* _next;//指针域
}SListNode;
链表的结构体
typedef struct SList
{
SListNode* _head;//头指针
}SList;
链表常用接口
一般讨论三种情况:
1.没有结点
2.需要对第一个结点经行操作
3.一般情况 (不动头结点)
void SListInit(SList* plist);//初始化链表
void SListDestory(SList* plist);//销毁链表
void SListPushFront(SList* plist, SLTDataType x);//头插
void SListPopFront(SList* plist);//头删
void SListPushBack(SList* plist);//尾插
void SListPopBack(SList* plist);//尾删
SListNode* SListFind(SList* plist, SLTDataType x);//查找
void SListInsertAfter(SListNode* pos, SLTDataType x);// 在pos的后面进行插入
void SListEraseAfter(SListNode* pos, SLTDataType x);// 在pos的前面进行插入
void SListRemove(SList* plist, SLTDataType x);//删除元素第一个元素x
void SListPrint(SList* plist);//打印链表
void SlistReverse(SList*plist);//翻转链表
初始化和销毁链表
传参为链表的地址
plist是指针 要访问_head ,使用plist->_head;
plist->_head 表示第一个结点 ,plist->_head ->_next 表示第二个结点。当plist->_head == NULL时 ,表示链表中没有结点
void SListInit(SList* plist)
{
assert(plist != NULL);
plist->_head = NULL;
}
void SListDestory(SList* plist)
{
assert(plist != NULL);
SListNode*next = NULL;
for (SListNode*cur = plist->_head; cur!=NULL;cur= next)
{
next = cur->_next;
free(cur);
}
plist->_head = NULL;
}
插入和删除
注意:头插法相对尾插简单,使用尾插时,要找最后一个结点,使用尾删时,要找倒数第二个结点。尾删时考虑:1.没有结点 2.一个结点 3.一般情况
//头插和头删
void SListPushFront(SList* plist, SLTDataType x)//头插
{
assert(plist != NULL);
SListNode*Node = (SListNode*)malloc(sizeof(SListNode));//产生新结点
Node->_data = x;
Node->_next = plist->_head;
plist->_head = Node;
}
void SListPopFront(SList* plist)//头删
{
assert(plist != NULL);
if (plist->_head == NULL)
return;
SListNode*cur = plist->_head->_next;
free(plist->_head);
plist->_head = cur;
}
//尾插和尾删
void SListPushBack(SList* plist, SLTDataType x)//尾插
{
assert(plist != NULL);
SListNode*cur = plist->_head;
SListNode*Node = (SListNode*)malloc(sizeof(SListNode));
assert(Node);
Node->_data = x;
Node->_next = NULL;
if (plist->_head == NULL)//没有结点的情况
{
plist->_head = Node;
return;
}
while (cur->_next != NULL)
{
cur = cur->_next;
}
cur->_next = Node;//cur表示最后一个结点
}
void SListPopBack(SList* plist)//尾删
{
assert(plist);
if (plist->_head == NULL)
return;
if (plist->_head->_next == NULL)
{
free(plist->_head);
plist->_head = NULL;
}
SListNode*cur = plist->_head;
while (cur->_next->_next != NULL)
{
cur = cur->_next;
}
free(cur->_next);
cur->_next = NULL;
}
查 增 删
在pos结点后增加结点 :让新结点指向 pos指向的下一个结点 ,再让pos指向新结点
在pos前增加结点:与在pos后增加结点一样 ,但是要把pos的数据改为新数据,pos后的新结点数据改为pos原数据
TIP: 知道某一结点 ,只能在这一个结点后面增加结点,但是结点存储的数据是可以修改的,从而实现在pos前增加结点。
SListNode* SListFind(SList* plist, SLTDataType x)//查找
{
assert(plist);
SListNode*cur = plist->_head;
for (cur; cur != NULL; cur = cur->_next)
{
if (cur->_data == x)
return cur;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDataType x)//在pos后面增加结点 数据为x
{
assert(pos);
SListNode*node = (SListNode*)malloc(sizeof(SListNode));
assert(node);
node->_data = x;
node->_next = pos->_next;
pos->_next = node;
}
void SListRemove(SList* plist, SLTDataType x)//删除第一个元素x
{
assert(plist);
SListNode *cur = plist->_head;
if (plist->_head == NULL)
return;
if (cur->_data == x)
{
plist->_head = cur->_next;
free(cur);
return;
}
while (cur->_next!= NULL)
{
if (cur->_next->_data == x)
{
SListNode*ptr = cur->_next;
cur->_next = cur->_next->_next;
free(ptr);
return;
}
cur = cur->_next;
}
}
翻转单链表
用三个指针来实现单链表的翻转
p1 表示翻转后 链表的最后一个结点
p2 表示当前结点
p3 表示下一个结点
以下图为例:
翻转前的链表
翻转后的链表
结合代码操作过程如下:
刚开始时 p1指向空 p2指向第一个结点 p3指向第二个结点
下一步 :p2->_next = p1;让第一个结点指向p1 ,此时p1为空 (注意第一个结点翻转后为最后一个结点 ,翻转后此结点下一个指向空。 p1 = p2 ; p1 表示翻转后 链表的最后一个结点,p2 = p3;让p2指向下一个结点;p3 = p3->_next;让p3指向下一个结点。
经过一次操作后 如上图所示;
p1 指向 1结点 1结点指向空 p2指向2结点 p3指向3结点
再经过一次操作后如下图所示:
。。。。
最终翻转后 如下:
最后 再让头结点指向p1 实现翻转。
注意: 循环的终止条件是p2指向NULL,表示所有结点都完成翻转。移动p3时要对其进行判断,防止对空指针进行移动
void SlistReverse(SList*plist)//三个指针的方式来翻转链表
{
assert(plist);
if (plist->_head == NULL)
return;
SListNode*p1 = NULL;
SListNode*p2 = plist->_head;
SListNode*p3 = plist->_head->_next;
while (p2 != NULL)
{
p2->_next = p1;
p1 = p2;
p2 = p3;
if (p3 != NULL)//这里是p3 而不是p3->_next
{
p3 = p3->_next;
}
}
plist->_head = p1;//让头指针指向第一个结点
}