单链表(增删查改)

简介: 单链表(增删查改)

一、什么是单链表?


单链表是一种链式存取的数据结构,,链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。以“结点的序列”表示的线性表称作线性链表(单链表),单链表是链式存取的结构。简单来说单链表就是一个一个的节点链接起来的链式结构,每个节点里面存储着一个数据和与它链接的下一个节点的(指针)地址。(注意,每一个节点都是同一种结构体类型)


二、单链表的增删查改


2.1 结构体变量的声明


typedef int SLTDataType;
typedef struct SLTNode
{
  SLTDataType data;
  struct SLTNode* next;
}SLTNode;


2.2 申请新结点


SLTNode* BuySListNode(SLTDataType x)
{
  SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));
  if (newNode == NULL)
  {
    perror("malloc fail");
    return NULL;
  }
  else
  {
    newNode->data = x;
    newNode->next = NULL;
    return newNode;
  }
}


2.2 链表的头插


void SListPushFront(SLTNode** pphead, SLTDataType x)
{
  assert(pphead);
  SLTNode* newNode = BuySListNode(x);
  newNode->next = *pphead;
  *pphead = newNode;
}


2.3 链表的尾插


void SListPushBack(SLTNode** pphead, SLTDataType x)
{
  assert(pphead);
  SLTNode* newNode = BuySListNode(x);
  if (*pphead == NULL)
  {
    *pphead = newNode;
  }
  else
  {
    SLTNode* tail = *pphead;
    while (tail->next)
    {
      tail = tail->next;
    }
    tail->next = newNode;
  }
}


2.4 链表的头删


void SListPopFront(SLTNode** pphead)
{
  assert(pphead && *pphead);
  SLTNode* del = *pphead;
  *pphead = (*pphead)->next;
  free(del);
  del = NULL;
}


2.5 链表的尾删


void SListPopBack(SLTNode** pphead)
{
  assert(pphead);
  assert(*pphead);
  if ((*pphead)->next == NULL)
  {
    free(*pphead);
    *pphead = NULL;
  }
  else
  {
    SLTNode* tailPrev = NULL;
    SLTNode* tail = *pphead;
    while (tail->next)
    {
      tailPrev = tail;
      tail = tail->next;
    }
    tailPrev->next = NULL;
    free(tail);
    tail = NULL;
  }
}


2.6 链表的查找


SLTNode* SListFind(SLTNode* pphead, SLTDataType x)
{
  SLTNode* cur = pphead;
  while (cur)
  {
    if (cur->data == x)
    {
      return cur;
    }
    else
    {
      cur = cur->next;
    }
  }
  printf("找不到%d\n",x);
  return NULL;
}


2.7 链表的任意位置后面插入


void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
  assert(pos);
  SLTNode* newNode = BuySListNode(x);
  SLTNode* posNext = pos->next;
  pos->next = newNode;
  newNode->next = posNext;
}


2.8 链表的任意位置后面删除


void SListEraseAfter(SLTNode* pos)
{
  assert(pos && pos->next);
  SLTNode* posNext = pos->next;
  pos->next = posNext->next;
  free(posNext);
  posNext = NULL;
}


2.9 链表的销毁


//建议传二级指针,因为销毁的话需要把这个指向链表的指针置空
//传一级指针无法把指向链表的指针置空
void SListDestroy(SLTNode** pphead)
{
  SLTNode* cur = *pphead;
  while (cur)
  {
    SLTNode* del = cur;
    cur = cur->next;
    free(del);
    del = NULL;
  }
  *pphead = NULL;
}


2.10 链表的打印


void SListPrint(SLTNode* phead)
{
  SLTNode* cur = phead;
  while (cur)
  {
    printf("%d->", cur->data);
    cur = cur->next;
  }
  printf("NULL\n");
}


三、代码汇总


3.1 SLish.h


