首页 > 编程语言 >【数据结构与算法】线性表链式存储结构

【数据结构与算法】线性表链式存储结构

时间:2024-10-15 22:19:06浏览次数:8  
标签:结点 单链 LNode 指针 next 链表 链式 数据结构 线性表

线性表链式存储结构

文章目录


前面所讲的顺序存储结构,简而言之就是相邻两个元素的存储位置具有邻居关系,它们在内存中的位置是紧挨着的,中间没有间隔,无法快速的插入和删除。那么,我们换一种方法,哪里有空位我们就放哪里,利用指针对元素进行定位,所有的元素就可以通过遍历来找到了。本篇文章,我将详细的介绍线性表的另外一种存储结构——链式存储结构。

链式存储结构

  1. 定义
  • 结点:线性表的链式存储结构的特点是,用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的),因此,为了表示某个数据元素a i与它的直接后继元素a i + 1 之间的逻辑关系,对数据元素a i 来说,除存储其本身的信息之外,还需要存储一个指示它直接后继的信息(即直接后继的存储位置),这两部分信息组成数据元素a i 的存储映像,称为结点(node)。

  • 它包括两个域:其中存储数据元素信息的域称为数据域,存储直接后继存储位置的域称为指针域,指针域中存储的信息称为指针或链,n 个结点(a i (1<=i<=n)的存储映像)链结成一个链表,即为线性表的链式存储结构。data的类型根据具体问题,next的类型是(data类型的)指针

  • 头指针:是指向链表中第一个结点的指针

  • 头结点:它是链表中的辅助结点,只包含指向第0个数据元素的指针,而没有数据信息,头结点有简化代码的作用,因为它始终指向了第0个元素,便于执行时对元素位置的定位。

  • 首元结点:是指链表中存储第一个数据元素a1的结点

  • 尾结点:尾结点中存储的地址信息可以用于区分链表类型。尾指针为空:单链表。尾指针指向链表的开头:循环链表。为随机值:非法链表。

  • 数据结点:它是链表中代表数据元素的结点,表现为数据域和指针域。

  1. 特点
  • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
  • 访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找到一个结点和最后一个结点所花费的时间不等。这种存取元素的方法称为顺序存储法
  1. 分类
  • 单链表: 每个结点只包含直接后继的地址信息,结点只有一个指针域的链表。
  • 循环链表:单链表的最后一个结点的直接后继为第一给结点(首尾相接
  • 双向链表:单链表中的结点包含直接前驱和后继的地址信息(结点有两个指针域

*头结点和头指针

上文我们提到了,头结点的数据域一般不存储任何信息,这是它作为第一个结点的特性。但是一个链表中不一定有头结点:

那么这个时候就会有疑惑了,既然头结点的数据域不存储任何信息,那么头指针和头结点又有什么异同呢?

  1. 头指针
  • 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
  • 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)
  • 无论链表是否为空,头指针均不为空。
  • 头指针是链表的必要元素。
  1. 头结点
  • 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度),但此结点不能计入链表长度值。
  • 有了头结点,对在第一个元素结点前插入结点和删除第一结点起操作域其他结点从操作就统一了。
  • 头结点不一定是链表的必须要素。

那么,在链表中设置头结点有什么好处呢?

  1. 便于首元结点的处理

    首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无需进行处理

  2. 便于空表和非空表的统一处理

    无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。

一.线性链表(单链表)

1.1定义

单链表是由表头唯一确定的,因此单链表可以用头指针的名字来命名。若头指针名是L,则把链表称为表L。

带头结点的单链表

我们在C语言中可以用结构指针来描述单链表:

typedef struct Node{//声明结点的类型和指向结点的指针类型
    ElemType data;//结点的数据域
    struct Node* Next;//结点的指针域
}Node;
typedef struct Node* LinkList;//LinkList为指向结构体Lnode的指针类型
  • 定义链表L:LinkList L
  • 定义结点指针p:LNode *pLinkList p

为了统一链表的操作,通常我们这样定义:先将数据域中要存储的多个数据项定义成一个结构类型,然后直接用这个结构类型来定义这个数据域data.

实例:存储学生学号,姓名,成绩的单链表结点类型定义如下:

typedef Struct{
    char num[8];//数据域
    char name[8];//数据域
    int score;//数据域
}ElemType;
typedef struct Lnode{
    ElemType data;//数据域
    struct Londe* next;//指针域
}Lnode;
typedef struct Node* LinkList;

