今天开始新的篇章-顺序表,开始上难度了,但是多看多想多练一定会掌握的。
1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改。
顺序表又分为两类:
2.1.1静态顺序表
静态顺序表:使用定长数组存储元素。
使用静态顺序表,开辟多少用多少,不够灵活。
2.1.2动态顺序表
动态顺序表:使用动态开辟的数组存储。
动态顺序表开辟的空间不够时可以扩容,但也存在浪费的情况,也就是新开辟的的空间过剩。
2.2接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空
间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间
大小,所以下面我们实现动态顺序表。
动态顺序表的实现,我们在创建项目时分类便于管理和检查代码,创建一个.h文件用来声明,两个.c ,一个.c用来定义,一个.c用来测试。
头文件里包含需要使用的相关函数,后面定义测试代码使用时只需要包含头文件即可。
2.2.1创建顺序表
注:结构体的命名,数据的命名要有意义。
2.2.2增删查改
先在头文件(SeqList.h)里声明。
再在源文件中(SeqList.c)中定义。
- 增
2. 删
3.查
原地扩容和异地扩容是两种不同的策略,用于对数据结构进行扩容操作。
原地扩容是指在原有的内存空间上进行扩容。当需要对数据结构进行扩容时,原地扩容会尝试在当前内存空间的基础上进行扩展,如果内存空间足够,则直接在原地进行扩容,将数据拷贝到新的空间中。这种方式可以避免频繁申请和释放内存空间,节省了内存管理的开销。但如果原有内存空间不足以容纳扩容后的数据,则无法进行原地扩容,需要采取其他策略。
异地扩容是指在内存空间不足时,需要申请新的内存空间进行扩容。当需要对数据结构进行扩容时,如果原有内存空间不足以容纳扩容后的数据,则需要申请新的内存空间,并将原来的数据拷贝到新的空间中。这种方式可以保证数据的完整性,并且能够灵活地进行扩容。但相应地,会增加内存管理的开销,并且需要额外的拷贝操作。
4.改
尾插:
尾删:
头插:
头删:
在指定位置插入:
在指定位置删除
3 顺序表的问题
问题:
1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
在下一篇链表的文章中会发现,链表能很好地解决上面的问题。
顺序表的就讲解到这了,后面也会写一些有关顺序表的相关习题来增加理解,本文如有不对不妥之处,欢迎大家留言!!最后给大家附上完整代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
// 静态顺序表
//#define N 1000
//typedef int SLDataType;
//
//struct SeqList
//{
// SLDataType a[N];
// int size;
//};
// 动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a; //指向动态开辟的数组
int size; // 存储有效数据个数
int capacity; // 空间大小
}SL;
//管理数据增删查改
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//检查容量
void SLCheckCapacity(SL* ps);
// 尾插尾删 头插头删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
int SLFind(SL* ps, SLDataType x);
// 在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
// 删除pos位置的值
void SLErase(SL* ps, int pos);
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
//顺序表初始化
void SLInit(SL* ps)
{
ps->a = (SLDataType*)malloc(sizeof(SLDataType)*4);
//检查
if(ps->a == NULL)
{
//创建失败就打印
perror("malloc failed");
//以异常方式退出 终止程序 负数代表异常终止
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
//销毁
void SLDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//输出
void SLPrint(SL* ps)
{
for (int i = 0;i < ps->size;i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//检查容量
void SLCheckCapacity(SL* ps)
{
assert(ps);
// 满了要扩容
if (ps->size == ps->capacity)
{
//扩容使用realloc函数,realloc函数包括两种扩容方式
//原地扩容和异地扩容
SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
//查找
int SLFind(SL* ps, SLDataType x)
{
for (int i = 0;i < ps->size;i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
//检查容量
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
//SLInsert(ps, ps->size, x);
}
//尾删
void SLPopBack(SL* ps)
{
//如果不对顺序表判断,顺序表为空时,继续删除存在越界
// 温柔检查
//if (ps->size == 0)
//return;
assert(ps->size > 0);
ps->size--;
//0-(size-1)有效数据
//ps->a[ps->size - 1] = 0;这么写存在局限性
/*SLErase(ps, ps->size - 1);*/
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
//检查容量
SLCheckCapacity(ps);
//挪动数据 从后往前挪
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
/*SLInsert(ps, 0, x);*/
}
//头删
void SLPopFront(SL* ps)
{
assert(ps->size > 0);
//如果不检查依然存在越界问题
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
/*SLErase(ps, 0);*/
}
// 在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{
//检查pos合法性
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity;
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
// 删除pos位置的值
void SLErase(SL* ps, int pos)
{
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}