数据结构-顺序表(c语言)
- 1.线性表
- 2.顺序表
-
- 2.1概念与结构
- 2.2分类
-
- [2.2.1 静态顺序表](#2.2.1 静态顺序表)
- 2.2.2动态顺序表(按需申请)
1.线性表
线性表是n个具有相同特性的数据元素的有限序列。线性表是一种实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1概念与结构
概念:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。(顺序表底层结构是数组)
顺序表和数组有啥区别?
顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。
需要具备一些C语言的知识:结构体、指针(一级、二级)、结构体指针、内存动态管理
2.2分类
2.2.1 静态顺序表
首先需要创建三个文件。头文件就相当一个目录一样,可以快速找到。
空间是固定的:
c
#pragma once
#include<stdio.h>
#define N 1000
typedef int SLDataType;
//静态顺序表
typedef struct SeqList
{
//顺序表不一定是整型的,
// 还能是其他任意一种类型,
// 所以要将数组变为可变化的类型
SLDataType arr[N];//只需要修改一处
int size;//表示有效数据个数
}SL;//SL表示结构体
//还可以下面这种方式,都是可以的
//typedef struct SeqList SL;
N是空间大小,arr是定义数组,size有效数据个数。
静态顺序表的缺陷:空间给少了不够用(用户流失),给多了造成空间浪费。
2.2.2动态顺序表(按需申请)
需要注意的是:
传值:形参是实参的拷贝
传址:形参的改变会影响实参
①顺序表的初始化:
.h:
c
typedef struct SeqList
{
SLDataType* arr;
int size;//有效个数
int capacity;//容量大小
}SL;
//顺序表的初始化
void SLInit(SL* s);
.c:
c
#include"SeqList.h"
void SLInit(SL* s)//形参
{
s->arr = 0;
s->size = s->capacity = 0;
}
test.c
c
#include"SeqList.h"
void SLTest()
{
SL s1;
SLInit(&s1);//实参
}
int main()
{
SLTest();
return 0;
}
②尾插
还需要使用动态内存申请函数
realloc:
连续的位置空间不够:
1)找到新的连续空间
2)拷贝旧数据
3)释放旧空间
如何增容呢?
一次增加一个增容频繁,会导致程序效率低下。
那么就一次多来点,增加大空间(空间浪费),这两种都不行,那么就取中,增容按倍数增加,如,2倍...
要插入的数据量跟增量是正相关的,这样空间浪费比较少。既能避免频繁增容,又能保证最大程度的减少空间的浪费。
头文件:
c
//尾插
void SLPushBack(SL* ps, SLDataType x);
//x是要插入的数据
.c
c
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
//温柔的方式
if (ps == NULL)
{
return;
}
//粗暴-
//空间不够,申请空间
//为真-通过,为假-报错
assert(ps != NULL);
if (ps->capacity == ps->size)
{
//防止capacity为0
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//空间不够,2倍增容
SLDataType* tmp = (SLDataType)realloc(ps->arr,newCapacity * sizeof(SLDataType));//重新定义一个tmp为了防止增容失败
//第二部分的单位是字节。
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr==tmp;
ps->capacity = newCapacity;
}
ps->arr[ps->size++] = x;
}
test.c
c
#include"SeqList.h"
void SLTest()
{
SL s1;
SLInit(&s1);//实参
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
}
int main()
{
SLTest();
return 0;
}
③头插
空间足够才能插,不够的话就增容,那么就可以将增容再分装成一个函数,可以直接使用。
将表中的数据整体向后挪动一位。
.h
c
//头插
void SLPushFront(SL* ps, SLDataType x);
.c
c
//头插
void SLPushFront(SL* ps, SLDataType x)
{
//不能为空
assert(ps);//等价于暗示sertraline(ps!=NULL)
SLcheckCapacity(ps);
//直接头插
//将数据向后挪动一位
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] == ps->arr[i - 1];
}
++ps->size;
}
test.c
c
#include"SeqList.h"
void SLTest()
{
SL s1;
SLInit(&s1);//实参
//SLPushBack(&s1, 1);
//SLPushBack(&s1, 2);
//SLPushBack(&s1, 3);
//SLPushBack(&s1, 4);
//SLPushBack(&s1, 5);
//SLPushBack(NULL, 1);//用户的一些错误操作
SLPushFront(&s1, 1);
}
int main()
{
SLTest();
return 0;
}
④尾删
.h
c
//尾删
void SLPopBack(SL* ps);
.c
c
//尾删
void SLPopBack(SL* ps)
{
assert(ps && ps->size);//不能为空
--ps->size;
//size是用来记录数组中有效元素个数的。
//当前有效个数不再是4,而是3个
}
test.c
c
#include"SeqList.h"
void SLTest()
{
SL sl;
SLInit(&sl);
SLPushFront(&sl, 2);
//SLPushFront(&sl, 3);
SLPopBack(&sl);
SLPrint(&sl);
}
int main()
{
SLTest();
return 0;
}
⑤头删
.h
c
void SLPopFront(SL* ps);
.c
c
//头删
void SLPopFront(SL* ps)
{
assert(ps && ps->size);
for (int i = 0;i<ps->size-1; i++)
{
ps->arr[i] == ps->arr[i + 1];
}
--ps->size;
}
test.c
c
void SLTest()
{
SL sl;
SLInit(&sl);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPopBack(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
}
int main()
{
SLTest();
return 0;
}
⑥查找
.h
c
//查找
int SLFind(SL* ps, SLDataType x);
.c
c
//查找x,查找到了就返回对应数据的下标
int SLFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
//找到了,返回下标
return i;
}
}
//没找到,返回无效下标
return -1;
}
test.c
c
void Test02()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
int ret = SLFind(&sl, 3);
if (ret < 0)
{
printf("未找到!\n");
}
else
{
printf("找到了!\n");
}
}
int main()
{
//SLTest();
Test02();
return 0;
}
⑦在指定位置插入或删除
pos及之后的数据整体向后挪动一位。
.h
c
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//指定位置之前删除数据
void SLErase(SL* ps, int pos);
.c
c
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)//在pos位置之前插入x
{
assert(ps);
//前等:头插
//后等:尾插
assert(pos >= 0 && pos <= ps->size);
SLcheckCapacity(ps);
//指定位置插
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
++ps->size;
}
//指定位置删除数据
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos>=0&&pos<=ps->size);//不能等于
for (int i = pos;i < ps->size-1; i++)
{
ps->arr[i] == ps->arr[i + 1];
}
--ps->size;
}
test.c
c
#include"SeqList.h"
void SLTest()
{
SL sl;
SLInit(&sl);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPopBack(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
}
void Test02()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLInsert(&sl, 0, 99);//99 1 2 3 4
SLPrint(&sl);
SLInsert(&sl, 4, 88);//99 1 2 3 88 4
SLPrint(&sl);
SLErase(&sl,3);
SLPrint(&sl);
/*int ret = SLFind(&sl, 3);
if (ret < 0)
{
printf("未找到!\n");
}
else
{
printf("找到了!\n");
}*/
}
int main()
{
//SLTest();
Test02();
return 0;
}
⑧销毁
c
//销毁
void SLDesTroy(SL* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
编写代码过程中要勤测试,避免写出大量代码后再测试而导致出现问题,问题定位无从下手。