【数据结构初阶】二、顺序表的实现

简介: 目录 一、线性表二、顺序表 2.1 顺序表概念及结构2.2 顺序表接口实现2.2.1 顺序表初始化2.2.2 顺序表的销毁2.2.3 顺序表的打印2.2.4 顺序表增加数据(插入,头插、尾插)2.2.5 顺序表删除数据(删除,头删、尾删)2.2.6 顺序表查找数据2.2.7 顺序表修改数据三、顺序表完整代码(C语言)3.1 SeqList.h3.2 SeqList.c3.3 Test.c 四、顺序表的优缺点

目录

一、线性表

二、顺序表

2.1 顺序表概念及结构

2.2 顺序表接口实现

2.2.1 顺序表初始化

2.2.2 顺序表的销毁

2.2.3 顺序表的打印

2.2.4 顺序表增加数据(插入,头插、尾插)

2.2.5 顺序表删除数据(删除,头删、尾删)

2.2.6 顺序表查找数据

2.2.7 顺序表修改数据

三、顺序表完整代码(C语言)

3.1 SeqList.h

3.2 SeqList.c

3.3 Test.c

四、顺序表的优缺点


一、线性表

在学习顺序表和链表之前,先要了解什么是线性表:

       线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

文章所讲述的顺序表和下一篇链表便是线性表的一种。

       线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

例如:顺序表和链表

image.png

顺序表在逻辑结构上是连续的,物理结构上也是连续的;

链表在逻辑结构上是连续的,但是在物理结构上不是连续的

-------------------我是分割线------------------  

二、顺序表

2.1 顺序表概念及结构

       顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

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

(1)静态顺序表:使用定长数组存储元素。

#define N 100//对空间大小进行了限定typedefintSLDataType;便于修改存储的数据类型typedefstructSeqList{
SLDataTypearr[N];//定义需要的数组长度intsize;//记录数组的有效个数(已使用)}SL;
//缺陷:空间给小了不够用,给大了可能浪费,不实用

image.png

(2)动态顺序表:使用动态开辟的数组存储

typedefintSLDataType;//便于修改存储的数据类型typedefstructSeqList{
SLDataType*arr;//指向动态开辟的数组,用于存放数据。空间不够则增容intsize;//数组的个数intcapacity;//容量大小}SL;


2.2 顺序表接口实现

首先,我们要创建一个顺序表类型,该顺序表类型包括了顺序表的起始位置、记录顺序表内已有元素个数的计数器(size),以及记录当前顺序表的容量的变量(capacity),这里实现的是动态顺序表

typedefintSLDataType;//便于修改存储的数据类型//顺序表的动态储存typedefstructSeqList{
SLDataType*arr;//指向动态开辟的数组,用于存放数据intsize;//数组的个数intcapacity;//容量大小}SL;

image.png

2.2.1 顺序表初始化

//初始化顺序表voidSeqListInit(SL*ps)
{
ps->arr=NULL;
ps->size=0;
ps->capacity=0;
}

2.2.2 顺序表的销毁

顺序表所用的内存空间是动态开辟在堆区的,所以我们在使用完后需要及时对其进行释放,避免造成内存泄漏

//销毁顺序表
void SeqListDestroy(SL* ps)
{
  assert(ps);
  if (ps->size)
  {
    free(ps->arr);
    ps->arr = NULL;
    ps->size = 0;
    ps->capacity = 0;
  }
}

2.2.3 顺序表的打印

遍历即可

//打印voidSeqListPrint(SL*ps)
{
assert(ps);
inti=0;
for (i=0; i<ps->size; i++)
    {
printf("%d ", ps->arr[i]);
    }
printf("\n");
}

2.2.4 顺序表增加数据(插入,头插、尾插)

       每次需要增加数据的时候,首先都应该先检查顺序表内元素个数是否已达顺序表容量上限。若已达上限,那么我们就需要先对顺序表进行扩容,然后才能增加数据。

(1)插入:就是从两个数据中间直接直接插入一个新的数据(时间复杂度为O(n))

image.png

//插入voidSeqListInsert(SL*ps, intpos, SLDataTypen)
{
assert(ps);
assert(pos>=0&&pos<=ps->size);
CheckCapacity(ps);//检查顺序表容量,不够则开辟新空间//挪动数据intend=ps->size-1;
while (end>=pos);
    {
ps->arr[end+1] =ps->arr[end];
end--;
    }
ps->arr[pos] =n;
ps->size++;
}
//检查顺序表容量voidCheckCapacity(SL*ps)
{
assert(ps);
//进行扩容,两种情况,1、没有开辟空间  2、空间不足if (ps->size==ps->capacity)
    {
intNewCapacity= (ps->capacity==0?4 : ps->capacity*2);
SLDataType*tmp= (SLDataType*)realloc(ps->arr, NewCapacity*sizeof(SLDataType));
if (tmp==NULL)
        {
printf("realloc fail\n");
//return;exit(-1);
        }
ps->arr=tmp;
ps->capacity=NewCapacity;
    }
}

(2)头插:顾名思义就是从头部插入一个新的数据(时间复杂度为O(n))

