目录
概述:
继上文我们了解了什么是顺序表后,接下来就让我们来了解下什么是链表也就是什么是线性表的链式存储结构。
一、什么是链表
1、概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的
。
2、存储结构的特点
用任意一组的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存中未被占用的任意位置。
并且链表是由一系列的结点构成,那什么是结点呢?
3、什么是结点
结点是由两个部分组成,对于一个数据元素a1来说,除了存储本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。那我们就把存储数据元素信息的域称为数据域。把存储直接后继的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成数据元素a1的存储印象,称为结点(Node)。
4、如何理解链表
通常我们会用两种方法来画图理解链表
第一种:逻辑图
第二种:物理图
我们可以从物理图当中看出,链表的存储位置可以是连续的,也可以是不连续的。
5、链表的构成
typedef int SLTDataType;
//定义
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
在32位的机器中,链表的大小为8个字节。而在64位的机器中,链表的大小为12个字节。
二、单链表的一些基本操作
打印链表
void SLTprint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d ->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
清空链表,释放内存
// 释放内存空间
void SLTDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
在进行增加结点之前,我们需要向内存申请空间来创建新的结点
//开辟新结点
SLTNode* BuySListNode(SLTDataType x)
{
//SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTDataNode));
//因为要给一个结点开辟空间,所以这里不能使用SLTDataNode(4个字节),而是使用SLTNode(8个字节)
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
尾插结点
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead==NULL)
{
//改变的结构体的指针,所以要用二级指针
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
//改变的结构体,用结构体的指针即可
tail->next = newnode;
}
}
头插结点
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
尾删结点
//尾删
void SLTPopBack(SLTNode** pphead)
{
//1、空
assert(*pphead);
//2、一个结点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//3、多个结点
else
{
SLTNode* tailprev = NULL;
SLTNode* tail = *pphead;
while (tail->next)
{
tailprev = tail;
tail = tail->next;
}
free(tail);
tailprev->next = NULL;
}
}
头删结点
//头删
void SLTPopFront(SLTNode** pphead)
{
//1、空
assert(*pphead);
//2、不为空
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
查找数据元素
// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
SLTNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
在pos结点前插入一个结点
// 在pos的前面插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);
assert(pphead);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
在pos结点之后插入一个结点
// 在pos之后插入
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode= BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
删除pos结点
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
删除pos结点后的一个结点
// 删除pos后一个位置
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
//检查pos是否为尾结点
assert(pos->next);
SLTNode* posNext = pos->next;
pos->next = posNext->next;
free(posNext);
posNext = NULL;
}
三、完整代码
头文件
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDataType;
//定义
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
//打印
void SLTprint(SLTNode* phead);
//开辟一个新结点
SLTNode* BuySListNode(SLTDataType x);
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//头删
void SLTPopFront(SLTNode** pphead);
//尾删
void SLTPopBack(SLTNode** pphead);
// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDataType x);
// 在pos的前面插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
// 在pos之后插入
void SListInsertAfter(SLTNode* pos, SLTDataType x);
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos);
// 删除pos后一个位置
void SListEraseAfter(SLTNode* pos);
// 释放内存空间
void SLTDestroy(SLTNode** pphead);
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
//打印
void SLTprint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d ->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//开辟新结点
SLTNode* BuySListNode(SLTDataType x)
{
//SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTDataNode));
//因为要给一个结点开辟空间,所以这里不能使用SLTDataNode(4个字节),而是使用SLTNode(8个字节)
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead==NULL)
{
//改变的结构体的指针,所以要用二级指针
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
//改变的结构体,用结构体的指针即可
tail->next = newnode;
}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//尾删
void SLTPopBack(SLTNode** pphead)
{
//1、空
assert(*pphead);
//2、一个结点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//3、多个结点
else
{
SLTNode* tailprev = NULL;
SLTNode* tail = *pphead;
while (tail->next)
{
tailprev = tail;
tail = tail->next;
}
free(tail);
tailprev->next = NULL;
}
}
//头删
void SLTPopFront(SLTNode** pphead)
{
//1、空
assert(*pphead);
//2、不为空
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
SLTNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
// 在pos的前面插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);
assert(pphead);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
// 在pos之后插入
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode= BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
// 删除pos后一个位置
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
//检查pos是否为尾结点
assert(pos->next);
SLTNode* posNext = pos->next;
pos->next = posNext->next;
free(posNext);
posNext = NULL;
}
// 释放内存空间
void SLTDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
实现代码的一些案例
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void TestSList1()
{
int n = 0;
printf("请输入链表的长度:");
scanf("%d", &n);
printf("\n请依次输入每个节点的值:");
SLTNode* plist = NULL;
//用scanf手动输入数据
for (int i = 0; i < n; i++)
{
int val = 0;
scanf("%d", &val);
SLTNode* newnode = BuySListNode(val);
//头插
newnode->next = plist;
plist = newnode;
}
SLTprint(plist);
//尾插
SLTPushBack(&plist, 10000);
SLTprint(plist);
}
void TestSList2()
{
//使链表为空
SLTNode* plist = NULL;
//尾插
SLTPushBack(&plist, 10000);
SLTPushBack(&plist, 20000);
SLTPushBack(&plist, 30000);
SLTPushBack(&plist, 40000);
SLTPushBack(&plist, 50000);
SLTprint(plist);
//头插
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPushFront(&plist, 5);
SLTprint(plist);
}
void TestSList3()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 10000);
SLTPushBack(&plist, 20000);
SLTPushBack(&plist, 30000);
SLTPushBack(&plist, 40000);
SLTPushBack(&plist, 50000);
SLTprint(plist);
//尾删
SLTPopBack(&plist);
SLTprint(plist);
SLTPopBack(&plist);
SLTprint(plist);
SLTPopBack(&plist);
SLTprint(plist);
SLTPopBack(&plist);
SLTprint(plist);
SLTPopBack(&plist);
SLTprint(plist);
/*SLTPushBack(&plist);
SLTprint(plist);*/
}
void TestSList4()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 10000);
SLTPushBack(&plist, 20000);
SLTPushBack(&plist, 30000);
SLTPushBack(&plist, 40000);
SLTPushBack(&plist, 50000);
//头删
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
}
void TestSList5()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 10000);
SLTPushBack(&plist, 20000);
SLTPushBack(&plist, 30000);
SLTPushBack(&plist, 40000);
SLTPushBack(&plist, 50000);
SLTprint(plist);
SLTNode* pos=SListFind(plist, 10000);//找到数据域为10000的结点,并用结构体指针pos记录
if (pos)
{
pos->data *= 10;
}
SLTprint(plist);
int x;
scanf("%d", &x);
pos = SListFind(plist, x);
if (pos)
{
SLTInsert(&plist, pos, x * 10);
}
SLTprint(plist);
}
void TestSList6()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTprint(plist);
int x;
scanf("%d", &x);
SLTNode* pos = SListFind(plist, x);
if (pos)
{
SListInsertAfter(pos, x * 10);
}
SLTprint(plist);
}
void TestSList7()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTprint(plist);
int x;
scanf("%d", &x);
SLTNode* pos = SListFind(plist, x);
if (pos)
{
//SLTErase(&plist, pos);
SListEraseAfter(pos);
pos = NULL;
}
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
SLTPopFront(&plist);
SLTprint(plist);
}
int main()
{
TestSList7();
return 0;
}