前言
链表是一种物理存储单元上非连续、非顺序的存储结构,由一系列结点组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
一、链表是什么?
1.概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
2.链表的组成
每个链表节点包含两个部分:存储数据元素的数据域和存储下一个节点地址的指针域。节点中的数据可以是任何类型,如整数、字符、浮点数等。此外,链表有一个头节点,它指向链表中的第一个节点。最后一个节点则指向NULL,表示链表的结束。
2.链表的优点与缺点
链表的主要优势在于它可以灵活地管理长度或数量不确定的数据,对于这种逐渐增加且不定长的数据,使用链表可以节省内存。当需要增加新数据时,链表可以动态地申请内存,避免了一次性分配大量空间可能造成的浪费。另外,链表并不需要按照序号对数据进行随机访问。
然而,相比于数组,链表在访问数据时可能耗费更多的时间,因为访问元素可能需要遍历整个链表。尽管如此,链表在插入和删除数据方面的效率较高,因为这些操作仅需要修改指针所指向的区域,不需要进行大量的数据移动操作。
二、单链表的创建;插入(头插、尾插);删除(头删、尾删、指点元素删除);查找
1.单链表的存储结构
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;//这里用typedef声名int类型,是方便后续修改为其它类型
typedef struct SListNode {
SLTDateType data;
struct SListNode* next;
}SLTNode;
2.创建一个节点
SLTNode* BuyListNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
3.插入元素 (尾插、头插)和 指定位置插入
void SListPushBack(SLTNode** pphead, SLTDateType x)//尾插
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SLTNode** pphead, SLTDateType x)//头插
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
void SListInsertAfter(SLTNode* pos, SLTDateType x)//在pos位置后面插入元素
{
assert(pos->next);
SLTNode* newnode = BuyListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
4.删除节点(尾删、头删)和删除指定位置
void SListPopBack(SLTNode** pphead)//尾删
{
assert(pphead);
if (*pphead == NULL)
{
return;
}
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
void SListPopFront(SLTNode** pphead)//头删
{
assert(*pphead != NULL);
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
void SListErase(SLTNode** pphead, SLTNode* pos)//删除掉指定位置
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
*pphead = pos->next;
free(pos);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
5.查找节点
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (x == cur->data)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
6.销毁所有节点
void SListDestory(SLTNode** pphead)//销毁
{
assert(*pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
printf("销毁成功");
}
7.打印所有节点
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
8.主函数
test1()
{
SLTNode* plist = NULL;
printf(" 尾插:");
SListPushBack(&plist, 4);
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPushBack(&plist, 9);
SListPushBack(&plist, 3);
SListPushBack(&plist, 5);//尾插
SListPrint(plist);
printf(" 头插:");
SListPushFront(&plist, 11);//头插
SListPrint(plist);
printf(" 尾删:");
SListPopBack(&plist);
SListPrint(plist);
printf(" 头删:");
SListPopFront(&plist);
SListPrint(plist);
printf("查找元素:");
SLTNode* k=SListFind(plist, 2);
//这里函数调用后,查找到会返回一个指针,表示查找到的位置。如果打印的话,返回指针位置的后续节点也会打印出来
SListPrint(k);
printf("销毁链表:");
SListDestory(&plist);//销毁链表
}
test2()
{
SLTNode* plist = NULL;
printf(" 尾插:");
SListPushBack(&plist, 4);
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPushBack(&plist, 9);
SListPushBack(&plist, 3);
SListPushBack(&plist, 5);//尾插
SListPrint(plist);
printf(" 查找元素:");
SLTNode* k = SListFind(plist, 8);//这里输入需要查找的元素8,返回存储8的地址的指针
//这里函数调用后,查找到元素会返回一个指针
printf(" 我找到了元素%d,函数调用后我返回了记录%d位置的指针k \n", k->data, k->data);//看一下找到没?找到了,位置返回正确
printf("指定位置k插入:");
SListInsertAfter(k, 19);
SListPrint(plist);
printf("指定位置k删除:");//这里用开始返回的k指针
SListErase(&plist,k);//删除掉指定位置(也就是删除8)
SListPrint(plist);//可以看到8已经被删除了
}
int main()
{
test1();
//test2();//这里测试:删除指定位置;指定位置(地址)后面插入
return 0;
}
9.完整代码(含test1和test2,都可以进行测试)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;//这里用typedef声名int类型,是方便后续修改为其它类型
typedef struct SListNode {
SLTDateType data;
struct SListNode* next;
}SLTNode;
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
SLTNode* BuyListNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPushBack(SLTNode** pphead, SLTDateType x)//尾插
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SLTNode** pphead, SLTDateType x)//头插
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
void SListPopBack(SLTNode** pphead)//尾删
{
assert(pphead);
if (*pphead == NULL)
{
return;
}
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
void SListPopFront(SLTNode** pphead)//头删
{
assert(*pphead != NULL);
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (x == cur->data)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
void SListInsertAfter(SLTNode* pos, SLTDateType x)//在pos位置后面插入元素
{
assert(pos->next);
SLTNode* newnode = BuyListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListErase(SLTNode** pphead, SLTNode* pos)//删除掉指定位置
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
*pphead = pos->next;
free(pos);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
void SListDestory(SLTNode** pphead)//销毁
{
assert(*pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
printf("销毁成功\n");
}
test1()
{
SLTNode* plist = NULL;
printf(" 尾插:");
SListPushBack(&plist, 4);
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPushBack(&plist, 9);
SListPushBack(&plist, 3);
SListPushBack(&plist, 5);//尾插
SListPrint(plist);
printf(" 头插:");
SListPushFront(&plist, 11);//头插
SListPrint(plist);
printf(" 尾删:");
SListPopBack(&plist);
SListPrint(plist);
printf(" 头删:");
SListPopFront(&plist);
SListPrint(plist);
printf("查找元素:");
SLTNode* k=SListFind(plist, 2);
//这里函数调用后,查找到会返回一个指针,表示查找到的位置。如果打印的话,返回指针位置的后续节点也会打印出来
SListPrint(k);
printf("销毁链表:");
SListDestory(&plist);//销毁链表
}
test2()
{
SLTNode* plist = NULL;
printf(" 尾插:");
SListPushBack(&plist, 4);
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPushBack(&plist, 9);
SListPushBack(&plist, 3);
SListPushBack(&plist, 5);//尾插
SListPrint(plist);
printf(" 查找元素:");
SLTNode* k = SListFind(plist, 8);//这里输入需要查找的元素8,返回存储8的地址的指针
//这里函数调用后,查找到元素会返回一个指针
printf(" 我找到了元素%d,函数调用后我返回了记录%d位置的指针k \n", k->data, k->data);//看一下找到没?找到了,位置返回正确
printf("指定位置k插入:");
SListInsertAfter(k, 19);
SListPrint(plist);
printf("指定位置k删除:");//这里用开始返回的k指针
SListErase(&plist,k);//删除掉指定位置(也就是删除8)
SListPrint(plist);//可以看到8已经被删除了
}
int main()
{
test1();
//test2();//这里测试:删除指定位置;指定位置(地址)后面插入
return 0;
}
关注博主。有不懂的可以直接问我,看到消息后我会及时回复!!!