【数据结构】链表—C/C++实现

简介: 【数据结构】链表—C/C++实现



前言

什么是链表?

线性表的链式存储结构称为链表(Linked List)。链表是一种常见的线性数据结构,用于组织和存储一系列元素,这些元素以节点(Node)的形式连接在一起。每个节点包括两个主要部分:用于存储数据的数据域(Data Field)和指向节点的指针域(Next Pointer)。链表可以有不同的变种,包括单链表双链表循环链表等。

双链表和单链表的优缺点


1. 单链表

1.1 定义

typedef struct LNode//单链表定义
{
    int data;
    struct LNode *next;
}LNode;

1.2 常用操作

1.2.1 创建

尾插法创建:(额...头插和尾插会一个就行,因为尾插法创建的顺序和输入数组一样所以习惯了)

void CreateListB(LNode **L,int a[],int n) //尾插法创建单链表
{
    LNode *r,*s;
    *L=(LNode *)malloc(sizeof(LNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=a[i];
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

1.2.2 输出

void DispList(LNode *L) //输出单链表
{
    LNode *p=L;//p指向头节点
    while (p!=NULL)
    {
        printf("%d ",p->next->data);
        p=p->next;
    }
    printf("\n");
}

1.2.3 插入

void InsertList(LNode **L,int i,int e)//位置i插入元素e
{
    LNode *p,*s,*q;
    p=*L;//p指向头结点
    int j=0;
    while (p!=NULL && j<i-1)//p定位到第i-1节点
    {
        p=p->next;
        j++;
    }
    s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    q=p->next;
    s->next=q;
    p->next=s;
}

1.2.4 删除

void DelList(LNode **L,int i)//删除第i位置元素
{
    LNode *p=*L;//p指向头结点
    int j=0;
    while (p!=NULL && j<i-1)//p定位第i-1节点
    {
        p=p->next;
        j++;
    }
    p->next=p->next->next;
}

1.2.5 销毁

void DestoryList(LNode **L)//销毁链表
{
    LNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
    while (p!=NULL)
    {
        free(p);
        p=p2;
        p2=p2->next;
    }
}

1.3 完整代码

#include <iostream>
using namespace std;
struct LNode{
  int data;
  LNode *next;
};
//创建链表:尾插法
void CreateLNode(LNode **L,int a[],int n){
  LNode *s,*r;
  *L=(LNode *)malloc(sizeof(LNode));
  r=*L;
  for(int i=0;i<n;i++){
    s=(LNode *)malloc(sizeof(LNode));
    s->data=a[i];
    r->next=s;
    r=s;
  }
  r->next=NULL;
}
//输出链表
void DispLNode(LNode **L){
  LNode *p=*L;
  while(p->next!=NULL){
    cout<<p->next->data<<" ";
    p=p->next;
  }
  cout<<endl;
}
//插入
void InsertLNode(LNode **L,int n,int e){
  LNode *p=*L;
  for(int i=0;i<n-1 && p!=NULL;){
    p=p->next;
    i++;
  }
  if(p==NULL) cout<<"error!"<<endl;
  else{
    LNode *q,*s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    q=p->next;
    s->next=q;
    p->next=s;
  }
}
//删除
void DelLNode(LNode **L,int n){
  LNode *p=*L;
  for(int i=0;i<n-1 && p!=NULL;){
    p=p->next;
    i++;
  }
  if(p==NULL) cout<<"error!"<<endl;
  else{
    LNode *q=p->next;
    p->next=q->next;
  }
}
//查找
void Sreach(LNode **L,int e){
  LNode *p=*L;
  int count=0,flag=0;
  while(p->next!=NULL){
    if(p->next->data==e){
      flag=1;
      break;
    }
    else p=p->next;
    count++;
  }
  if(flag!=0){
    cout<<"查找成功,"<<e<<"位于第"<<count<<"位"<<endl;
  }
  else cout<<"查找失败"<<endl;
}
//销毁
void DesLNode(LNode **L){
  LNode *p=*L,*q=(*L)->next;
  while(p!=NULL){
    free(p);
    p=q;
    q=q->next;
  }
}
//判断是否为空串
void NullLNode(LNode **L){
    LNode *p=*L;
    if(p->next== NULL) cout<<"空串"<<endl;
    else cout<<"不是空串"<<endl;
}
int main(){
  LNode *L;
  int a[]={0,1,2,3,4,5,6,7,8,9,10};
  int n=sizeof(a)/sizeof(a[0]);
  CreateLNode(&L,a,n);
  DispLNode(&L);
    NullLNode(&L);
  //插入
  InsertLNode(&L,2,5);
  DispLNode(&L);
  //删除
  DelLNode(&L,2);
  DispLNode(&L);
  //查找
  Sreach(&L,5);
  //销毁
  DesLNode(&L);
  //NullLNode(&L);
  return 0;
}

2. 双链表

2.1 定义

typedef struct LNode//单链表定义
{
    int data;
    struct LNode *next;
}LNode;

2.2 常用操作

2.2.1 创建

尾插法创建:

void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
    DNode *r,*s;
    *L=(DNode *)malloc(sizeof(DNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=a[i];s->prior=r;//就这里不一样而已
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

2.2.2 输出

和单链表一样。

2.2.3 插入

void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
    DNode *p,*s,*q;
    p=*L;
    int j=0;
    while (p!=NULL && j<i-1)
    {
        p=p->next;
        j++;
    }
    s=(DNode *)malloc(sizeof(DNode));
    s->data=e;
    q=p->next;
    s->next=q;q->prior=s;//就这里和单链表不一样
    p->next=s;s->prior=p;//就这里和单链表不一样
}

2.2.4 删除

和单链表一样。

2.2.5 销毁

和单链表一样。

2.3 完整代码

#include <stdio.h>
#include <stdlib.h>
typedef struct DNode
{
    int data;
    struct DNode *prior;
    struct DNode *next;
}DNode;
void CreateDListB(DNode **L,int a[],int n); //尾插法创建双链表
void DispDList(DNode *L); //输出双链表
void InsertDList(DNode **L,int i,int e);//位置i插入元素e
void DelDList(DNode **L,int i);//删除第i位置元素
void DestoryDList(DNode **L);//销毁链表
int main()
{
    DNode *L;
    int a[10]={0,1,2,3,4,5,6,7,8,9};
    CreateDListB(&L,a,10);
    InsertDList(&L,1,2);
    DelDList(&L,1);
    DispDList(L);
    DestoryDList(&L);
    DispDList(L);
    return 0;
}
void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
    DNode *r,*s;
    *L=(DNode *)malloc(sizeof(DNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=a[i];s->prior=r;
        r->next=s;
        r=s;
    }
    r->next=NULL;
}
void DispDList(DNode *L) //输出双链表
{
    DNode *p=L;//p指向头节点
    while (p!=NULL)
    {
        printf("%d ",p->next->data);
        p=p->next;
    }
    printf("\n");
}
void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
    DNode *p,*s,*q;
    p=*L;
    int j=0;
    while (p!=NULL && j<i-1)
    {
        p=p->next;
        j++;
    }
    s=(DNode *)malloc(sizeof(DNode));
    s->data=e;
    q=p->next;
    s->next=q;q->prior=s;//就这里不一样
    p->next=s;s->prior=p;//就这里不一样
}
void DelDList(DNode **L,int i)//删除第i位置元素
{
    DNode *p=*L;//p指向头结点
    int j=0;
    while (p!=NULL && j<i-1)//p定位第i-1节点
    {
        p=p->next;
        j++;
    }
    p->next=p->next->next;
}
void DestoryDList(DNode **L)//销毁链表
{
    DNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
    while (p!=NULL)
    {
        free(p);
        p=p2;
        p2=p2->next;
    }
}

3. 循环链表

3.1 定义

循环链表是一种链表数据结构,其特点是链表的尾节点指向链表中的头节点,形成一个循环。包括循环单链表和循环双链表。

循环单链表:每个节点包含一个next指针,并且尾节点的next指针指向头节点

循环双链表:每个节点包含next指针piror指针。尾节点的next指针指向头节点,头节点的piror指针指向尾节点

3.2 扫描链表结束的临界条件

p!=L  //while语句内,正常为p!=NULL

3.3 环形链表

环形链路: 环形链路指的是在链表中存在一个或多个节点形成了循环,其中一个节点指向先前的节点,导致链表永无止境地继续下去。是链表中的问题或异常情况

解决:判断链表是否为环形链表,通常可以使用两个指针(快慢指针)的方法,也称为弗洛伊德环检测算法。以下是判断环形链表的一般步骤:

  1. 使用两个指针,一个称为"慢指针",另一个称为"快指针",同时从链表的起始节点出发。
  2. 在每一步中,慢指针前进一步,快指针前进两步。如果链表中存在环,快指针最终会进入环中,并在某个时刻与慢指针相遇。如果链表不包含环,快指针将在某个时刻到达链表的末尾并未曾与慢指针相遇。
  3. 如果快慢指针相遇,那么链表包含环,可以判断为环形链表。

致读者

非知之难,行之为难;非行之难,终之斯难

目录
相关文章
|
6月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
192 0
|
存储 算法 C++
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
二分查找的基本思想是:每次比较中间元素与目标元素的大小,如果中间元素等于目标元素,则查找成功;顺序表是线性表的一种存储方式,它用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素在物理存储位置上也相邻。第1次比较:查找范围R[0...10],比较元素R[5]:25。第1次比较:查找范围R[0...10],比较元素R[5]:25。第2次比较:查找范围R[0..4],比较元素R[2]:10。第3次比较:查找范围R[3...4],比较元素R[3]:15。,其中是顺序表中元素的个数。
537 68
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
582 77
|
11月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
434 30
|
11月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
588 25
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
482 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
479 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
算法 C++
【C++数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】
【数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】 目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果: 任务描述 本关任务:实现二叉排序树的基本算法。 相关知识 为了完成本关任务,你需要掌握:二叉树的创建、查找和删除算法。具体如下: (1)由关键字序列(4,9,0,1,8,6,3,5,2,7)创建一棵二叉排序树bt并以括号表示法输出。 (2)判断bt是否为一棵二叉排序树。 (3)采用递归方法查找关键字为6的结点,并输出其查找路径。 (4)分别删除bt中关键
339 11
【C++数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】
|
11月前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
233 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
|
存储 人工智能 算法
【C++数据结构——图】最短路径(头歌教学实验平台习题) 【合集】
任务描述 本关任务:编写一个程序,利用Dijkstra算法,实现带权有向图的最短路径。 相关知识 为了完成本关任务,你需要掌握:Dijkst本关任务:编写一个程序,利用Dijkstra算法,实现带权有向图的最短路径。为了完成本关任务,你需要掌握:Dijkstra算法。带权有向图:该图对应的二维数组如下所示:Dijkstra算法:Dijkstra算法是指给定一个带权有向图G与源点v,求从v到G中其他顶点的最短路径。Dijkstra算法的具体步骤如下:(1)初始时,S只包含源点,即S={v},v的距离为0。
188 15