首页 > 其他分享 >【数据结构入门】顺序表(SeqList)详解(初始化、增、删、查、改)

【数据结构入门】顺序表(SeqList)详解(初始化、增、删、查、改)

时间:2023-02-24 23:02:07浏览次数:38  
标签:初始化 顺序 ps SeqList pos SLDataType SL 数据结构 size

顺序表我们采用将函数声明放到SeqList.h里面,函数的实现放到SeqList.c里面,test.c调用函数实现。


线性表

  • 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结 构,常见的线性表:顺序表、链表、栈、队列、字符串…
  • 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。


顺序表

1)什么是顺序表

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

2)顺序表的定义

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

  • 缺陷:给小了不够用,给大了可能浪费,非常不实用
#define N 10
typedef int SLDataType;

typedef struct SeqList
{
SLDataType a[N]; //定长数组
size_t size; //有效数据个数
}SL;


2、动态顺序表:使用动态开辟的数组存储元素

  • 动态顺序表可根据我们的需要分配空间大小
  • size 表示当前顺序表中已存放的数据个数
  • capacity 表示顺序表总共能够存放的数据个数


typedef int SLDataType; //类型重命名,后续要存储其它类型时方便更改

typedef struct SeqList
{
SLDataType* a; //指向动态开辟的数组
size_t size; //有效数据个数
size_t capacity; //容量大小
}SL;


2)顺序表的接口实现

首先新建一个工程( 博主使用的是 VS2019 )此次用的是动态顺序表

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

如图:


【数据结构入门】顺序表(SeqList)详解(初始化、增、删、查、改)_线性表


  • SeqList.h 头文件代码如下:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//动态顺序表
#define INIT_CAPACITY 4
typedef int SLDataType;
typedef struct SeqList
{

int size; //有效数据个数
int capacity; //空间容量
SLDataType* a;


}SL;


void SLInit(SL *ps); //初始化

void SLDstroy(SL *ps); //销毁

void SLPrint(SL* ps);

void SLCheckCapacity(SL* ps);

void SLPushBack(SL* ps, SLDataType x); //尾插 x为插入的数据

void SLPopBack(SL* ps);

void SLPushFront(SL* ps, SLDataType x); //头插 x为插入的数据

void SLPopFront(SL* ps);


// 顺序表查找
int SListFind(SL* ps, SLDataType x);
// 顺序表在pos位置插入x
void SListInsert(SL* ps, int pos, SLDataType x);
// 顺序表删除pos位置的值
void SListErase(SL* ps, int pos);


这里重点讲解 SeqList.c 中各个接口函数的实现

1、初始化顺序表

记得一定要加上断言,防止传进来的指针为空

void SLInit(SL *ps)
{
assert(ps);
//初始化
ps->a = (SLDataType*)malloc(sizeof(SLDataType)*INIT_CAPACITY);
if (ps->a == NULL)
{
perror("malloc fail");
return;
}
ps->capacity = INIT_CAPACITY;
ps->size = 0;

2、销毁(释放)顺序表

记得一定要加上断言,防止传进来的指针为空

void SLDstroy(SL* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL; //置空
ps->capacity = ps->size = 0; //从右往左赋值
}


3、检查顺序表容量是否满了,好进行增容

为什么不采取插一个数据,增容一个空间的方式呢,因为这样也太麻烦了,代价也太大了,一般情况下,为了避免频繁的增容,当空间满了后,我们不会一个一个的去增,而是一次增容 2 倍,当然也不会一次增容太大,比如 3 倍 4 倍,空间可能会浪费,2 倍是一个折中的选择。

void SLCheckCapacity(SL* ps)
{
assert(ps);
if (ps->capacity == ps->size)
{
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2); //用realloc 给capacity增容
if (tmp == NULL) //扩容失败怎么办
{
perror("realloc fail");
return;
}
//增容为tmp
ps->capacity *= 2;
ps->a = tmp;


3、顺序表尾插

void SLPushBack(SL* ps, SLDataType x)   //尾插 x为插入的数据  加的时候需要判断一下 size和capacity的大小
{
//SLCheckCapacity(ps);
//ps->a[ps->size++] = x;
SListInsert(ps, ps->size, x);
}


4、顺序表尾删

不知道 SLDataType 是什么类型的数据,不能冒然的将顺序表最后一个数据赋值为 0,我们只需将有效数据个数 size 减 1 即可达到尾删的目的。

void SLPopBack(SL* ps) // 尾删   原理和尾插差不多    越界问题  注意size的检查
{
////暴力检查
////assert(ps->size > 0);
////温柔的检查
//assert(ps);
//if (ps->size == 0)
//{
//
// return;
//}
//ps->a[ps->size-1] = 0;
//ps->size--;
SListErase(ps, ps->size);


}


5、顺序表头插

因为顺序表是连续存储的,所以头插时要依次挪动数据

void SLPushFront(SL* ps, SLDataType x)   //头插 x为插入的数据       
{
//assert(ps);
//SLCheckCapacity(ps);
//
//
//ps->a[ps->size++]=0;
//if (ps->size > ps->capacity)
//{
// perror("SLPushFront fail");
// return;
//}
//for (int i = ps->size - 1; i > 0; i--) //交换位置
//{
//
// int temp = 0;
// temp = ps->a[i];
// ps->a[i] = ps->a[i - 1];
// ps->a[i - 1] = temp;
//}
//ps->a[0] = x;
SListInsert(ps, 0, x);

}

6、顺序表头删

因为顺序表是连续存储的,所以头删时要依次挪动数据

void SLPopFront(SL* ps)   //头删
{
//assert(ps);
//if (ps->size == 0) //防止越界
//{

// return;
//}
////把第一个数据放到整个最后
//for (int i = 0; i < ps->size; i++)
//{
// int temp = 0;
// temp = ps->a[i];
// ps->a[i] = ps->a[i + 1];
// ps->a[i + 1] = temp;
//}
//ps->a[ps->size - 1] = 0;
//ps->size--;
SListErase(ps, 0);
}


7、打印顺序表

void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");

}

8、在顺序表中查找指定值

// 顺序表查找
int SListFind(SL* ps, SLDataType x)
{

assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;

}
}
return -1;
}