1.2初始化

1.2.1带头结点的初始化

算法思路:构造一个空表:从内存中找到头结点,用指针变量L指向头结点,给结点的指针域置空。

typedef struct LNode{//定义单链表结点类型
    ElemType data;//每个结点存放一个数据元素
    struct LNode* next;//指针指向下一个结点
}LNode;
typedef struct LNode* LinkList;

//初始化一个单链表(带头结点)
bool InitList(LinkList &L){
    L=(LNode*)malloc(sizeof(LNode));//分配一个头结点
    if(L==NULL){//内存分配不足,分配失败
        return false;
    }
    L->next=NULL;//建立空的单链表,结点之后暂时还没有结点
    return true;
}//bool类型函数返回true或false

void test(){
    LinkList L;//声明一个指向单链表的指针
    //初始化一个空表
    InitList(L);
    ……
}

注意:

L是指向单链表的头结点的指针,用来接收主程序中待初始化单链表的头指针变量的地址

*L相当于主程序中待初始化单链表的头指针变量

1.2.2不带头结点的初始化

typedef struct LNode{//定义单链表结点类型
    ElemType data;//每个结点存放一个数据元素
    struct LNode* next;//指针指向下一个结点
}LNode;
typedef struct Lnode* LinkList;

//初始化一个空的单链表
bool InitList(LinkList &L){
    L=NULL;//空表,暂时没有任何结点(防止脏数据)
    return true;
}

void test(){
    LinList L;//声明一个指向单链表的指针
    //初始化空表
    InitList(L);
    ……
}

//判断单链表是否为空
bool Empty(LinkList L){
    if(L==NULL){
        return true;
    }else{
        return false;
    }
}

由此可见这两种情况下判断空表的方法:

  • 无头结点时,头指针为空时表示空表
  • 有头结点时,当头结点的指针域为空时表示空表

1.3插入

1.3.1按位序插入

在表L中的第i个位置上插入指定元素。那么就要找到第i-1个结点的存储位置p,生成一个数据域为e的新结点t,然后将新结点插入其后面:新结点的指针域指向ai,结点ai-1的指针域指向新结点。注意:这两步操作不能交换顺序,不然会丢失ai的地址。

  • 带头结点:
img
typedef struct LNode{           //定义单链表节点类型
    ElemType data;              //每个节点存放一个数据元素    
    struct LNode *next;         //指针指向下一个节点    
}LNode, *LinkList               

//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, ElemType e){
    if(i<1){
        return false;
    }
    if(i==1){
        LNode *t = (LNode *)malloc(sizeof(LNode));
        t->data = e;
        t->next = L;
        L = t:      //头指针指向新结点
        return true;
    }
    LNode *p;       //指针p指向当前扫描的结点
    int j = 1;      //当前p指向的是第几个结点
    p = L;          //p指向第一个结点(注意:不是头结点)
    while(p != NULL && j<i-1){      //循环找到第i-1个结点
        p = p->next;
        j++;
    }
    if(p == NULL){      //i值不合法
        return false;
    }
    LNode *t = (LNode *)malloc(sizeof(LNode));
    t->data = e;
    t->next = p->next;
    p->next = t;
    return true;        //插入成功
}
  • 不带头结点:
在这里插入图片描述
typedef struct LNode{//定义单链表结点类型
    ElemType data;//每个结点存放一个数据元素
    struct LNode* next;//指针指向下一个结点
}LNode;
typedef struct LNode* LinkList;

//在第i个位置插入元素e(不带头结点)
bool ListInsert(LinkList &L,int i,ElemType e){//参数可以是各种类型
    if(i<1){
        return false;
    }
    LNode* p;//指针p指向当前扫描的结点
    int j=0;//当前p指针的是第几个结点
    while(p!=NULL && j<i-1){//循环找到第i-1个结点
        p=p->next;
        j++;
    }
    if(p==NULL){//i值不合法
        return false;
    }
    LNode* t=(LNode*)malloc(sizeof(LNode));//给结点t分配空间
    t->data=e;//给结点t的data赋值为e
    t->next=p->next;//结点t的指针赋值为当前结点p的下一个结点
    p->next=t;//将结点t连接到p之后
    return true;//插入成功
}

i=1时,执行插入操作所需要的时间复杂度为T(n)=O(1)

