数据结构——顺序表

article/2025/8/22 5:16:49

        

目录

       一. 什么是顺序表?

        1,静态顺序表

        2,动态顺序表

①动态顺序表的实现及其初始化

②空间的创建

③顺序表的打印和销毁

④顺序表的尾部插入和删除

 ⑤顺序表的头部插入和删除

⑥顺序表pos位置的插入和删除

 ⑦顺序表指定元素的删除

        二,整体代码


        开始进入数据结构的篇章啦!!!!这篇文章想来介绍一下顺序表这一数据结构,它属于线性表的一种,线性表是一种线性结构——可以理解为一条连续的直线。那么什么是线性表呢? 线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等。

       一. 什么是顺序表?

        顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。这里可以看出,顺序表与我们C语言的数组是类似的,存储相同的数据在一段连续的空间,而顺序表有另一要求——要求数据连续存储的,不能跳跃间隔。

        顺序表一般可以分为:静态顺序表动态顺序表

静态顺序表是用固定长度的数组来存储元素,动态顺序表是用可以动态开辟的数组存储元素。

        1,静态顺序表

        我们以静态表为例:它需要一个指定大小的数组,但数组的每个元素并不是都需要存放,所以我们需要一个变量来记录表中元素个数,类似于我们的通讯录一般。所以这里我们就需要一个结构体变量,基本实现如下:

typedef struct staticseqlist
typedef int SLDateType;
{SLDateType arr[10];//数据的空间大小int sz;//有效数据的个数
}SL;

这里我们为了方便,重命名该结构体为SL,typedef是由于我们无法确定该数组存储的元素类型,方便修改数据。这里可以观察到,静态顺序表只适用于确定知道需要存多少数据的场景。如果静态顺序表的的数组空间开多了,就会造成浪费,开少了又不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。这里我们

        2,动态顺序表

①动态顺序表的实现及其初始化

typedef int SLDateType;
typedef struct SeqList
{SLDateType* a;//开辟空间int sz;//有效元素个数int capacity;//总大小空间
}SL;//初始化
void SLInit(SL* ps)
{ps->a = NULL;ps->sz = 0;ps->capacity = 0;
}

②空间的创建

void SLCheckcapacity(SL* ps)
{if(ps->sz == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->a = tmp;ps->capacity = newcapacity;}
}

         在初始化中我们定义的都是NULL和0值,但存储数据免不了空间的申请创建,于是我们实现一个空间开辟函数,基本逻辑是:若元素个数和空间开始都为0,就开辟一段四个SLDateType大小的空间;若元素个数已经等于表的大小,则重新开辟两倍的原空间以供使用。

③顺序表的打印和销毁

由于我们要对顺序表进行增删查改,我们需要一个打印函数来观察其元素的时刻变化,其次,动态开辟空间时内存是不会主动释放的,所以我们需要一个释放空间的函数来进行对空间进行自主释放,实现如下:

//打印顺序表
void SLprint(SL* ps)
{int i = 0;for (i = 0; i < ps->sz; i++){printf("%d ", ps->a[i]);}printf("\n");
}//程序的销毁
void SLDestroy(SL* ps)
{assert(ps);if(ps->a){ps->a = NULL;ps->sz = 0;ps->capacity = 0;}
}

④顺序表的尾部插入和删除

由于顺序表示一段连续开辟的空间,所以我们可以从它的尾部往表中插入数据,在容量未满时我们直接插入,容量满后我们开辟新的空间再插入。实现如下:

void SLPushback(SL* ps,SLDateType x)
{assert(ps);SLCheckcapacity(ps);ps->a[ps->sz] = x;ps->sz++;
}

起初我们需要检验ps是否为空,然后进行空间检查,将x的值放入开辟的空间中。

再来进行尾部删除操作,由于我们空间是连续的,在删除尾部元素时只需要将开辟的空间大小减一然后程序结束后释放开辟的空间即可

void SLPopback(SL* ps)
{assert(ps->sz > 0);ps->sz--;
}

整体的实现效果如下:

 

 ⑤顺序表的头部插入和删除

我们也可以将数据从表的头部插入,实现逻辑如下:

void SLPushFront(SL* ps,SLDateType x)
{assert(ps);SLCheckcapacity(ps);int end = ps->sz - 1;while (end>=0){ps->a[end+1] = ps->a[end];end--;}ps->a[0] = x;ps->sz++;
}

从头部删除也是同样类似的逻辑,但这里需要注意,如果sz是0的话——也就是没有元素可以删除了,我们会进行ps->sz--这一步,这样会使得sz变为-1,在我们后面的内存释放过程中遇到free函数时,就会报错,因为这时sz是-1。所以我们需要断言ps->sz是否大于0。实现如下:

void SLPopFront(SL* ps)
{assert(ps);//assert(ps->sz > 0);int begin = 1;while (begin < ps->sz){ps->a[begin-1] = ps->a[begin];begin++;}ps->sz--;
}

效果如下:

 

⑥顺序表pos位置的插入和删除

我们的pos要使用合法,即——0<=pos<=sz,因为pos可以插入在最后一个元素的末尾,所以我们是sz而不是sz-1,实现如下:

SLInsert(SL* ps, int pos , SLDateType x)
{assert(ps);assert(pos > 0);assert(pos<=ps->sz);SLCheckcapacity(ps);int end = ps->sz-1;while (end>=pos){ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = x;ps->sz++;
}

可以观察到与我们的头部插入逻辑极其相似,而当pos取0的时候就是头插,pos取sz的时候就是尾插所以我们可以改变代码如下:

//头部插入
void SLPushFront(SL* ps,SLDateType x)
{SLInsert(ps, 0, x);
}//尾部插入
void SLPushback(SL* ps,SLDateType x)
{SLInsert(ps, ps->size, x);
}

删除则与头部删除也类似,实现如下:

SLErase(SL* ps, int pos)
{assert(ps);assert(pos > 0);assert(pos <= ps->sz);int begin = pos;while (begin < ps->sz - 1){ps->a[begin] = ps->a[begin+1];begin++;}ps->sz--;
}

类似的,也可以将头部删除和尾部删除改为如下:

//尾删
void SLPopback(SL* ps)
{SLErase(ps, ps->size-1);
}//头删
void SLPopFront(SL* ps)
{SLErase(ps, 0);
}

实现效果如下: 

 

 ⑦顺序表指定元素的删除

当我们要删除指定元素的时候呢,应该如何实现,只需要遍历顺序表找到该元素,然后删除即可,创建一个查找函数,实现如下:

int SLFind(SL* ps, int pos)
{assert(ps);int i = 0;for (i = 0; i < ps->sz; i++){if (ps->a[i] == pos){return i;}}return -1;
}

效果如下:

 于此,我们便实现了整个动态顺序表的增删查改等一系列操作。

        二,整体代码

我们在这里创建一个工程

  • SeqList.h(顺序表接口函数声明和引用的头文件)
  • SeqList.c(顺序表接口函数的实现)
  • Test.c(测试)

 代码如下:

//seqlist.c
#include"Seqlist.h"//初始化顺序表
void SLInit(SL* ps)
{ps->a = NULL;ps->sz = 0;ps->capacity = 0;
}//打印顺序表
void SLprint(SL* ps)
{int i = 0;for (i = 0; i < ps->sz; i++){printf("%d ", ps->a[i]);}printf("\n");
}//程序的销毁
void SLDestroy(SL* ps)
{assert(ps);//if (ps->a != NULL)if(ps->a){free(ps->a);ps->a = NULL;ps->sz = ps->capacity = 0;}
}//检查空间
void SLCheckcapacity(SL* ps)
{if (ps->sz == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->a = tmp;ps->capacity = newcapacity;}
}//尾插
void SLPushback(SL* ps,SLDateType x)
{assert(ps);SLCheckcapacity(ps);ps->a[ps->sz] = x;ps->sz++;
}//尾删
void SLPopback(SL* ps)
{assert(ps->sz > 0);ps->sz--;
}//头插
void SLPushFront(SL* ps,SLDateType x)
{assert(ps);SLCheckcapacity(ps);int end = ps->sz - 1;while (end>=0){ps->a[end+1] = ps->a[end];end--;}ps->a[0] = x;ps->sz++;
}//头删
void SLPopFront(SL* ps)
{assert(ps);assert(ps->sz > 0);int begin = 1;while (begin < ps->sz){ps->a[begin-1] = ps->a[begin];begin++;}ps->sz--;
}//pos位置插入,删除
SLInsert(SL* ps, int pos , SLDateType x)
{assert(ps);assert(pos > 0);assert(pos<=ps->sz);SLCheckcapacity(ps);int end = ps->sz-1;while (end>=pos){ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = x;ps->sz++;
}//删除pos的数据
SLErase(SL* ps, int pos)
{assert(ps);assert(pos > 0);assert(pos <= ps->sz);int begin = pos;while (begin < ps->sz - 1){ps->a[begin] = ps->a[begin+1];begin++;}ps->sz--;
}//找到指定的数据
int SLFind(SL* ps, int pos)
{assert(ps);int i = 0;for (i = 0; i < ps->sz; i++){if (ps->a[i] == pos){return i;}}return -1;
}//seqlist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDateType;
typedef struct SeqList
{SLDateType* a;int sz;int capacity;
}SL;//初始化顺序表
void SLInit(SL* ps);//打印顺序表
void SLprint(SL* ps);//程序的销毁
void SLDestroy(SL* ps);//尾插
void SLPushback(SL* ps, SLDateType x);//尾删
void SLPopback(SL* ps);//头插
void SLPushFront(SL* ps, SLDateType x);//检查空间
void SLCheckcapacity(SL* ps);//头删
void SLPopFront(SL* ps);//特定位置插入
SLInsert(SL* ps, int pos, SLDateType x);//删除指定位置
SLErase(SL* ps, int pos);//找到指定位置
int SLFind(SL* ps, int pos);//test.c
#include"Seqlist.h"
void Test1()
{SL s1;SLInit(&s1);SLPushback(&s1, 1);SLPushback(&s1, 2);SLPushback(&s1, 3);SLPushback(&s1, 4);SLprint(&s1);SLDestroy(&s1);
}void Test2()
{SL s1;SLInit(&s1);SLPushback(&s1, 1);SLPushback(&s1, 2);SLPushback(&s1, 3);SLPushback(&s1, 4);SLPopback(&s1);SLprint(&s1);SLDestroy(&s1);
}void Test3()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPopFront(&s1);SLprint(&s1);SLDestroy(&s1);
}void Test4()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLInsert(&s1,4,100);int pos = SLFind(&s1, 3);if(pos != -1){SLInsert(&s1, pos, 100);}SLprint(&s1);SLDestroy(&s1);
}void Test5()
{SL s1;SLInit(&s1);SLPushFront(&s1, 1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);int pos = SLFind(&s1, 1);if (pos != -1){SLErase(&s1, pos);}SLprint(&s1);SLDestroy(&s1);
}int main()
{Test1();Test2();Test3();Test4();Test5();return 0;
}