#pragma once
//SList.h
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int SLTDataType;
typedef struct SLTNode
{
  SLTDataType data;
  struct SLTNode* next;
}SLTNode;
// 动态申请一个节点
extern SLTNode* BuySListNode(SLTDataType x);
// 单链表打印
extern void SListPrint(SLTNode* phead);
// 单链表尾插
extern void SListPushBack(SLTNode** pphead, SLTDataType x);
// 单链表的头插
extern void SListPushFront(SLTNode** pphead, SLTDataType x);
// 单链表的尾删
extern void SListPopBack(SLTNode** pphead);
// 单链表头删
extern void SListPopFront(SLTNode** pphead);
// 单链表查找
extern SLTNode* SListFind(SLTNode* pphead, SLTDataType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
extern void SListInsertAfter(SLTNode* pos, SLTDataType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
extern void SListEraseAfter(SLTNode* pos);
// 单链表的销毁
extern void SListDestroy(SLTNode** pphead);


3.2 SLish.c


#define _CRT_SECURE_NO_WARNINGS 1
//Slist.c
#include "SList.h"
SLTNode* BuySListNode(SLTDataType x)
{
  SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));
  if (newNode == NULL)
  {
    perror("malloc fail");
    return NULL;
  }
  else
  {
    newNode->data = x;
    newNode->next = NULL;
    return newNode;
  }
}
void SListPrint(SLTNode* phead)
{
  SLTNode* cur = phead;
  while (cur)
  {
    printf("%d->", cur->data);
    cur = cur->next;
  }
  printf("NULL\n");
}
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
  assert(pphead);
  SLTNode* newNode = BuySListNode(x);
  if (*pphead == NULL)
  {
    *pphead = newNode;
  }
  else
  {
    SLTNode* tail = *pphead;
    while (tail->next)
    {
      tail = tail->next;
    }
    tail->next = newNode;
  }
}
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
  assert(pphead);
  SLTNode* newNode = BuySListNode(x);
  newNode->next = *pphead;
  *pphead = newNode;
}
void SListPopBack(SLTNode** pphead)
{
  assert(pphead);
  assert(*pphead);
  if ((*pphead)->next == NULL)
  {
    free(*pphead);
    *pphead = NULL;
  }
  else
  {
    SLTNode* tailPrev = NULL;
    SLTNode* tail = *pphead;
    while (tail->next)
    {
      tailPrev = tail;
      tail = tail->next;
    }
    tailPrev->next = NULL;
    free(tail);
    tail = NULL;
  }
}
void SListPopFront(SLTNode** pphead)
{
  assert(pphead && *pphead);
  SLTNode* del = *pphead;
  *pphead = (*pphead)->next;
  free(del);
  del = NULL;
}
SLTNode* SListFind(SLTNode* pphead, SLTDataType x)
{
  SLTNode* cur = pphead;
  while (cur)
  {
    if (cur->data == x)
    {
      return cur;
    }
    else
    {
      cur = cur->next;
    }
  }
  printf("找不到%d\n",x);
  return NULL;
}
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
  assert(pos);
  SLTNode* newNode = BuySListNode(x);
  SLTNode* posNext = pos->next;
  pos->next = newNode;
  newNode->next = posNext;
}
void SListEraseAfter(SLTNode* pos)
{
  assert(pos && pos->next);
  SLTNode* posNext = pos->next;
  pos->next = posNext->next;
  free(posNext);
  posNext = NULL;
}
void SListDestroy(SLTNode** pphead)
{
  SLTNode* cur = *pphead;
  while (cur)
  {
    SLTNode* del = cur;
    cur = cur->next;
    free(del);
    del = NULL;
  }
  *pphead = NULL;
}


3.3 test.c