i>1 && i<=n时,时间复杂度为T(n)=O(n)

但是,值得注意的是,实际上插入这个操作真正的时间复杂度为T(n)=O(1),而程序运行所耗费时间都在找到要插入的元素位置的前一个位置,所以综合考究:

标签:结点,单链,LNode,指针,next,链表,链式,数据结构,线性表
From: https://blog.csdn.net/2301_79279099/article/details/142930369

相关文章

  • 【数据结构】:破译排序算法--数字世界的秩序密码(二)
    文章目录前言一.比较排序算法1.`BubbleSort`冒泡排序1.1.冒泡排序原理1.2.冒泡排序过程1.3.代码实现1.4.复杂度和稳定性2.`QuickSort`快速排序2.1递归快速排序2.1.1.递归快速排序原理2.1.2.递归快速排序过程2.1.3.代码实现2.2.非递归快速排序2.2.1.非递归快速排序原......
  • 嵌入式开发学习日记——数据结构基础
    数据结构基础学习内容概述今天我开始学习数据结构,重点理解了它在编程中的重要性。数据结构是为了高效访问数据而设计的一种数据组织和存储方式。它不仅仅关注数据的存储位置,还关注数据元素之间的关系。计算机科学家尼古拉斯·沃斯提出了著名的公式:算法+数据结构=程序......
  • 数据结构--线性表
    一、线性表的类型定义数据元素类型:线性表由一系列数据元素组成,这些数据元素可以是基本数据类型(如整型、浮点型、字符型等),也可以是复杂的数据类型(如结构体、类、指针等)。存储结构:线性表的存储结构可以是顺序存储或链式存储。顺序存储:使用连续的存储空间来存储线性表的元......
  • 数据结构与算法篇(回溯&递归&分治 - 刷题篇)(目前一天图片上传太多加载不出来)(后续更新)
    目录1.没有重复项数字的全排列(中等)1.1.题目描述1.2解题思路1.3代码实现方法一:递归方法二:非递归版2.有重复项数字的全排列(中等)2.1.题目描述2.2.解题思路2.3.代码实现递归+回溯(推荐使用)3.岛屿数量(中等)3.1.题目描述3.2.解题思路3.3代码实现方法一:dfs......
  • ArrayList源码分析(底层数据结构,成员变量,构造方法)以及面试题(基于JDK1.8)
    要分析Arraylist,我们首先要从它的底层数据结构实现出发,再结合其底层源码,可能能让读者理解的更加深刻一点。1,底层数据结构(数组)Arraylist底层是基于动态数组实现的。数组是一种使用连续储存空间储存相同数据类型的线性数据结构。面试题1为什么数组索引从0开始不从1开始?分......
  • 软考14——数据结构
    ◆无向图:图的结点之间连接线是没有箭头的,不分方向。◆有向图:图的结点之间连接线是箭头,区分A到B,和B到A是两条线。◆完全图:无向完全图中,节点两两之间都有连线,n个结点的连线数为(n·1)+(n-2)+.+1=n*(n-1)/2;有向完全图中,节点两两之间都有互通的两个箭头,n个节点的连线数为n*(n-1)◆度......
  • 【数据结构】学习笔记之栈和队列
    目录一、栈基本概念二、顺序栈2.1置空栈2.2判栈空2.3判栈满2.4进栈2.5退栈2.6取栈顶元素三、链栈3.1建栈3.2判栈空3.3进栈3.4退栈3.5取栈顶元素四、队列基本概念五、顺序队列5.1置队空5.2判队空5.3判队满5.4入队5.5出队5.6取队头元素......
  • (C语言)算法数据结构
    王道数据结构以及本人上课的笔记             ......
  • 有关数据结构线性结构(线性表、栈、队列)的创销增删改查
    #include<stdio.h>#include<iostream>#include<stdlib.h>#defineMaxSize50typedefintElemType;//1.静态顺序存储typedefstruct{   ElemTypedata[MaxSize];   intlength;}SqList;//1.1插入操作boolListInsert(SqList&L,inti,ElemTypee){......
  • 【趣学C语言和数据结构100例】
    【趣学C语言和数据结构100例】问题描述找出一个二维数组中的鞍点,即该位置上的元素在该行上最大、在该列上最小。也可能没有鞍点。有15个数按由大到小顺序存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。如果该数不在数组中,则输出“无......