数据结构之顺序存储与链式存储

定义

线性表,全名为线性存储结构。

线性表是n个数据特性相同的元素的组成有限序列,是最基本且常用的一种线性结构(线性表,栈,队列,串和数组都是线性结构),同时也是其他数据结构的基础。具有“一对一”逻辑关系的数据,最佳的存储方式是使用线性表。

特点

  • 是一个有限的序列
  • 可以是有序的也可以是无序的。
  • 线性表的开始元素没有前驱元素只有后继元素,线性表的结束元素没有后继元素只有前驱元素,除了开头元素和结尾元素以外,每个元素都有且只有一个前驱元素和后继元素。

前驱和后继

数据结构中,一组数据中的每个个体被称为“数据元素”(简称“元素”)。

  • 某一元素的左侧相邻元素称为“直接前驱”,位于此元素左侧的所有元素都统称为“前驱元素”;
  • 某一元素的右侧相邻元素称为“直接后继”,位于此元素右侧的所有元素都统称为“后继元素”;

以下图数据中的元素 3 来说,它的直接前驱是 2 ,此元素的前驱元素有 2 个,分别是 1 和 2;同理,此元素的直接后继是 4 ,后继元素也有 2 个,分别是 4 和 5。
在这里插入图片描述

存储结构

线性表存储结构分为顺序存储结构链式存储结构,前者称为顺序表,后者称为链表。

顺序存储结构

定义

顺序表就是把线性表中的所有元素按照某种逻辑顺序,依次存储到从指定位置开始的一块连续的存储空间。

特点

逻辑上相邻的数据元素,物理次序也是相邻的。

只要确定好了存储线性表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的储存结构,因为高级语言中的数组类型也是有随机存取的特性,所以通常我们都使用数组来描述数据结构中的顺序储存结构,用动态分配的一维数组表示线性表。

**数组长度和线性表的长度区别:**数组长度是存放线性表的存储空间的长度,存储分配后这个量一般是不变的,线性表的长度是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的。

优缺点
优点缺点
无需为表示表中元素之间的逻辑关系而增加额外的存储空间插入和删除操作需要移动大量元素
可快速的存取表中的任一位置元素当线性表长度变化比较大时,难以确定存储空间的容量
容易造成存储空间碎片
基本操作

结构体定义

顺序表可以分为静态分配和动态分配两种:

  • 静态分配
#define MaxSize 50  / /顺序表的最大长度
typedef struct{		// typedef类型重命名
    ElemType data[MaxSize]; // 顺序表的元素,ElemType是未明确的数据类型,使用时可以用typedef定义具体类型
    int length;				// 长度
}SqList;

静态分配方法需要预先分配一段固定大小的连续空间,但在运算过程中,进行插入,合并等操作时,容易超过预分配的空间长度,出现溢出问题。

  • 动态分配
#define InitSize 50  // 顺序表的初始长度
typedef struct{		// typedef类型重命名
    ElemType *data; // 指向动态分配数组的指针,基地址,*取内容
    int MaxSize;	// 数组的最大容量
    int length;	// 数组的当前个数
}SeqList;
// 定义SqList类型的变量
SqList L;
L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);

初始化

struct SqList{
	int data[MaxSize]; // 存放顺序表的元素
	int length; // 存放顺序表的长度
	int last;
};

void Init(SqList *L){
	L->data[0] = int(malloc(MaxSize * sizeof(int)));
	int n, i = 0;
	cin >> n;
	L->length = n;
	while (i < n)
	{
		cin >>(L->data[i++]);
	}
	L->last = i - 1;
}

插入

bool InsertList(SqList &L, int i, int e)
{
	// 在顺序表中第i个位置(位序,不是下标)插入数值e
	int j;
	if (i<1 || i>L.length+1) // 是否越界
		return false;
	if (L.length == MaxSize)// 内存已满
		return false;
	L.length++;            // 长度++
	for (j = L.length; j >= i; j--) // 将第i个起的数据后移
		L.data[j] = L.data[j - 1];
	L.data[j] = e;
	return true;
}

删除

bool DeleteList(SqList&L, int i)
{
	// 删除顺序表中第i个元素
	int j;
	if (i<1 || i>L.length)
		return false;   // 判断越界
	for (j = i; j < L.length; j++)
		L.data[j - 1] = L.data[j];
	L.length--;
	return true;
}

查找

bool LocateElem(SqList L, int e)
{
	int i;
    for(i = 0; i < L.length; i++){
        if(L.data[i] == e){
            return i+1;
        }
    }
    return 0;
}

链式存储结构

又称为链表,用于存储逻辑关系为 “一对一” 的数据。

用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的),包括数据域和指针域,数据域存数据,指针域指示其后继的信息。

1、单链表

节点

一个完整的链表需要由以下几部分构成:

  1. **头指针:**一个普通的指针,它的特点是永远指向链表第一个节点的位置。很明显,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据;

  2. 节点:链表中的节点又细分为头节点、首元节点和其他节点:

    • **头节点:**其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
    • **首元节点:**由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
    • **其他节点:**链表中其他的节点;

在这里插入图片描述

链表中有头节点时,头指针指向头节点;反之,若链表中没有头节点,则头指针指向首元节点。

基本操作

结构体定义

typedef struct Lnode{	
	ElemType data; 
	struct Lnode *next;
}LNode,*LinkList

初始化

单链表的初始化是指构建一个空表。先创建一个空结点,不存放数据,然后令其指针域为空;

bool InitList_L(LinkList &L){
	L = new Lnode; 
	if(!L) return false;
	L -> next = NULL;
	return true;
}