http://chatgpt.dhexx.cn/article/N5XOXwMq.shtml

相关文章

【数据结构】——顺序表介绍(独家介绍,小白必看!!)

重点和易错点都用彩笔标记出来了&#xff0c;放心食用&#xff01;&#xff01; 数据结构分为线性表和非线性表&#xff0c;今天我们要学习的顺序表就是线性表中的一个小类。那么&#xff0c;何为线性表&#xff0c;线性表是指n个具有相同性质的数据元素的有限序列&#xff0c…

顺序表详解

目录 一、线性表的概念 二、顺序表 1、顺序表的概念 1&#xff09;. 静态顺序表&#xff1a;使用定长数组存储元素。 2&#xff09;. 动态顺序表&#xff1a;使用动态开辟的数组存储。 三、接口实现 1、创建 2、初始化 3、扩容 4、尾插 5、打印 6、销毁 7、尾删 越界&…

php的strstr是什么意思,php strstr函数怎么用

strstr()函数是PHP中的一个内置函数&#xff0c;语法为strstr(string,search,before_search) &#xff0c;用于搜索字符串在另一字符串中是否存在&#xff0c;如果是&#xff0c;返回该字符串及剩余部分&#xff0c;否则返回 FALSE。此函数区分大小写。 php strstr()函数怎么用…

【数据结构】--- kmp算法和strstr函数

kmp算法和strstr函数 引言一、概念分析分析原理分析 KMP算法原理基本操作图解KMP原理 三、复杂度分析四、KMP算法代码 引言 现实生活中&#xff0c;字符串匹配在很多的应用场景里都有着极其重要的作用&#xff0c;包括生物信息学、信息检索、拼写检查、语言翻译、数据压缩、网…

实现strstr函数

strstr函数的作用是寻找子字符串在目标字符串中第一次出现的位置。 #include <stdio.h> #include<stdlib.h> #include<assert.h> const char * Strstr(const char * str1, const char * str2) {assert(str1 ! NULL);assert(str2 ! NULL);char* cur str1;ch…

字符串函数剖析(3)---strstr函数

1.strstr函数的巧妙 – 查找子字符串 1.1模拟实现strstr函数 strstr函数&#xff1a;在一个字符串中查找子串 学习新函数时&#xff0c;先去c库查找该函数的相关资料&#xff0c;更加助于你的学习 const char * strstr ( const char * str1, const char * str2 );先看函数…

C语言strstr()函数用法-字符串查找

1.函数定义 strstr()函数是一个参数为两个字符指针类型&#xff0c;返回值是char*类型的函数。 用于找到子串&#xff08;str2&#xff09;在一个字符串&#xff08;str1&#xff09;中第一次出现的位置&#xff08;不包括str2的串结束符&#xff09;&#xff0c;并返回该位置…

ConcurrentHashMap 实现原理

一. ConcurrentHashMap 是什么 在并发编程中&#xff0c;ConcurrentHashMap 是一个经常被使用的数据结构&#xff0c;相比于 Hashtable 以及Collections.synchronizedMap() 来说&#xff0c;ConcurrentHashMap 在线程安全的基础上提供了更好的写并发能力&#xff0c;同时还降低…

