1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有线序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就是说是连续的一条线。但是在物理结构上并不一定是连续的,比如链表。
线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1 概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为静态顺序表和动态顺序表
静态顺序表
使用定长数组存储元素。
#define MAX 7 typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Daratype a[MAX]; int sz;//有效数据个数 }SL;
动态顺序表
使用动态开辟的数组存储
typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Daratype* a; int sz;//有效数据个数 int capacity;//存储空间大小 }SL;
3.模拟实现
静态顺序表只适合于确定知道要存储多少数据的场景下。在储存空间不确定的场景下,对于静态顺序表当MAX开大了就会造成浪费,当MAX开小了又不够。所以在实际的场景中基本都是使用动态顺序表,根据需要动态分配空间大小。
下面是模拟实现:
3.1 准备工作
定义一个结构体
typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Datatype* a; int sz;//有效数据个数 int capacity;//存储空间大小 }SL;
3.2 顺序表的初始化与销毁
对于顺序表的初始化,我的话会先给顺序表开好3个空间的大小.
注意:一定要传地址。
void InitSeqlist(SL* ps) { ps->a = (Datatype*)malloc(sizeof(Datatype)*3); if(ps->a == NULL)//扩容失败,直接退出程序 { perror("InitSeqlist"); exit(-1); } ps->sz = 0; ps->capacity = 3; }
销毁
销毁我们直接free就可以了,任何其他值赋0.
void DestorySeqlist(SL* ps) { free(ps->a); ps->a = NULL; ps->sz = 0; ps->capacity = 0; }
3.3 顺序表的尾插
插入前我们一定要先检查一下ps是不是应该空指针。
检查完ps后,对于数据的插入会存在两种情况:
1.顺序表已满,需要扩容
2.顺序表未,满直接插入
因为后面的头插与在特定位置的数据插入都会用到检查顺序是否已满,满就扩容的功能,那么我们可以封装成应该函数。
void SeqlistCapacity(SL* ps) { if(ps->capacity == ps->sz)//满了 { Datatype* tmp = (Datatype*)realloc(ps->a, sizeof(Datatype) * 2*ps->capacity); if(tmp == NULL)//开辟失败 { perror("realloc"); exit(-1); } ps->a = tmp; ps->capacity *= 2; } } void PushBack(SL* ps,Datatype x) { assert(ps); //检查空间是否已满 SeqlistCapacity(ps); ps->a[ps->sz] = x; ps->sz+=1; }
3.4 顺序表的尾删
删除前我们一定要先检查一下ps是不是应该空指针。
同时还要删除该顺序表中的数据也又两种情况:
1.顺序表中的数据已经删完了,无法再删。
2.顺序表中的数据足够删除。直接删
void PopBack(SL* ps) { assert(ps); if(ps->sz>0) { ps->sz-=1; } }
3.5顺序表的打印
直接打印就好了
void PrintSeqlist(SL* ps) { for (int i = 0; i < ps->sz; ++i) { printf("%d ", ps->a[i]); } }
3.6 顺序表的头插
只要是插入数据就需要检查空间是否已满。
然后头插需要移动数据,我们需要把所有的数据都相后移动一位
这样的话,覆盖的顺序就很重要了,如果从前向后覆盖话,有效数据就没了
这样的话2就被覆盖了。
这样就不会了,还是不明白的话多画图就可以搞懂了。
void PushFront(SL* ps,Datatype x) { assert(ps); SeqlistCapacity(ps); for(int i = ps->sz;i>=1;--i) { ps->a[i] = ps->a[i-1]; } ps->a[0] = x; ps->sz += 1; }
3.7 顺序表的头删
只要是删除就需要判断顺序表是否为空。
这个图我就不画了,大家可以画一画从前面开始覆盖和从后面开始覆盖的图。就会豁然开朗的。
void PopFront(SL* ps) { assert(ps); assert(ps->sz!=0); for(int i = 0;i<ps->sz-1;++i) { ps->a[i] = ps->a[i+1]; } ps->sz -= 1; }
3.8 顺序表查找
遍历顺序表,没找到的话就返回-1
int FindSeqlist(SL* ps,Datatype x) { assert(ps); for(int i = 0;i<ps->sz;++i) { if(ps->a[i] == x) return i; } return -1;//没找到 }
3.9 顺序表在pos位置插入x
就是类似与头插,头插可以理解为在0位置前插入数据。
void InsertSeqlist(SL* ps,int pos,Datatype x) { assert(ps); SeqlistCapacity(ps); for(int i = ps->sz;i>=pos+1;--i) { ps->a[i] = ps->a[i-1]; } ps->a[pos] = x; ps->sz+=1; }
3.10 顺序表删除pos位置的值
类似于头删
void EraseSeqlist(SL* ps,int pos) { assert(ps); assert(ps->sz!=0); for(int i = pos;i<ps->sz-1;++i) { ps->a[i] = ps->a[i+1]; } ps->sz -= 1; }
4.代码整合
//seqlist.h #include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Datatype* a; int sz;//有效数据个数 int capacity;//存储空间大小 }SL; void InitSeqlist(SL* ps); void DestorySeqlist(SL* ps); void SeqlistCapacity(SL* ps); void PopBack(SL* ps); void PrintSeqlist(SL* ps); void PushFront(SL* ps, Datatype x); void PopFront(SL* ps); int FindSeqlist(SL* ps, Datatype x); void InsertSeqlist(SL* ps, int pos, Datatype x); void EraseSeqlist(SL* ps, int pos); void PushBack(SL* ps, Datatype x); //seqlist.c #include "seqlist.h" void InitSeqlist(SL* ps) { ps->a = (Datatype*)malloc(sizeof(Datatype) * 3); if (ps->a == NULL)//扩容失败,直接退出程序 { perror("InitSeqlist"); exit(-1); } ps->sz = 0; ps->capacity = 3; } void DestorySeqlist(SL* ps) { free(ps->a); ps->a = NULL; ps->sz = 0; ps->capacity = 0; } void PushBack(SL* ps, Datatype x) { assert(ps); //检查空间是否已满 SeqlistCapacity(ps); ps->a[ps->sz] = x; ps->sz += 1; } void SeqlistCapacity(SL* ps) { if (ps->capacity == ps->sz)//满了 { Datatype* tmp = (Datatype*)realloc(ps->a, sizeof(Datatype) * 2*ps->capacity); if (tmp == NULL)//开辟失败 { perror("realloc"); exit(-1); } ps->a = tmp; ps->capacity *= 2; } } void PopBack(SL* ps) { assert(ps); if (ps->sz > 0) { ps->sz -= 1; } } void PrintSeqlist(SL* ps) { for (int i = 0; i < ps->sz; ++i) { printf("%d ", ps->a[i]); } printf("\n"); } void PushFront(SL* ps, Datatype x) { assert(ps); SeqlistCapacity(ps); for (int i = ps->sz; i >= 1; --i) { ps->a[i] = ps->a[i - 1]; } ps->a[0] = x; ps->sz += 1; } void PopFront(SL* ps) { assert(ps); assert(ps->sz != 0); for (int i = 0; i < ps->sz - 1; ++i) { ps->a[i] = ps->a[i + 1]; } ps->sz -= 1; } int FindSeqlist(SL* ps, Datatype x) { assert(ps); for (int i = 0; i < ps->sz; ++i) { if (ps->a[i] == x) return i; } return -1;//没找到 } void InsertSeqlist(SL* ps, int pos, Datatype x) { assert(ps); SeqlistCapacity(ps); for (int i = ps->sz; i >= pos + 1; --i) { ps->a[i] = ps->a[i - 1]; } ps->a[pos] = x; ps->sz += 1; } void EraseSeqlist(SL* ps, int pos) { assert(ps); assert(ps->sz != 0); for (int i = pos; i < ps->sz - 1; ++i) { ps->a[i] = ps->a[i + 1]; } ps->sz -= 1; } //test.c #include "seqlist.h" int main() { //测试 /*SL sl; InitSeqlist(&sl); PushBack(&sl, 1); PushBack(&sl, 2); PushBack(&sl, 3); PushBack(&sl, 4); PushBack(&sl, 5); PushBack(&sl, 6); PrintSeqlist(&sl); PopBack(&sl); PopBack(&sl); PopBack(&sl); PopBack(&sl); PrintSeqlist(&sl); PopBack(&sl); PopBack(&sl); PopBack(&sl); PrintSeqlist(&sl); PushBack(&sl, 1); PushBack(&sl, 2); PushBack(&sl, 3); PushBack(&sl, 4); PushBack(&sl, 5); PushBack(&sl, 6); PushFront(&sl, 100); PushFront(&sl, 200); PrintSeqlist(&sl); InsertSeqlist(&sl, 0, 1000); PrintSeqlist(&sl); int pos = FindSeqlist(&sl, 100); PopFront(&sl); PopFront(&sl); PopFront(&sl); PopFront(&sl); PrintSeqlist(&sl);*/ //printf("%d", pos); return 0; }
完