目录
前言
今天我们来讲数据结构方面的知识,数据结构是由“数据”和“结构”两词组合而来。
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。
通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的方式任意对数据进行增删改查等操作。最基础的数据结构:数组
但是最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。所以我们今天来讲数据结构中的线性表。
一、线性表

二、顺序表

三、动态顺序表的实现
代码接口:
typedef int SLDataType;
typedef struct Seqlist {
SLDataType* arr;
int size; //有效数据个数
int capacity; //数组容量
} SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//打印顺序表
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插入
void SLPushFront(SL* ps, SLDataType x);
//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部删除
void SLPopFront(SL* ps);
//尾部删除
void SLPopBack(SL* ps);
//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x);
//删除任意位置
void SLErase(SL* ps, int pos);
//查找元素
int SLFind(SL* ps, SLDataType x);
3.1 顺序表初始化和销毁
顺序表的初始化
//初始化
void SLInit(SL* ps){
ps->arr =NULL;
ps->size = ps->capacity = 0;
}
我们先把开辟的数组指针先为空指针,把数组的有效元素大小和数组大小置为空。
顺序表的销毁
void SLDestroy(SL* ps) {
assert(ps);
free(ps->arr);
ps->arr = NULL;
}
我们先用assert判断一下顺序表是否为空,释放开辟的空间,把开辟空间的起始地址置为空。
3.2 打印顺序表
如果我们想知道顺序表的内容,我们就可以打印顺序表的内容
void SLPrint(SL* ps) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
printf("%d ", ps->arr[i]);
}
printf("\n");
}
跟打印数组一样,打印个数小于有效的数据的个数(因为下标从0开始)
3.3 扩容顺序表
如果我们要对数组进行插入操作,我们就要判断顺序表空间是否已满,如果已满我们就要对顺序表进行扩容操作。
但是扩容规则有:
依次扩容一个空间 缺点:程序效率低下
依次扩容固定的空间 缺点:小了会频繁扩容,大了会空间浪费
成倍数的增加扩容空间 (一般为1.5或者2倍)
我们使用成倍数的增加扩容空间(如4,8,16,32),这样可以减少扩容次数,大了不会浪费过多空间。
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity) //判断是否已满{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//如果初始化为零,则赋值大小为4,反之扩容为以前大小的2倍
SLDataType* t = ps->arr;
t= (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));//扩容
if (t == NULL) {
perror(" SLCheckCapacity error");
exit(1);
}
ps->arr = t;//地址为扩容后新的地址
ps->capacity = newcapacity;//大小为扩容后的大小
}
}
3.4 顺序表头部插入和尾部插入
头部插入
void SLPushFront(SL* ps, SLDataType x){
assert(ps);
SLCheckCapacity(ps);//扩容
for (int i=ps->size; i >0 ; i-- ) {
ps->arr[i] = ps->arr[i-1];
}
ps->arr[0] = x;//第一个元素则为插入元素
ps->size++;//有效数据个数加1
}
从后往前移动:
尾部插入
void SLPushBack(SL* ps, SLDataType x) {
assert(ps);
SLCheckCapacity(ps);//扩容
ps->arr[ps->size++] = x;
}
直接在数组的末尾加入x,然后有效数据个数加1。
3.5 顺序表头部删除和尾部删除
头部删除
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--;//有效数据个数减1
}
从前往后移动
尾部删除
void SLPopBack(SL* ps){
assert(ps);
assert(ps->size);
ps->size--;
}
3.6 插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--) {
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
插入位置后的元素往后移一位,我们从后往前移动,从size位置开始,到pos加1的位置。
3.7 删除任意位置
void SLErase(SL* ps, int pos) {
assert(ps && ps->size);
for (int i = pos; i < ps->size-1;i++) {
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
删除位置后的元素往前移一位,我们从前往后移动,从pos位置开始,到size减2的位置。
3.8 查找任意元素
int SLFind(SL* ps, SLDataType x) {
assert(ps && ps->size);//判空
for (int i = 0; i < ps->size; i++) {
if (ps->arr[i] == x)
return i+1;//找到了返回位置(从1开始)
}
return 0;//没找到返回零
}
四、实现代码
接口的定义
seqlist.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLDataType;
typedef struct Seqlist {
SLDataType* arr;
int size; //有效数据个数
int capacity; //数组容量
} SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插入
void SLPushFront(SL* ps, SLDataType x);
//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部删除
void SLPopFront(SL* ps);
//尾部删除
void SLPopBack(SL* ps);
//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x);
//删除任意位置
void SLErase(SL* ps, int pos);
//查找元素
int SLFind(SL* ps, SLDataType x);
接口的具体实现
seqlist.h
#define _CRT_SECURE_NO_WARNINGS 1
#include "Seqlist.h"
//初始化
void SLInit(SL* ps){
ps->arr =NULL;
ps->size = ps->capacity = 0;
}
//销毁
void SLDestroy(SL* ps) {
assert(ps);
free(ps->arr);
ps->arr = NULL;
}
//打印
void SLPrint(SL* ps) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
printf("%d ", ps->arr[i]);
}
printf("\n");
}
//扩容
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity) {
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* t = ps->arr;
t= (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
if (t == NULL) {
perror(" SLCheckCapacity error");
exit(1);
}
ps->arr = t;
ps->capacity = newcapacity;
}
}
//头部插入
void SLPushFront(SL* ps, SLDataType x){
assert(ps);
SLCheckCapacity(ps);
for (int i=ps->size; i >0 ; i-- ) {
ps->arr[i] = ps->arr[i-1];
}
ps->arr[0] = x;
ps->size++;
}
//尾部插入
void SLPushBack(SL* ps, SLDataType x) {
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
//头部删除
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--;
}
//尾部删除
void SLPopBack(SL* ps){
assert(ps);
assert(ps->size);
ps->size--;
}
//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
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 && ps->size);
for (int i = pos; i < ps->size-1;i++) {
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
//查找元素
int SLFind(SL* ps, SLDataType x) {
assert(ps && ps->size);
for (int i = 0; i < ps->size; i++) {
if (ps->arr[i] == x)
return i+1;
}
return 0;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void Seqlist_test() {
//初始化
SL s;
SLInit(&s);
//尾部插入
SLPushBack(&s,1);
SLPushBack(&s,2);
SLPushBack(&s,3);
SLPushBack(&s,4);
SLPrint(&s); //1 2 3 4
//头部插入
//SLPushFront(&s,0);
//SLPrint(&s);
//头部删除
//SLPopFront(&s);
//SLPrint(&s);
//尾部删除
//SLPopBack(&s);
//SLPrint(&s);
//任意位置插入
SLInsert(&s,1 ,8 );
SLPrint(&s);
//SLPrint(&s);
//任意位置删除
//SLErase(&s, 2);
//SLPrint(&s);
int ret = SLFind(&s, 8);
if (ret)
printf("找到了是第%d个元素", ret);
else
printf("没找到");
}
int main() {
Seqlist_test();
return 0;
}
总结
上述文章,我们讲了数据结构中的线性表的一种顺序表,讲了顺序表的定义和具体实现,希望对你有所帮助。