image.png

这里有两种写法,推荐可以直接复用插入的代码

//顺序表的头插voidSeqListPushFront(SL*ps, SLDataTypen)
{
/*assert(ps);CheckCapacity(ps);int end = ps->size - 1;while (end >= 0){ps->arr[end + 1] = ps->arr[end];end--;}ps->arr[0] = n;ps->size++;*///两种都行,下面这种比较方便SeqListInsert(ps, 0, n);
}

(3)尾插:这个比较简单,直接从尾上插入即可(时间复杂度为O(1))

image.png

这里也是有两种写法,推荐可以直接复用插入的代码

//尾插voidSeqListPushBack(SL*ps, SLDataTypen)
{
/*assert(ps);CheckCapacity(ps);ps->arr[ps->size] = n;ps->size++;*///两种都行,下面这种比较方便SeqListInsert(ps, ps->size, n);
}

2.2.5 顺序表删除数据(删除,头删、尾删)

删除数据,其实可以理解为:从某个位置开始,数据依次向前覆盖。这样一来,该位置的数据就相当于删除了。

(1)删除: 直接删除 pos 位置的值,向前覆盖(时间复度为O(n))

image.png

//删除数据voidSeqListErase(SL*ps, intpos)
{
assert(ps);
assert(pos>=0&&pos<ps->size);
intbegin=pos;
while (begin<ps->size-1)
    {
ps->arr[begin] =ps->arr[begin+1];
begin++;
    }
ps->size--;
}

(2)头删(时间复杂度为O(n))

image.png

这里也是有两种写法,这里也是有两种写法,推荐可以直接复用删除的代码

//头删, O(n)voidSeqListPopFront(SL*ps)
{
/*assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->arr[begin - 1] = ps->arr[begin];begin++;}ps->size--;*///两种都行,下面这种比较方便SeqListErase(ps, 0);
}

(3)尾删(时间复杂度为O(1))

image.png

这里也是有两种写法,推荐可以直接复用删除的代码

//尾删voidSeqListPopBack(SL*ps)
{
/*assert(ps);assert(ps->size > 0);ps->size--;*///两种都行,下面这种比较方便SeqListErase(ps, ps->size-1);
}

2.2.6 顺序表查找数据

查找数据也相对简单,直接遍历一次顺序表即可,若找到了目标数据,则停止遍历,并返回该数据的下标