9、在顺序表指定下标位置插入数据(要注意下int与size_t间的转换问题)

原先这种写法,当顺序表为空 size = 0 时,会导致 i = -1, 执行 i >= pos 时,i 被算术转换成无符号数,而无符号数的 -1 是一个值很大的正数, 远大于 pos,满足条件进入循环,会造成越界访问 注:转换并不会改变 i 本身的值,而是执行 i >= pos 时,生成一个临时的值与 pos 比较 如果在顺序表头部插入数据 pos = 0,i 最终也会减减变成 -1,被算术转换后变成一个很大的数 总结:避免负数给到无符号数,或者避免有符号数变成负数后,被算术转换或整型提升后,变成一个很大的数 下面这样写就避免 i 变成 -1 负数了。​

// 顺序表在pos位置插入x
void SListInsert(SL* ps, int pos, SLDataType x)
{

assert(ps);
assert(pos > 0 && pos <= ps->size);
SLCheckCapacity(ps);

int i = ps->size-1;
while (i >= pos)
{
ps->a[i+1] = ps->a[i];
i--;
}
ps->a[pos] = x;
ps->size++;
}


10、在顺序表中删除指定下标位置的数据

// 顺序表删除pos位置的值
void SListErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);

if (ps->size == 0)
{
return;
}

int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}


  • 补充:

越界不一定报错,系统对越界的检查是一种抽查

  • 越界读一般是检查不出来的
  • 越界写如果是修改到标志位才会检查出来

(系统在数组末尾后设的有标志位,越界写时,恰好修改到标志位了,就会被检查出来)


12、修改指定下标位置的数据

void SListAt(SL* ps, int pos, SLDataType x)
{
assert(ps); //断言
assert(ps->size > 0); //顺序表不能为空
assert(pos >= 0 && pos < psl->size); //检查pos下标的合法性

ps->a[pos] = x; //修改pos下标处对应的数据
}


标签:初始化,顺序,ps,SeqList,pos,SLDataType,SL,数据结构,size
From: https://blog.51cto.com/u_15831056/6084409

相关文章

  • 数据结构基础—二叉树的非递归遍历和基本操作
    数据结构基础—二叉树的非递归遍历和基本操作非递归遍历先序//非递归先序遍历二叉树voidzhongxu(BiTreeT){BiTreestack[MAX];//模拟栈 BiTreenode;int......
  • C语言数据结构串的表示与操作的实现
    串的堆分配储存表示typedefstruct{ char*ch;//若是非空字符串,则按串长分配存储区,否则ch为NULL intlength;//串长度}HString;生成一个其值等于串常量的串HStr......
  • 数据结构(借鉴408)-栈与队列
    数据结构栈与队列栈给定n个元素,出栈的顺序的情形满足卡特兰数,计算公式为Cn\2n/(n+1)b=t=-1出栈前先判断栈是否为空,空栈出栈报错进栈,先top++栈顶指针top指向栈顶元......
  • jedis快速入门 String数据结构操作
    jedis一款java操作redis数据库的工具使用步骤下载jedis的jar包  使用获取连接Jedisjedis=newJedis("localhost",6379)操作je......
  • java 常见数据结构之哈希表
       ......
  • python定义类的初始化方法
    1、当类的初始化时,类中的方法__init__可以被直接定义,它在实例生成时执行,并且类中的方法与普通函数有很小的区别。2、一个类中的方法必须包含一个关键字self,也就是instance本......
  • 数据结构(借鉴408)-线性表
    数据结构逻辑结构、物理结构时间复杂度、空间复杂度线性表顺序表#defineMAX_SIZE100typedefintElemType;typedefstructseqlist{ ElemTypedata[MAX_SIZE];......
  • 【Java数据结构和算法】002-数据结构和算法概述
    目录​​一、数据结构和算法的关系​​​​二、实际编程中遇到的问题​​​​1、一段Java代码​​​​代码:​​​​问题:​​​​2、一个五子棋程序​​​​图示:​​​​问题......
  • 数据结构和算法-小甲鱼【笔记】
    数据结构和算法-小甲鱼鱼C工作室序论程序设计=数据结构+算法数据结构就是关系--数据元素相互之间存在的一种或多种特定关系的集合逻辑结构:数据对象中数据元素间......
  • 数组数据结构的使用与代码编写(二)
    数组数据结构的使用与代码编写(二)定义数组Studentstudents[]=newStudent[3];students[0]=newStudent("张三",10);students[1]=newStudent("李四",11);stud......