#define _CRT_SECURE_NO_WARNINGS 1
//test.c
#include "SList.h"
void test_SListPushBack(void)
{
  SLTNode* plist = NULL;
  SListPushBack(&plist, 1);
  SListPushBack(&plist, 2);
  SListPushBack(&plist, 3);
  SListPushBack(&plist, 4);
  SListPushBack(&plist, 5);
  SListPrint(plist);
  SLTNode* ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListInsertAfter(ret, 6);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListInsertAfter(ret, 7);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListInsertAfter(ret, 8);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListInsertAfter(ret, 9);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListInsertAfter(ret, 10);
    SListPrint(plist);
  }
  /*SLTNode* ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListEraseAfter(ret);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListEraseAfter(ret);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListEraseAfter(ret);
    SListPrint(plist);
  }
  ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListEraseAfter(ret);
    SListPrint(plist);
  }*/
  /*ret = SListFind(plist, 1);
  if (ret != NULL)
  {
    printf("找到了,地址为:%p\n", ret);
    SListEraseAfter(ret);
    SListPrint(plist);
  }*/
  //SListInsertAfter(ret, 8);
  /*SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist); 
  SListPopFront(&plist);
  SListPrint(plist); 
  SListPopFront(&plist);
  SListPrint(plist); 
  SListPopFront(&plist);
  SListPrint(plist);*/
}
void test_SListPushFront(void)
{
  SLTNode* plist = NULL;
  SListPushFront(&plist, 1);
  SListPushFront(&plist, 2);
  SListPushFront(&plist, 3);
  SListPushFront(&plist, 4);
  SListPushFront(&plist, 5);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  /*SListPopFront(&plist);
  SListPrint(plist);*/
  //SListDestroy(plist);
  //SListPrint(plist);
  /*SListPopBack(&plist);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);*/
}
int main()
{
  //test_SListPushBack();
  test_SListPushFront();
  return 0;
}
相关文章
|
算法 调度 C++
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
318 0
|
8月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
307 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
10月前
|
数据处理
《原子操作:程序世界里的“最小魔法单位”解析》
在计算机编程中,原子操作是解决并发和多线程问题的关键。它指在执行过程中不会被其他操作中断的操作,确保数据处理的完整性和一致性。本文深入探讨了原子操作的概念、重要性、与普通操作的区别、应用场景及局限性,帮助读者更好地理解和应用这一核心技术。
286 3
|
10月前
|
JSON 测试技术 持续交付
自动化测试与脚本编写:Python实践指南
自动化测试与脚本编写:Python实践指南
278 1
|
监控 数据可视化 Devops
DevOps的心脏:持续集成与持续部署(CI/CD)的实践之道
在软件开发的快节奏竞赛中,DevOps作为提升交付速度和软件质量的关键战略,其核心组成部分——持续集成(Continuous Integration, CI)与持续部署(Continuous Deployment, CD)——已经成为现代企业追求敏捷性和竞争力的标配。本篇文章将深入探讨如何有效实施CI/CD,通过实际案例分析、统计数据支持以及最佳实践指南,为读者呈现一个全景式的CI/CD实践路径。
430 57
|
11月前
|
Devops jenkins 测试技术
DevOps实践:持续集成与持续部署(CI/CD)的实现之路
【9月更文挑战第33天】在软件开发的海洋中,DevOps是一艘能够加速航行、提升航程质量的巨轮。本文将作为你的航海图,指引你理解并实现DevOps文化中的核心环节——持续集成(CI)与持续部署(CD)。我们将从基础概念出发,逐步深入到实际操作,带你领略代码到部署的全过程。准备好扬帆起航,让我们共同探索如何通过自动化工具和流程优化,让软件交付变得既高效又可靠。
|
监控 数据可视化 Serverless
Elasticsearch Serverless体验
通过这次体验探究阿里云Elasticsearch Serverless版的基本功能、性能表现以及稳定性。同时也会针对Elasticsearch版进行对比分析。
585 68
|
消息中间件 Python
深入理解操作系统的进程间通信(IPC)机制
本文将探讨操作系统中的核心概念——进程间通信(IPC),揭示其在系统运作中的重要性及实现方式。通过分析不同类型的IPC手段,如管道、信号、共享内存等,帮助读者更好地理解操作系统的内部工作原理及其在实际应用中的表现。
569 1
|
程序员 C++
malloc与free的内存管理奥秘:技术分享
【8月更文挑战第22天】在软件开发过程中,内存管理是一个至关重要的环节。特别是在使用C或C++这类语言时,程序员需要手动管理内存的分配与释放。malloc和free函数是这一过程中的核心工具。本文将深入探讨malloc如何分配内存,以及free如何知道释放多少内存,帮助你在工作学习中更好地掌握这一技术干货。
290 4
|
测试技术 持续交付 开发者
自动化测试之美:从零开始构建Python测试脚本
【8月更文挑战第31天】在软件开发的海洋中,自动化测试是一艘能够引领我们高效航行的帆船。本文将带领读者踏上一段探索性旅程,深入浅出地介绍如何使用Python语言编写自动化测试脚本。从搭建测试环境到编写实用测试案例,我们将一步步解锁软件测试的秘密,确保代码质量和项目成功。让我们启航吧!