目录
一、链表的概念
二、单链表的接口实现
1.首先创建一个链表的结构
2.单链表的结点申请
3.单链表的打印
4.单链表的尾插
5.单链表的头插
6.单链表的尾删
7.单链表的头删
8.单链表的查找
9.单链表在pos位置前插入一个结点
10.单链表在pos位置后插入一个结点
11.单链表删除pos位置的结点
12.单链表删除pos位置后的结点
13.单链表的销毁
三、源文件
SList.h
SList.c
Test.c
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。链表由两个部分组成:数据域和指针域,数据域用来存放数据,指针域用来链接到下一个数据,具体看下图;
从图中可以看出,每个数据的下面的指针域里都存储了下一个数据的地址,那么我们通过访问第一个数据的地址,顺藤摸瓜就能访问到其他数据
typedef int SLDataType;//这里typedef可以用于存储类型之间的切换
typedef struct SListNode
{
SLDataType Data;//数据域
struct SListNode* next;//指针域
}SLNode;
//动态申请一个结点
SLNode* BuyListNode(SLDataType x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->Data = x; //x的值存放在newnode数据域中
newnode->next = NULL; //newnode的指针域置空
return newnode;
}
//单链表的打印
void SListPrint(SLNode* phead)
{
assert(phead);
SLNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->Data); //打印链表数据
cur = cur->next; //让cur指向下一个数据
} //当cur为空时,链表打印结束
printf("NULL\n\n");
}
//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x)
{
assert(pphead);
SLNode* newnode = BuyListNode(x);//插入数据需要申请结点
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//尾插--就去找链表的尾巴
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
//找到尾巴,把尾结点的指针域指向插入的新结点的地址
tail->next = newnode;
}
}
//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x)
{
assert(pphead);
SLNode* newnode = BuyListNode(x);
newnode->next = *pphead; //让newnode的指针域指向链表的数据域(就是链表的头)
*pphead = newnode; //让newnode作新链表的头
}
//单链表的尾删
void SListPopBack(SLNode** pphead)
{
assert(*pphead);
//如果链表为空
if (*pphead == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//先要找到尾结点(tail),在记录一下尾结点的前一个结点(prev);
SLNode* tail = *pphead;
SLNode* prev = NULL;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
//找到尾结点了,释放它并置空;
free(tail);
tail = NULL;
//此时尾结点的前一个结点(prev)还保存着tail结点的数据的地址,要让它置空;
prev->next = NULL;
}
}
//单链表的头删
void SListPopFront(SLNode** pphead)
{
assert(*pphead);
SLNode* next = (*pphead)->next;//记录头结点的下一个结点的地址
free(*pphead);//释放头结点
*pphead = next;//头结点的下一个结点作新的头
}
//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x)
{
assert(phead);
SLNode* cur = phead;
while (cur != NULL)
{
if (cur->Data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//单链表在pos位置前插入一个结点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(pos);
SLNode* newnode = BuyListNode(x);//申请结点
//如果只有一个结点
if (*pphead == pos)
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
//找到pos位置的前一个结点,并记录
SLNode* posprev = *pphead;
while (posprev->next != pos)
{
posprev = posprev->next;
}
//让新结点的指针域指向pos,再让pos的前一个结点的指针域重新指向新结点的数据域地址,就很好的插入到pos的前面了
newnode->next = pos;
posprev->next = newnode;
}
}
//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x)
{
assert(pos);
SLNode* newnode = BuyListNode(x);//申请结点
newnode->next = pos->next;//让新结点的指针域指向pos位置的下一个结点的数据域地址
pos->next = newnode;//pos的指针域指向新结点的数据域地址
}
//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos)
{
assert(*pphead);
//如果链表只有一个结点
if (*pphead == pos)
{
SListPopBack(pphead);
}
else
{
//找到pos位置的前一个位置(posPrev),并记录
SLNode* posPrev = *pphead;
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
//让pos位置的前一个结点posPrev的指针域指向pos结点的指针域,如果pos位置还有结点,即指向了pos位置的下一个结点的数据域的地址;
posPrev->next = pos->next;
free(pos);//释放pos
}
}
//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos)
{
assert(pos);
assert(pos->next);
SLNode* next = pos->next;
pos->next = next->next;
free(next);
}
单链表的销毁并不能和顺序表一样直接释放,顺序表是开辟的连续的空间,链表不是;
要将链表释放需要释放每一个结点;
可以采用双指针;
//单链表的销毁
void SListDestroy(SLNode** pphead)
{
assert(*pphead);
SLNode* cur = *pphead;
while (cur != NULL)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
#include
#include
#include
typedef int SLDataType;
typedef struct SListNode
{
SLDataType Data;
struct SListNode* next;
}SLNode;
//动态申请一个节点
SLNode* BuyListNode(SLDataType x);
//单链表的打印
void SListPrint(SLNode* phead);
//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x);
//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x);
//单链表的尾删
void SListPopBack(SLNode** pphead);
//单链表的头删
void SListPopFront(SLNode** pphead);
//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x);
//单链表在pos位置前插入一个节点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x);
//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x);
//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos);
//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos);
//单链表的销毁
void SListDestroy(SLNode** pphead);
#include "SList.h"
//动态申请一个结点
SLNode* BuyListNode(SLDataType x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->Data = x; //x的值存放在newnode数据域中
newnode->next = NULL; //newnode的指针域置空
return newnode;
}
//单链表的打印
void SListPrint(SLNode* phead)
{
assert(phead);
SLNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->Data); //打印链表数据
cur = cur->next; //让cur指向下一个数据
} //当cur为空时,链表打印结束
printf("NULL\n\n");
}
//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x)
{
assert(pphead);
SLNode* newnode = BuyListNode(x);//插入数据需要申请结点
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//尾插--就去找链表的尾巴
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
//找到尾巴,把尾结点的指针域指向插入的新结点的地址
tail->next = newnode;
}
}
//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x)
{
assert(pphead);
SLNode* newnode = BuyListNode(x);
newnode->next = *pphead; //让newnode的指针域指向链表的数据域(就是链表的头)
*pphead = newnode; //让newnode作新链表的头
}
//单链表的尾删
void SListPopBack(SLNode** pphead)
{
assert(*pphead);
//如果链表为空
if (*pphead == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//先要找到尾结点(tail),在记录一下尾结点的前一个结点(prev);
SLNode* tail = *pphead;
SLNode* prev = NULL;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
//找到尾结点了,释放它并置空;
free(tail);
tail = NULL;
//此时尾结点的前一个结点(prev)还保存着tail结点的数据的地址,要让它置空;
prev->next = NULL;
}
}
//单链表的头删
void SListPopFront(SLNode** pphead)
{
assert(*pphead);
SLNode* next = (*pphead)->next;//记录头结点的下一个结点的地址
free(*pphead);//释放头结点
*pphead = next;//头结点的下一个结点作新的头
}
//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x)
{
assert(phead);
SLNode* cur = phead;
while (cur != NULL)
{
if (cur->Data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//单链表在pos位置前插入一个结点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(pos);
SLNode* newnode = BuyListNode(x);//申请结点
//如果只有一个结点
if (*pphead == pos)
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
//找到pos位置的前一个结点,并记录
SLNode* posprev = *pphead;
while (posprev->next != pos)
{
posprev = posprev->next;
}
//让新结点的指针域指向pos,再让pos的前一个结点的指针域重新指向新结点的数据域地址,就很好的插入到pos的前面了
newnode->next = pos;
posprev->next = newnode;
}
}
//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x)
{
assert(pos);
SLNode* newnode = BuyListNode(x);//申请结点
newnode->next = pos->next;//让新结点的指针域指向pos位置的下一个结点的数据域地址
pos->next = newnode;//pos的指针域指向新结点的数据域地址
}
//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos)
{
assert(*pphead);
//如果链表只有一个结点
if (*pphead == pos)
{
SListPopBack(pphead);
}
else
{
//找到pos位置的前一个位置(posPrev),并记录
SLNode* posPrev = *pphead;
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
//让pos位置的前一个结点posPrev的指针域指向pos结点的指针域,如果pos位置还有结点,即指向了pos位置的下一个结点的数据域的地址;
posPrev->next = pos->next;
free(pos);//释放pos
}
}
//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos)
{
assert(pos);
assert(pos->next);
SLNode* next = pos->next;
pos->next = next->next;
free(next);
}
//单链表的销毁
void SListDestroy(SLNode** pphead)
{
assert(*pphead);
SLNode* cur = *pphead;
while (cur != NULL)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
#include "SList.h"
void TestList1()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPushBack(&plist, 5);
SListPrint(plist);
//SListPushFront(&plist, 1);
//SListPushFront(&plist, 2);
//SListPushFront(&plist, 3);
//SListPushFront(&plist, 4);
//SListPushFront(&plist, 5);
//SListPrint(plist);
}
void TestList2()
{
SLTNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPushFront(&plist, 5);
SListPopFront(&plist);
SListPopFront(&plist);
SListPrint(plist);
}
void TestList3()
{
SLTNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 2);
SListPushFront(&plist, 5);
SListPrint(plist);
SLTNode* pos = SListFind(plist, 2);
int i = 1;
while (pos)
{
printf("%d个pos节点:%p->%d\n", i++, pos, pos->data);
pos = SListFind(pos->next, 2);
}
pos = SListFind(plist, 3);
if (pos)
{
pos->data = 30;
}
SListPrint(plist);
}
void TestList4()
{
SLTNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPushFront(&plist, 5);
SListPrint(plist);
//SLTNode* pos = SListFind(plist, 5);
//if (pos)
//{
// SListInsert(&plist, pos, 30);
//}
//SListPrint(plist);
}
void TestList5()
{
SLTNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPushFront(&plist, 5);
SListPrint(plist);
/*
SLTNode* pos = SListFind(plist, 5);
SListInsertAfter(pos, 20);
SListPrint(plist);
*/
/*
SLTNode* pos = SListFind(plist, 3);
SListEase(&plist, pos);
SListPrint(plist);
*/
SLTNode* pos = SListFind(plist, 5);
SListEaseAfter(pos);
SListPrint(plist);
SListDestory(&plist);
}
int main()
{
TestList1();
//TestList2();
//TestList3();
//TestList4();
//TestList5();
return 0;
}
下一期更新顺序表和链表相关的LeetCode题