创建

创建单链表分为头插法和尾插法两种。

  • 头插法:是指每次将新结点插入到头结点之后,其创建的单链表和数据输入的顺序正好相反,因此也称为逆序建表。
void CreateList_H(LinkList &L){
	int n;
	LinkList s;
	L = new LNode;
	L -> next = NULL;
	cin>>n;
	while(n--){
		s = new LNode;
		cin>>s->data;
		s->next = L->next;
		L->next = s;
	}
}
  • 尾插法:是指每次把新结点链接到链表的尾部,其创建的单链表和数据输入顺序一致,因此也称为正序建表。
void CreatList_R(LinkList &L){	
	int n;
    LinkList s, r;
    L = new Lnode;
    L -> next = NULL;
    r = L;
    cin>>n;
    while(n--){
		s = new LNode; 
        cin >> s->data;
        s -> next = NULL; 
        r -> next = s;
        r = s; 
    }
}

按序取值
从单链表的第一个结点出发,顺指针逐个往下搜索,知道找到第i个结点为止,否则返回最后一个结点的指针NULL。

LinkList GetElem(LinkList L, int i){
	int j = 1; 
    LinkList p = L -> next;
    if(i == 0) return L;
    if(i < 1) return NULL;
    while(p && j<i){
        p = p->next;
        j++;
    }
    return p;
}

插入结点

p = GetElem(L, i-1);
s -> next = p -> next;
p -> next = s;

删除结点

p = GetElem(L, i-1);
q = p -> next;
p -> next = q -> next;

2、静态链表

静态链表,兼顾了顺序表和链表的优点于一身。

使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。

3、双向链表

在单链表的基础上,再在每个结点中设置一个指向其前驱结点的指针域,这样一个结点既可以指向它的前面又可以指向它的下一个,我们把这种链表称为双向链表。

4、循环链表

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。

顺序存储和链式存储比较

  • 顺序表只需知道第一个元素的位置,就可以通过起始位置的偏移去获取顺序表中的任何元素(随机访问),而链表的存储结构是一个元素中包含下一个数据元素的位置信息,下一个包含下下一个,也就是每个数据元素之间都是单线联系的,你要想知道最后一个元素在哪里,你必须从头走到尾才可以,所以链表是不支持随机访问的。
  • 顺序表中的数据元素是存放在一段地址连续的空间中,且这个存储空间是提前分配的,一旦分配好了,在对其进行操作的过程中是不会更改的。而链表支持存储空间动态分配。
  • 顺序表在插入删除一个元素的时候需要移动大量元素。而链表在插入和删除一个元素时,不需要移动大量元素,只需要更改插入位置的指针指向就可以。

本篇完

  • 43
    点赞
  • 231
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
(1)用带表头的链表存放输入的数据,每读入一个数,按升序顺序插入到链表中,链表中允许两个结点有相同值。链表的头结点存放链表后面的结点个数,初始化时就生成头结点(初值为0)。链表翻转是把数据逆序(变成降序),注意,头结点不动。翻转后要再翻转一次,恢复升序后才能插入新元素,否则会出错。 (2)先定义堆栈的几个基本操作,再设计一主函数利用堆的操作完成以下功能:假设一个算术表达式中可以包含三种括号:()[]{},且这三种括号可以按任意次序嵌套使用(如:...[...{...}...[...]...]...(...))。编写判别给定表达式中所含括号是否正确配对出现的算法,已知表达式已存入数据元素为字符的单链表中。 (3)先定义队列的几个基本操作,再设计一主函数利用队列的操作完成以下功能:键盘输入的字符可以临时存入键盘的缓冲区中。为了充分利用缓冲区的空间,往往将缓冲区设计成链式循环队列的结构,并为循环队列结构的缓冲区设置一个队首指针和一个队尾指针。每输入一个字符到缓冲区中,就将尾指针后移,链入缓冲区的循环队列之中;每输出一个字符号,就将队头指针前移,将它从缓冲队列中删除。假设有两个进程同时存在于一个应用程序中,第一个进程连续在屏幕上显示字符“X”,第二个进程不断检查键盘上是否有输入,若有则读入用户键入的字符,将其保存到键盘缓冲区中。
数据结构中的顺序存储链式存储是两种常见的存储方式。顺序存储结构是指将数据元素按照其逻辑顺序依次存放在一片连续的内存空间中,通过元素的下标来访问数据。而链式存储结构则是通过节点之间的指针来建立数据元素之间的关系,每个节点包含数据和指向下一个节点的指针。 顺序存储结构的特点是可以随机访问元素,插入和删除元素的操作相对于链式存储结构来说比较复杂,因为需要移动其后面的元素。而链式存储结构的特点是插入和删除元素的操作相对较简单,但是访问元素需要通过遍历指针来查找。 顺序存储结构的优点是可以快速访问元素,适用于元素的数量已知且不会频繁变动的情况。而链式存储结构的优点是可以动态地插入和删除元素,适用于元素的数量未知或需要频繁变动的情况。 在比较顺序存储链式存储时,需要考虑到数据的访问效率和存储空间的利用率。顺序存储适用于对数据的随机访问较多的情况,而链式存储适用于对数据的插入和删除操作较多的情况。因此,选择使用哪种存储方式要根据具体的应用场景来决定。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [数据结构顺序存储链式存储](https://blog.csdn.net/HQ_LIN/article/details/110090228)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [顺序存储链式存储](https://blog.csdn.net/dreamwbt/article/details/53220125)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wahkim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值