单链表
- 1. 函数的声明部分
- 2. 函数的实现部分
- (1)打印链表
- (2)头插
- (3)尾插
- (3)头删
- (4)尾删
- (5)单链表的查找
- (6)删除pos位置之后的值
- (7)在pos位置之后插入x
- 3. 函数的测试部分
单链表概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
1. 函数的声明部分
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDateType;
typedef struct SingleListNode
{
SLTDateType data;
struct SingleListNode* next;
}SLTNode;
//打印
void SLTPrint(SLTNode* phead);
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x);
//头删
void SLTPopFront(SLTNode** pphead);
//尾删
void SLTPopBack(SLTNode** pphead);
//在pos位置之后插入x
void SLTInsertAfter(SLTNode* pos, SLTDateType x);
//删除pos位置之后的值
void SLTEraseAfter(SLTNode* pos);
//单链表查找
SLTNode* SLTFind(SLTNode** pphead, SLTDateType x);
2. 函数的实现部分
由于头插,尾插等需要开辟一个节点,所以把开辟节点单独作为一个函数,需要开辟节点的时候直接调用;
SLTNode* BuyListNode(SLTDateType x)
{
//开辟一个节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
//对newnode初始化
newnode->data = x;
newnode->next = NULL;
return newnode;
}
(1)打印链表
//打印
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
(2)头插
头插需要传二级指针,因为在调用函数SLTPushFront的时候,实参plist本来是结构体指针,现在头插需要改变链表的头,即需要传地址去改变plist的头;
如进行以下操作,值传递操作:
假设链表是如下形式:
当头插函数使用一级指针接收实参时,实参和形参同为一级指针,它们是同等类型的,它们在两个不同的栈空间,假如进行以下操作:
实际上phead并不会改变plist的值:
因为它们两个在不同的栈空间,phead只是plist的临时拷贝,只要出了SLTPushFront这个函数,栈帧被销毁,phead也被销毁了,但是phead的改变也没有改变plist的值,所以相当于什么也没有发生;
所以需要传二级指针,存放plist的指针,到函数内部需要解引用找到plist,再去改变plist的值,这样才能达到我们想要的效果;
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
SLTNode* newnode = BuyListNode(x);
//将newnode的next更新为原来的头节点
newnode->next = *pphead;
//将newnode更新为新的头节点
*pphead = newnode;
}
(3)尾插
尾插的时候,当链表为空时,需要改变的是plist这个结构体指针,所以这个时候也是要传二级指针;当链表为非空链表时,需要改变的是结构体,所以不需要用到二级指针;但为了防止链表为空,这里干脆直接传二级指针;
//尾插
void SLTPushBack(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;
}
}
(3)头删
//头删
void SLTPopFront(SLTNode** pphead)
{
//没有节点
assert(*pphead);
//一个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个节点
else
{
SLTNode* cur = *pphead;
*pphead = cur->next;
free(cur);
cur = NULL;
}
}
(4)尾删
//尾删
void SLTPopBack(SLTNode** pphead)
{
//空链表
assert(*pphead);
//一个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个节点
else
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
(5)单链表的查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
(6)删除pos位置之后的值
void SLTEraseAfter(SLTNode* pos)
{
assert(pos && pos->next);
SLTNode* cur = pos;
cur->next = cur->next->next;
}
(7)在pos位置之后插入x
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
SLTNode* cur = BuyListNode(x);
assert(cur);
cur->next = pos->next;
pos->next = cur;
}
3. 函数的测试部分
#define _CRT_SECURE_NO_WARNINGS 1
#include "Single List.h"
int main()
{
SLTNode* plist = NULL;
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 1);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
//SLTPopFront(&plist);
//SLTPopBack(&plist);
//SLTInsertAfter(plist->next->next, 10);
SLTEraseAfter(plist);
SLTPrint(plist);
SLTNode* ret = SLTFind(plist, 5);
if (ret != NULL)
{
printf("%d->", ret->data);
}
else
{
printf("NULL\n");
}
SListDestroy(&plist);
return 0;
}