@[TOC]
前言
本篇介绍数据结构中存储元素--线性表中的顺序表
线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
顺序表
- 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改
静态和动态顺序表
- 静态顺序---使用定长数组存储元素
- 动态顺序表---动态存储元素
- 静动态比较
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪
费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实
现动态顺序表
动态顺序表
结构体声明
既然动态存储元素。自然需要先确定一个
当前
开辟内存的最大容量--capacity和记录有效数据个数据的指标,方便进行内存的访问。因此定义结构体:typedef int SLDateType;//方便修改顺序表的元素类型 typedef struct SeqList//重命名结构体为SL { SLDateType* psl;//psl指向动态申请的空间. size_t sz;//记录有效数据个数 size_t capacity;//开辟空间的容量 }SL;
初始化结构体
- 数组下标是从0开始的,因此初始化中的sz,总是指向数组中下一个元素的下标,这很特殊!!!!后面判断当前申请的空间是否满要留意。
void SeqListInit(SL* p) { assert(p); p->psl = NULL; p->sz = 0; p->capacity = 0; }
判断数组是否满
假设数组最大容量是10,因此数组下标从0开始,因此当sz等于cappacity时就满了。
void SeqListRealloc(SL* p) { if (p->sz == p->capacity)//当相等时,就需要扩容了,这里我以二赔扩容. { p->capacity = p->capacity == 0 ? 4 : 2 * p->capacity; SLDateType* tmp = (SLDateType*)realloc(p->psl, p->capacity * sizeof(SLDateType)); if (tmp == NULL) { printf(""); exit(-1); } p->psl = tmp; } }
指定位置,插入元素
- 头插,尾插只是其中的一种形式。后面可以复用SeqListInsert
注意数组下标
void SeqListInsert(SL* p, size_t pos, SLDateType x)//在指定位置pos,插入元素。 { assert(p); assert(pos <= p->sz && pos>=0);//顺序表是依次填入数据的,因此不能比较capacity SeqListRealloc(p);//判断是否需要扩容。 for (size_t i = p->sz; i>pos; --i)//size_t 要考虑 { p->psl[i] = p->psl[i-1]; } p->psl[pos] = x; p->sz++; }
头插元素
void SeqListPushFront(SL* p, SLDateType x)//头插元素. { assert(p); SeqListInsert(p, 0, x); }
尾插元素
void SeqListPushBack(SL* p, SLDateType x)//尾插元素. { assert(p); SeqListInsert(p, p->sz, x); }
指定位置删元素
- 注意我们指的删除元素,并不是真的删除它,而是通过sz--的形式,因为一方面当后续添加元素时,在相应位置添加元素就行,另外一方面是动态开辟的内存不允许在中间就行释放动态内存。
void SeqListErase(SL* p, size_t pos)//指定位置pos(代表数组下标)删除. { assert(p); assert(pos <= p->sz && pos >= 0); for (size_t i = pos; i < p->sz - 1; ++i) { p->psl[i] = p->psl[i + 1]; } p->sz--; }
头删元素
void SeqListPopFront(SL* p)//头删元素. { assert(p); SeqListErase(p, 0); }
尾删元素
void SeqListPopBack(SL* p)//尾删元素. { assert(p); SeqListErase(p, p->sz-1); }
查找元素
int SeqListFind(SL* p, SLDateType x)//在查找元素,找到返回下标,否则0; { for (size_t i = 0; i < p->sz; ++i) { if (x == p->psl[i]) { return i; } } return 0; }
打印数组
void SeqListPrint(SL* p)//打印 { assert(p); for (size_t i = 0; i < p->sz; ++i) { printf("%d ", p->psl[i]); } printf("\n"); }
销毁申请的空间
void SeqLIstDestory(SL* p)//free { free(p->psl); p->psl = NULL; p->capacity = 0; p->sz = 0; }
菜单
// 构建动态顺序表。 enum whole { Exit, pushfront, pushback, pushinsert, popfront, popback, poperase, popdate, print }; void menu() { printf("************************************************************\n"); printf("***************静态顺序表***********************************\n"); printf("*********1.头插 2.尾插 3.任意位置插*****************\n"); printf("*********4.头删 5.尾删 6.任意位置删*****************\n"); printf("*********7.指定元素删 8.打印 0 .Exit ********************\n"); printf("************************************************************\n"); } int main () { SL s; int input = 0; SeqListInit(&s); SLDateType x = 0;//要是你是整型,需修改scanf. int Flage = 0; size_t pos = 0;//位置 int ret = 0; do { menu(); printf("请输入你的选择:"); scanf("%d", &input); switch (input) { case pushfront: printf("\t头插\n"); printf("请输入你要插入的元素->,以0为结束标志\n"); scanf("%d", &x); while (x) { SeqListPushFront(&s, x); scanf("%d", &x); } break; case pushback: printf("\t尾插\n"); printf("请输入你要插入的元素->,以0为结束标志\n"); scanf("%d", &x); while (x) { SeqListPushBack(&s, x); scanf("%d", &x); } break; case pushinsert: printf("\t任意位置插\n"); printf("请输入你要插入的元素->与位置->,以0为结束标志\n"); scanf("%d%d", &x,&pos); while (x) { SeqListInsert(&s,pos, x); scanf("%d%d", &x,&pos); } break; case popfront: printf("\t头删\n"); printf("输入一个数,以非0数表示进行头删,0表示结束\n"); scanf("%d", &Flage); while (Flage) { SeqListPopFront(&s); scanf("%d", &Flage); } break; case popback: printf("\t尾删\n"); printf("输入一个数,以非0数表示进行尾删,0表示结束\n"); scanf("%d", &Flage); while (Flage) { SeqListPopBack(&s); scanf("%d", &Flage); } break; case poperase: printf("\t任意位置删\n"); printf("请输入一个数,以1表示进行任意位置删除,以0为结束标志,并输入一个位置\n"); scanf("%d%d", &Flage,&pos); while (Flage) { SeqListErase(&s,pos); scanf("%d%d", &Flage, &pos); } break; case popdate: printf("\t指定元素删除\n"); printf("输入一个数,以非0数表示进行头删,0表示结束,并输入指定元素\n"); scanf("%d%d", &Flage, &x); while (Flage) { ret = SeqListFind(&s, x); if (ret != -1) { SeqListErase(&s, ret); printf("删除成功\n"); } else { printf("该数据不存在\n"); } scanf("%d%d", &Flage, &x); } break; case print: SeqListPrint(&s); break; case Exit: SeqLIstDestory(&s); system("cls"); printf("感谢使用!\n"); break; default:printf("输入有误,请重新输入"); } } while (input); return 0; }
效果展示
总结
- 搞这些是为了熟悉动态顺序表,另外程序中得菜单是不需要特别去在意。能把代码功能无BUG的搞出来,才是对的。
- 下面介绍顺序表中的链表---8种链表形式,主要用其中2种。