ConcurrentHashMap 详解

前言 ConcurrentHashMap。 以下的技术点都基于JDK1.8~ 基础回顾 我们都知道&#xff0c;从JDK1.8起&#xff0c;ConcurrentHashMap底层的数据结构就已经从原来的Segment分段锁变为了数组 链表 红黑树的形态。 它是一款并发容器&#xff0c;一款装数据的容器在并发环境下铁…

ConcurrentHashMap介绍

引言 学习ConcurrentHashMap&#xff0c;合理的问题顺序应该如下&#xff1a; ConcurrentHashMap是什么&#xff08;WHAT&#xff09;为什么要有ConcurrentHashMap&#xff08;WHY&#xff09;ConcurrentHashMap的实现原理&#xff08;HOW&#xff09;ConcurrentHashMap如何使…

一文彻底弄懂ConcurrentHashMap

一文彻底弄懂ConcurrentHashMap 导读前言锁synchronizedvolatile&#xff08;非锁&#xff09;自旋锁分段锁ReentrantLockCAS ConcurrentHashMap 实现原理JDK1.7 中的 ConcurrentHashMapSegmentConcurrentHashMap 并发读写的几种情况get 方法put 方法 JDK1.8 中的 ConcurrentHa…

ConcurrentHashMap杂谈

为什么使用ConcurrentHashMap 在并发编程中使用HashMap可能导致程序死循环&#xff0c;而使用线程安全的HashTable效率又非常低下 线程不安全的HashMap 在多线程环境下&#xff0c;使用HashMap进行put操作会引起死循环&#xff0c;导致CPU利用率接近100% 死循环案例&#xf…

图解ConcurrentHashMap

曾经研究过jkd1.5新特性&#xff0c;其中ConcurrentHashMap就是其中之一&#xff0c;其特点&#xff1a;效率比Hashtable高&#xff0c;并发性比hashmap好。结合了两者的特点。 集合是编程中最常用的数据结构。而谈到并发&#xff0c;几乎总是离不开集合这类高级数据结构的支…

Java集合:ConcurrentHashMap

本篇内容包括&#xff1a;ConcurrentHashMap 概述、ConcurrentHashMap 底层数据结构、ConcurrentHashMap 的使用以及相关知识点。 一、ConcurrentHashMap 概述 ConcurrentHashMap 是 HashMap 的线程安全版本&#xff0c;其内部和 HashMap 一样&#xff0c;也是采用了数组 链表…

Hashtable与ConcurrentHashMap区别

ConcurrentHashMap融合了hashtable和hashmap二者的优势。 hashtable是做了同步的&#xff0c;hashmap未考虑同步。所以hashmap在单线程情况下效率较高。hashtable在的多线程情况下&#xff0c;同步操作能保证程序执行的正确性。 但是hashtable每次同步执行的时候都要锁住整个结…

ConcurrentHashMap 面试题

作者&#xff1a;程序员库森 链接&#xff1a;https://www.nowcoder.com/discuss/591527?source_idprofile_create_nctrack&channel-1 来源&#xff1a;牛客网 本文汇总了常考的 ConcurrentHashMap 面试题&#xff0c;面试 ConcurrentHashMap&#xff0c;看这一篇就够了…

Hashmap和ConcurrentHashmap的区别

HashTable &#xff08;1&#xff09;底层数组链表实现&#xff0c;无论key还是value都不能为null&#xff0c;线程安全&#xff0c;实现线程安全的方式是在修改数据时锁住整个HashTable&#xff0c;效率低&#xff0c;ConcurrentHashMap做了相关优化 &#xff08;2&#xff0…

ConcurrentHashMap的作用与用法

ConcurrentHashMap的作用与用法 一.ConcurrentHashMap简介 ConcurrentHashMap是属于JUC工具包中的并发容器之一&#xff0c;在多线程开发中很经常会使用到这个类&#xff0c;它与HashMap的区别是HashMap是线程不安全的&#xff0c;在高并发的情况下&#xff0c;使用HashMap进行…

Java并发包concurrent——ConcurrentHashMap

目录 1. ConcurrentHashMap的实现——JDK7版本 1.1 分段锁机制 1.2 ConcurrentHashMap的数据结构 1.3 ConcurrentHashMap的初始化 1.3.1 初始化ConcurrentHashMap 1.3.2 初始化Segment分段 1.4 定位Segment 1.5 ConcurrentHashMap的操作 1.5.1 get 1.5.2 put 1.5.3 …

Java8 ConcurrentHashMap详解

点个赞&#xff0c;看一看&#xff0c;好习惯&#xff01;本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录&#xff0c;这是我花了 3 个月总结的一线大厂 Java 面试总结&#xff0c;本人已拿大厂 offer。 另外&#xff0c;原创文章首发在我的个人博客&#x…