//查找数据intSeqListFind(SL*ps, SLDataTypen)
{
assert(ps);
inti=0;
for (i=0; i<ps->size; i++)
    {
if (ps->arr[i] ==n)
        {
returnn;//找到了        }
    }
return-1; //找不到}

2.2.7 顺序表修改数据

修改数据,就直接对该位置的数据进行再次赋值即可。

//修改数据voidSeqListModify(SL*ps, intpos, SLDataTypen)
{
assert(ps);
assert(pos>=0&&pos<ps->size);
ps->arr[pos] =n;
}

-------------------我是分割线------------------

三、顺序表完整代码(C语言)

分三个文件写

  1. SeqList.h(类型定义、接口函数声明、引用的头文件)
  1. SeqList.c(接口函数的实现)
  2. Test.c(主函数、测试顺序表各个接口功能)

3.1 SeqList.h

#pragma once//防止头文件被二次引用//声明#define _CRT_SECURE_NO_WARNINGS//vs编译器需要,可自行删去#include <stdio.h>#include <stdlib.h>#include <assert.h>typedefintSLDataType;//便于修改存储的数据类型//顺序表的动态储存typedefstructSeqList{
SLDataType*arr;//指向动态开辟的数组,用于存放数据intsize;//数组的个数intcapacity;//容量大小}SL;
//初始化顺序表voidSeqListInit(SL*ps);
//打印voidSeqListPrint(SL*ps);
//检查顺序表容量voidCheckCapacity(SL*ps);
//销毁顺序表voidSeqListDestroy(SL*ps);
//增//顺序表的头插,O(n)voidSeqListPushFront(SL*ps, SLDataTypen);
//尾插,O(1)voidSeqListPushBack(SL*ps, SLDataTypen);
//插入数据voidSeqListInsert(SL*ps, intpos, SLDataTypen);
//删//头删, O(n)voidSeqListPopFront(SL*ps);
//尾删, O(1)voidSeqListPopBack(SL*ps);
//删除数据voidSeqListErase(SL*ps, intpos);
//查找数据intSeqListFind(SL*ps, SLDataTypen);
//修改数据voidSeqListModify(SL*ps, intpos, SLDataTypen);

3.2 SeqList.c

//函数接口实现#include "SeqList.h"//打印voidSeqListPrint(SL*ps)
{
assert(ps);
inti=0;
for (i=0; i<ps->size; i++)
    {
printf("%d ", ps->arr[i]);
    }
printf("\n");
}
//初始化顺序表voidSeqListInit(SL*ps)
{
ps->arr=NULL;
ps->size=0;
ps->capacity=0;
}
//检查顺序表容量voidCheckCapacity(SL*ps)
{
assert(ps);
//进行扩容,两种情况,1、没有开辟空间  2、空间不足if (ps->size==ps->capacity)
    {
intNewCapacity= (ps->capacity==0?4 : ps->capacity*2);
SLDataType*tmp= (SLDataType*)realloc(ps->arr, NewCapacity*sizeof(SLDataType));
if (tmp==NULL)
        {
printf("realloc fail\n");
//return;exit(-1);
        }
ps->arr=tmp;
ps->capacity=NewCapacity;
    }
}
//销毁顺序表voidSeqListDestroy(SL*ps)
{
assert(ps);
if (ps->size)
    {
free(ps->arr);
ps->arr=NULL;
ps->size=0;
ps->capacity=0;
    }
}
//顺序表的头插voidSeqListPushFront(SL*ps, SLDataTypen)
{
/*assert(ps);CheckCapacity(ps);int end = ps->size - 1;while (end >= 0){ps->arr[end + 1] = ps->arr[end];end--;}ps->arr[0] = n;ps->size++;*///两种都行,下面这种比较方便SeqListInsert(ps, 0, n);
}
//尾插voidSeqListPushBack(SL*ps, SLDataTypen)
{
/*assert(ps);CheckCapacity(ps);ps->arr[ps->size] = n;ps->size++;*///两种都行,下面这种比较方便SeqListInsert(ps, ps->size, n);
}
//插入voidSeqListInsert(SL*ps, intpos, SLDataTypen)
{
assert(ps);
assert(pos>=0&&pos<=ps->size);
CheckCapacity(ps);//检查顺序表容量,不够则开辟新空间//挪动数据intend=ps->size-1;
while (end>=pos);
    {
ps->arr[end+1] =ps->arr[end];
end--;
    }
ps->arr[pos] =n;
ps->size++;
}
//头删, O(n)voidSeqListPopFront(SL*ps)
{
/*assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->arr[begin - 1] = ps->arr[begin];begin++;}ps->size--;*///两种都行,下面这种比较方便SeqListErase(ps, 0);
}
//尾删voidSeqListPopBack(SL*ps)
{
/*assert(ps);assert(ps->size > 0);ps->size--;*///两种都行,下面这种比较方便SeqListErase(ps, ps->size-1);
}
//删除数据voidSeqListErase(SL*ps, intpos)
{
assert(ps);
assert(pos>=0&&pos<ps->size);
intbegin=pos;
while (begin<ps->size-1)
    {
ps->arr[begin] =ps->arr[begin+1];
begin++;
    }
ps->size--;
}
//查找数据intSeqListFind(SL*ps, SLDataTypen)
{
assert(ps);
inti=0;
for (i=0; i<ps->size; i++)
    {
if (ps->arr[i] ==n)
        {
returnn;//找到了        }
    }
return-1; //找不到}
//修改数据voidSeqListModify(SL*ps, intpos, SLDataTypen)
{
assert(ps);
assert(pos>=0&&pos<ps->size);
ps->arr[pos] =n;
}

3.3 Test.c

//测试函数#include "SeqList.h"voidSeqListTest1()
{
SLs1;//定义一个结构体SeqListInit(&s1);//初始化SeqListPushFront(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPrint(&s1);
SeqListPopFront(&s1);
SeqListPrint(&s1);
SeqListDestroy(&s1);
}
voidSeqListTest2()
{
SLs1;
SeqListInit(&s1);
SeqListPushFront(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushFront(&s1, 0);
SeqListPushBack(&s1, 4);
SeqListPrint(&s1);
SeqListPopBack(&s1);
SeqListPrint(&s1);
SeqListDestroy(&s1);
}
voidSeqListTest3()
{
SLs1;
SeqListInit(&s1);
SeqListPushFront(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPrint(&s1);
intfind=SeqListFind(&s1, 1);
if (find==-1)
printf("找不到\n");
elseprintf("%d", find);
}
voidSeqListTest4()
{
SLs1;
SeqListInit(&s1);
SeqListPushFront(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListInsert(&s1, 2, 3);
SeqListPrint(&s1);
}
intmain()
{
SeqListTest1();
return0;
}

四、顺序表的优缺点

(1)优点

  • 空间连续,可以按下标进行随机访问
  • 顺序表的 cpu 高速缓存命中率高

(2)缺点

  • 空间不够需要增容,增容有一定的性能消耗,且可能存在一定的空间浪费
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
  • 中间或头部的插入、删除,需要挪动数据,效率低,时间复杂度为O(N)

-------------------我是分割线------------------

文章就到这里

相关文章
|
2月前
|
存储 编译器 C语言
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
52 2
|
28天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
28天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
59 3
|
28天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
2月前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
31 6
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
25 3
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
23 2
|
2月前
|
存储
数据结构(顺序表)
数据结构(顺序表)
27 0
|
2月前
|
存储 算法
【数据结构】新篇章 -- 顺序表
【数据结构】新篇章 -- 顺序表
15 0
|
2月前
|
存储 测试技术
探索数据结构:顺序表的实现与应用
探索数据结构:顺序表的实现与应用