数据结构-链表SListNode详解
1.链表与顺序表的关系
链表也是顺序表的一种,顺序表是相同元素,逻辑上连续排列的一种数据结构。但是在物理上,链表的存储是随机的而不是连续的。
2.链表与顺序表的对比
顺序表的缺点
- 进行指定位置插入或者删除时,需要移动元素,花费大量时间
- 进行扩容时,扩大了浪费,少了需要多次扩容
链表的优点
- 进行指定位置插入或者删除时,不需要进行元素的移动
- 物理上不是连续的,不需要进行扩容。添加一次申请一次内存,不会浪费额外空间。
3.链表的接口以及实现
#pragma once
// slist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
//因为只能知道指针A的next是什么,不能知道哪一个指针的next时A,是向后链接的
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
//无法获得pos前一个指针
void SListEraseAfter(SListNode* pos);
3.1动态申请一个节点
SListNode* BuySListNode(SLTDateType x) {
SListNode* p = malloc(sizeof(SListNode));
if (p == NULL)
{
perror("malloc Node fail");
return;
}
p->data = x;
p->next = NULL;
return p;
}
当链表需要添加元素的时候,才需要申请节点。这时需要手动malloc一个节点,接节点的val设为传入的参数,节点的next为NULL。返回这个节点的地址
3.2单链表打印
void SListPrint(SListNode* plist) {
SListNode* p = plist;
while (p!= NULL)
{
printf("%d->", p->data);
p = p->next;
}
printf("NULL\n");
}
使用指针遍历链表,当指针指向不为空时,输出指针的val,将指针移动到next
test
void test1(){
SListNode* pplist = NULL;
SListPushBack(&pplist, 1);
SListPushBack(&pplist, 2);
SListPushBack(&pplist, 3);
SListPushBack(&pplist, 4);
SListPrint(pplist);
}
3.3单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {
SListNode* phead = *pplist;
if (phead == NULL)
{ //链表为空时
*pplist = BuySListNode(x);
}
else
{
//链表不为空时
while (phead->next != NULL)
{
phead = phead->next;
}
phead->next = BuySListNode(x);
}
}
3.4单链表的头插
头插也需要使用二级指针,而且与链表有没有元素没有关系。因为要改变链表的头就代表要改变外部的结构体指针,要改变指针的值就要使用二级指针
void SListPushFront(SListNode** pplist, SLTDateType x) {
SListNode* p = BuySListNode(x);
p->next = *pplist;
*pplist = p;
}
test
void testSListPushFront() {
SListNode* pplist = NULL;
SListPushFront(&pplist, 1);
SListPushFront(&pplist, 2);
SListPushFront(&pplist, 3);
SListPushFront(&pplist, 4);
SListPushBack(&pplist, 5);
SListPrint(pplist);
}
3.5单链表的尾删
当链表只有一个值的时候,需要改变指针的指向
void SListPopBack(SListNode** pplist) {
assert(*pplist);
SListNode* pre = NULL;
SListNode* tail = *pplist;
while (tail->next) {
pre = tail;
tail = tail->next;
}
if (!pre)
{
free(*pplist);
*pplist = NULL;
}
else
{
pre->next = NULL;
free(tail);
}
}
test
void testSListPopBack2() {
SListNode* pplist = NULL;
SListPushFront(&pplist, 1);
SListPushFront(&pplist, 2);
SListPushFront(&pplist, 3);
SListPushFront(&pplist, 4);
SListPrint(pplist);
SListPopBack(&pplist);
SListPrint(pplist);
SListPopBack(&pplist);
SListPrint(pplist);
SListPopBack(&pplist);
SListPrint(pplist);
SListPopBack(&pplist);
SListPrint(pplist);
}
3.6单链表的头删
头删也要改变外面指针的指向,所以也需要二级指针
void SListPopFront(SListNode** pplist) {
assert(*pplist);
SListNode* p = *pplist;
*pplist = p->next;
free(p);
}
test
void testSListPopFront() {
SListNode* pplist = NULL;
SListPushFront(&pplist, 1);
SListPushFront(&pplist, 2);
SListPushFront(&pplist, 3);
SListPushFront(&pplist, 4);
SListPrint(pplist);
SListPopFront(&pplist);
SListPrint(pplist);
SListPopFront(&pplist);
SListPrint(pplist);
SListPopFront(&pplist);
SListPrint(pplist);
SListPopFront(&pplist);
SListPrint(pplist);
}
3.7单链表的查找
查找只需要返回指针的地址,不需要进行修改,所以不需要使用二级指针,遍历链表,当x与链表的val相等时,返回节点地址。
SListNode* SListFind(SListNode* plist, SLTDateType x) {
assert(plist);
while (plist)
{
if (plist->data == x)
{
return plist;
}
plist = plist->next;
}
return NULL;
}
test
void testSListFind() {
SListNode* pplist = NULL;
SListPushFront(&pplist, 1);
SListPushFront(&pplist, 2);
SListPushFront(&pplist, 3);
SListPushFront(&pplist, 4);
printf("%p\n", SListFind(pplist, 1));
printf("%p\n", SListFind(pplist, 2));
printf("%p\n", SListFind(pplist, 3));
printf("%p\n", SListFind(pplist, 4));
}
3.8单链表在pos位置之后插入x
通过find函数获得节点的地址,然后执行在指定节点的插入
void SListInsertAfter(SListNode* pos, SLTDateType x) {
assert(pos);
SListNode* p = BuySListNode(x);
if (p == NULL) {
perror("cant buy node");
return;
}
p->next = pos->next;
pos->next = p;
}
test
void testSListInsertAfter() {
SListNode* pplist = NULL;
SListPushBack(&pplist, 1);
SListPushBack(&pplist, 3);
SListPushBack(&pplist, 5);
SListPushBack(&pplist, 7);
SListPrint(pplist);
SListInsertAfter(SListFind(pplist,1), 2);
SListPrint(pplist);
SListInsertAfter(SListFind(pplist, 3), 4);
SListPrint(pplist);
SListInsertAfter(SListFind(pplist, 5), 6);
SListPrint(pplist);
SListInsertAfter(SListFind(pplist, 7), 8);
SListPrint(pplist);
}
3.9单链表删除pos位置之后的值
因为是删除pos之后的值,永远不可能删除第一个元素,所以不用二级指针。
void SListEraseAfter(SListNode* pos) {
assert(pos);
SListNode* p = pos->next;
pos->next = pos->next->next;
free(p);
}
test
void testSListEraseAfter() {
SListNode* pplist = NULL;
SListPushBack(&pplist, 1);
SListPushBack(&pplist, 2);
SListPushBack(&pplist, 3);
SListPushBack(&pplist, 4);
SListPushBack(&pplist, 5);
SListPushBack(&pplist, 6);
SListPushBack(&pplist, 7);
SListPushBack(&pplist, 8);
SListPrint(pplist);
SListEraseAfter(SListFind(pplist, 7));
SListPrint(pplist);
SListEraseAfter(SListFind(pplist, 5));
SListPrint(pplist);
SListEraseAfter(SListFind(pplist, 3));
SListPrint(pplist);
SListEraseAfter(SListFind(pplist, 1));
SListPrint(pplist);
}