单链表(Singly Linked List)是一种常见的数据结构,用于存储一系列元素,这些元素被组织成一个链表,其中每个元素被称为节点(Node)。每个节点包含两部分信息:
数据(Data):用来存储具体的元素值。
指针(Pointer):用来指向下一个节点的位置。
单链表的特点是节点之间只有一个指针,指向下一个节点,而最后一个节点的指针通常为空(或者指向一个特殊的终止标志)。
单链表(Singly Linked List)是一种常见的数据结构,具有一些优点和缺点,这些特点使得它在特定的应用场景中更适合使用。
单链表的优点:
高效的插入和删除操作: 在单链表中,插入和删除元素非常高效,只需要调整指针的指向,而不需要像数组那样搬移元素。这使得单链表在需要频繁插入和删除操作的情况下非常有用。
动态内存分配: 单链表的大小可以根据需要动态增长,而不需要预先分配固定大小的内存空间。这使得它更适合处理未知大小的数据集。
不浪费内存: 单链表只需额外的指针来连接节点,而不像数组那样需要预分配大块连续内存空间。这可以减少内存的浪费。
易于实现: 单链表的基本操作相对简单,易于实现。这使得它成为教学和学习数据结构的良好选择。
单链表的缺点:
随机访问慢: 单链表中要访问特定位置的元素需要从头节点开始遍历链表,直到找到目标节点。这导致访问时间是线性的,而不像数组那样可以常数时间内访问特定位置的元素。
额外的内存开销: 每个节点都需要一个额外的指针来指向下一个节点,这会导致内存开销相对较大,特别是当节点数量很大时。
不适用于大型数据集: 由于额外的指针和节点开销,单链表在存储大量数据时可能不是最佳选择。对于大型数据集,可能会消耗大量内存。
无法直接访问前一个节点: 单链表只有一个指向下一个节点的指针,不能直接访问前一个节点。如果需要双向访问,可以考虑使用双向链表。
总之,单链表是一种非常灵活和高效的数据结构,特别适用于需要频繁插入和删除操作的情况。然而,在需要快速随机访问元素或者对内存使用有限制的情况下,可能需要考虑其他数据结构。不同的数据结构适用于不同的应用场景,选择合适的数据结构取决于问题的需求和约束。
#pragma once
#include
#include
#include
typedef int SLDataType;
typedef struct SListNode
{
int data;
struct SListNode* next;
//strcut STLNode* next; //不能这样定义
}SLTNode;
SLTNode* BuySLTNode(SLDataType x);
SLTNode* CreateSList(int n); //创建n个节点
void SLTPrint(SLTNode* phead); //打印链表
void SLTPushBack(SLTNode** pphead, SLDataType x); //尾插
void SLTPopBack(SLTNode** pphead); //尾删
void SLTPushFront(SLTNode** pphead, SLDataType x);//头插
void SLTPopFront(SLTNode** pphead); //头删
SLTNode* SLTFind(SLTNode* plist, SLDataType x); //查找
void SLTInsertAfter(SLTNode* pos, SLDataType x);// 插入 pos为插入的位置,x为插入的值
void SLTInert(SLTNode** pphead, SLTNode* pos, SLDataType x); //前插
void SLTEraseAfter(SLTNode* pos); //删除pos后的一个节点
void SLTErase(SLTNode** pphead, SLTNode* pos); //删除pos节点
void SLTDestroy(SLTNode** pphead); //释放单链表
#include "SList.h"
SLTNode* BuySLTNode(SLDataType x)//创建一个节点,并将x放到节点的data中
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
SLTNode* CreateSList(int n) //创建n个节点
{
SLTNode* phead = NULL, *ptail = NULL;
for (int i = 0; i < n; i++)
{
SLTNode* newnode = BuySLTNode(i); //创建一个新节点,节点中data数据放入 i
if (phead == NULL)
{
ptail = phead = newnode; //初始化第一个节点
}
else
{
ptail->next = newnode;
ptail = newnode; // ???
}
}
return phead;
}
void SLTPrint(SLTNode* phead) //打印链表
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next; // 将cur结构体中的next 地址传给cur,cur指向下一个节点的地址
}
printf("NULL\n");
}
void SLTPushBack(SLTNode** pphead, SLDataType x)//尾插
{
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
//void SLTPopBack(SLTNode* phead) //尾删
//{
// SLTNode* tail = phead;//将传入的链表的地址传递给tail
// while (tail->next != NULL)
// {
// tail = tail->next;
// }
// free(tail);
// tail = NULL; //错误!!只将最后一个节点释放了,前一个节点指向的next地址不是NULL
//}
void SLTPopBack(SLTNode** pphead) //尾删
{
//assert(*pphead); //断言,对链表为空的情况进行干预
if (*pphead == NULL) // 两种检查方式
{
return;
}
if ((*pphead)->next == NULL) //当只有一个节点时
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;//将传入的链表的地址传递给tail
SLTNode* prev = NULL;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL; //此时prev最后一个节点,将最后一个节点的next置空
}
}
void SLTPushFront(SLTNode** pphead, SLDataType x)//头插
{
SLTNode* newnode = BuySLTNode(x); //创建一个新节点
newnode->next = *pphead; //新节点的next指向pphead
*pphead = newnode; //pphead再重新指向第一个节点
}
void SLTPopFront(SLTNode** pphead) //头删
{
assert(*pphead);//断言检查
SLTNode* plist = *pphead;
free(*pphead);
*pphead = plist->next;
}
SLTNode* SLTFind(SLTNode* phead, SLDataType x) //查找
{
SLTNode* cur = phead;
while (cur != NULL)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL; //返回为空证明没有找到
}
void SLTInsertAfter(SLTNode* pos, SLDataType x)// 插入 pos为插入的位置,x为插入的值
{
assert(pos != NULL); //这里要防止pos为NULL的情况
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next; //先将newnode节点指向pos的下一个节点
pos->next = newnode;
}
void SLTInert(SLTNode** pphead, SLTNode* pos, SLDataType x) //前插,造pos之前插入
{
assert(pos != NULL);
if (*pphead == pos) //如果为第一个节点,头插
{
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
else
{
SLTNode* prev = *pphead; //定义一个新节点,用于查找pos的前一个节点
while (prev->next != pos) // 查找pos的前一个指针
{
prev = prev->next;
}
SLTNode* newnode = BuySLTNode(x); //新建一个节点
newnode->next = pos; //新节点的下一个节点指向pos
prev->next = newnode; // newnode的前一个节点指向newnode
}
}
void SLTEraseAfter(SLTNode* pos) //删除pos后的一个节点
{
assert(pos != NULL);
if (pos->next == NULL) //如果pos节点为最后一个节点
{
return; //无法删除,直接退出
}
else //如果为中间位置
{
SLTNode* nextNode = pos->next;
pos->next = nextNode->next;
free(nextNode); //释放pos后的节点
}
}
void SLTErase(SLTNode** pphead, SLTNode* pos) //删除pos节点
{
assert(pos != NULL);
if (*pphead == pos) //当pos位置为第一个节点时
{
*pphead = pos->next;
free(pos);//释放掉pos节点
}
else
{
SLTNode* prev = *pphead; //创建一个新节点,用于找pos的前一个节点
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next; // 将pos的前一个节点的next指向pos的后一个节点
free(pos); // 释放pos,避免内存泄漏
}
}
void SLTDestroy(SLTNode** pphead) //释放单链表
{
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
#include
#include "SList.h"
//顺序表的缺点:
// 1、空间不够的情况下,需要进行扩容。扩容(尤其异地扩容)是由一定代价的,其次还可能存在一定的空间浪费
// 2、头部或者中部插入删除,需要挪动数据,效率低下
// 链表头插效率很高
void SListTest1()
{
/*
SLTNode* n1 = BuySLTNode(1);
SLTNode* n2 = BuySLTNode(2);
SLTNode* n3 = BuySLTNode(3);
SLTNode* n4 = BuySLTNode(4);
n1->next = n2; // n1节点的next指向n2节点的地址
n2->next = n3;
n3->next = n4;
n4->next = NULL;
*/
SLTNode* plist = CreateSList(10);
SLTPrint(plist);
}
void SListTest2()
{
SLTNode* plist = CreateSList(5);
SLTPushBack(&plist, 100);
SLTPushBack(&plist, 200);
SLTPushBack(&plist, 300);
}
void SListTest3()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 100);
SLTPushBack(&plist, 200);
SLTPushBack(&plist, 300);
SLTPushBack(&plist, 400);
SLTPushBack(&plist, 500);
SLTPrint(plist);
//SLTPopBack(&plist);
//SLTPopBack(&plist);
//SLTPopBack(&plist);
//SLTPopBack(&plist);
//SLTPopFront(&plist);
SLTPrint(plist);
SLTNode* pos = SLTFind(plist, 300);
if (pos != NULL)
{
printf("找到了!\n");
//SLTInsertAfter(pos, 30); //后插
//SLTInert(&plist, pos, 30); //前插
//SLTEraseAfter(pos); //删除pos后一个节点
SLTErase(&plist, pos);
}
else
{
printf("没有找到!!\n");
}
SLTDestroy(&plist); //销毁链表
SLTPrint(plist);
}
void Swap1(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void Swap2(int** pp1, int** pp2)
{
int* tmp = *pp1;
*pp1 = *pp2;
*pp2 = tmp;
}
int main()
{
//SListTest1();
//SListTest2();
SListTest3();
//int a = 1, b = 0;
//Swap1(&a, &b);
return 0;
}