各位CSDN的uu们你们好呀,今天小雅兰的内容是数据结构与算法里面的顺序表啦,在我看来,数据结构总体上是一个抽象的东西,关键还是要多写代码,下面,就让我们进入顺序表的世界吧
线性表
顺序表
线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表就是数组,但是在数组的基础上,它还要求数据是从头开始连续存储的,不能跳跃间隔
顺序表一般可以分为:
静态顺序表:使用定长数组存储元素。
#define _CRT_SECURE_NO_WARNINGS 1 #define N 7 #include"SeqList.h" typedef int SLDateType; typedef struct SeqList { SLDateType arr[N];//定长数组 size_t size;//表示数组中存储了多少数据 }SL;
动态顺序表:使用动态开辟的数组存储。
#define _CRT_SECURE_NO_WARNINGS 1 #define N 7 #include"SeqList.h" typedef int SLDateType; typedef struct SeqList { SLDateType* arr;//指向动态开辟的数组 size_t size;//表示数组中存储了多少数据 size_t capicity;//数组实际能存储数据的空间容量是多大 }SL;
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
在这里,所有函数命名风格都是跟着STL走的,方便小雅兰日后的学习!!!
顺序表的初始化:
void SeqListInit(SL* ps)//顺序表的初始化 { ps->arr = NULL; ps->size = ps->capacity = 0; }
注意:在这里,不可以使用传值调用的方式,只能使用传址调用的方式
void SeqListInit(SL ps)//顺序表的初始化 { ps.arr = NULL; ps.size = ps.capacity = 0; }
万万不能使用这样的方式初始化,因为:在函数传参的时候,形参是实参的一份临时拷贝,对形参的修改并不会影响实参
顺序表的打印:
void SeqListPrint(SL* ps)//顺序表的打印 { int i = 0; for (i = 0; i < ps->size; i++) { printf("%d ", ps->arr[i]); } printf("\n"); }
扩容:
void SeqListCheckCapacity(SL* ps)//检查空间,如果满了,进行扩容 { //如果没有空间或者空间不足,那么我们就扩容 if (ps->size == ps->capacity) { int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; SLDateType* tmp = (SLDateType*)realloc(ps->arr, newcapacity * sizeof(SLDateType)); if (tmp == NULL) { printf("realloc fail!\n"); exit(-1); } ps->arr = tmp; ps->capacity = newcapacity; } }
顺序表销毁:
void SeqListDestroy(SL* ps)//顺序表销毁 { free(ps->arr); ps->arr = NULL; ps->capacity = ps->size = 0; }
尾插:
void SeqListPushBack(SL* ps, SLDateType x)//尾插 { SeqListCheckCapacity(ps); ps->arr[ps->size] = x; ps->size++; }
尾删:
void SeqListPopBack(SL* ps)//尾删 { assert(ps->size > 0); ps->size--; }
头插:
void SeqListPushFront(SL* ps, SLDateType x)//头插 { SeqListCheckCapacity(ps); //挪动数据 int end = ps->size - 1; while (end >= 0) { ps->arr[end + 1] = ps->arr[end]; end--; } ps->arr[0] = x; ps->size++; }
头删:
void SeqListPopFront(SL* ps)//头删 { assert(ps->size > 0); //挪动数据 int begin = 0; while (begin < ps->size-1) { ps->arr[begin] = ps->arr[begin+1]; ++begin; } ps->size--; }
另一种写法:
void SeqListPopFront(SL* ps)//头删 { assert(ps->size > 0); //挪动数据 int begin = 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; ++begin; } ps->size--; }
顺序表查找:
//顺序表查找 //找到了返回x位置的下标,没有找到返回-1 int SeqListFind(SL* ps, SLDateType x) { int i = 0; for (i = 0; i < ps->size; i++) { if (ps->arr[i] == x) { return i; } } return -1; }
指定pos下标位置插入:
// 顺序表指定pos下标位置插入x void SeqListInsert(SL* ps, size_t pos, SLDateType x) { 温柔的写法 //if (pos > ps->size || pos < 0) //{ // printf("pos invalid!\n"); //} //粗暴的写法 assert(pos <= ps->size || pos >= 0); SeqListCheckCapacity(ps); //挪动数据 int end = ps->size - 1; while (end >= pos) { ps->arr[end + 1] = ps->arr[end]; end--; } ps->arr[pos] = x; ps->size++; }
然后,写出了这个函数的功能之后,我们发现:可以对之前写的头插和尾插搞一些事情,下面,我们开始搞事情!!!
头插和尾插:
void SeqListPushBack(SL* ps, SLDateType x)//尾插 { SeqListInsert(ps, ps->size, x); } void SeqListPushFront(SL* ps, SLDateType x)//头插 { SeqListInsert(ps, 0, x); }
删除pos位置的数据:
//顺序表删除pos位置的数据 void SeqListErase(SL* ps, size_t pos) { assert(pos < ps->size || pos >= 0); int begin = pos + 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; ++begin; } ps->size--; } void SeqListErase(SL* ps, size_t pos) { assert(pos < ps->size || pos >= 0); int begin = pos + 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; ++begin; } ps->size--; }
写出这个函数的功能之后,我们又可以搞事情啦!!!
头删和尾删:
void SeqListPopBack(SL* ps)//尾删 { SeqListErase(ps, 0); } void SeqListPopFront(SL* ps)//头删 { SeqListErase(ps, ps->size - 1); }
菜单:
void menu() { printf("#############################################\n"); printf("\n"); printf("###################1.头插#####################\n"); printf("\n"); printf("###################2.头删#####################\n"); printf("\n"); printf("###################3.尾插######################\n"); printf("\n"); printf("###################4.尾删######################\n"); printf("\n"); printf("###################5.打印#####################\n"); printf("\n"); printf("###################0.exit#####################\n"); printf("\n"); printf("##############################################\n"); }
其实,最好不要一上来就写菜单,先写单元测试,菜单不方便调试
测试菜单函数:
void MenuTest() { SL s1; SeqListInit(&s1); int input = 0; int x; while (input != -1) { menu(); scanf("%d", &input); switch (input) { case 1: printf("请输入你要头插的数据,以-1结束:"); while (x != -1) { SeqListPushFront(&s1, x); scanf("%d", &x); } break; case 2: SeqListPopFront(&s1); break; case 3: printf("请输入你要尾插的数据,以-1结束:"); while (x != -1) { SeqListPushBack(&s1, x); scanf("%d", &x); } break; case 4: SeqListPopBack(&s1); break; case 5: SeqListPrint(&s1); break; case 0: printf("退出程序\n"); break; default: printf("输入错误,请重新输入\n"); } } }
源代码如下:
SeqList.h的内容:
#pragma once #include<stdio.h> #define _CRT_SECURE_NO_WARNINGS 1 #include<stdlib.h> #include<assert.h> #define N 7 typedef int SLDateType; //顺序表的动态存储 typedef struct SeqList { SLDateType* arr;//指向动态开辟的数组 size_t size;//表示数组中存储了多少数据 size_t capacity;//数组实际能存储数据的空间容量是多大 }SL; //基本增删查改接口 void SeqListInit(SL* ps);//顺序表的初始化 void SeqListPrint(SL* ps);//顺序表的打印 void SeqListCheckCapacity(SL* ps);//检查空间,如果满了,进行扩容 void SeqListDestroy(SL* ps);//顺序表销毁 void SeqListPushBack(SL* ps, SLDateType x);//尾插 void SeqListPopBack(SL* ps);//尾删 void SeqListPushFront(SL* ps, SLDateType x);//头插 void SeqListPopFront(SL* ps);//头删 //顺序表查找 //找到了返回x位置的下标,没有找到返回-1 int SeqListFind(SL* ps, SLDateType x); //顺序表指定pos下标位置插入x void SeqListInsert(SL* ps, size_t pos, SLDateType x); //顺序表删除pos位置的数据 void SeqListErase(SL* ps, size_t pos);
SeqList.c的内容:
#define _CRT_SECURE_NO_WARNINGS 1 #include"SeqList.h" void SeqListInit(SL* ps)//顺序表的初始化 { ps->arr = NULL; ps->size = ps->capacity = 0; } void SeqListPrint(SL* ps)//顺序表的打印 { int i = 0; for (i = 0; i < ps->size; i++) { printf("%d ", ps->arr[i]); } printf("\n"); } void SeqListCheckCapacity(SL* ps)//检查空间,如果满了,进行扩容 { //如果没有空间或者空间不足,那么我们就扩容 if (ps->size == ps->capacity) { int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; SLDateType* tmp = (SLDateType*)realloc(ps->arr, newcapacity * sizeof(SLDateType)); if (tmp == NULL) { printf("realloc fail!\n"); exit(-1); } ps->arr = tmp; ps->capacity = newcapacity; } } void SeqListDestroy(SL* ps)//顺序表销毁 { free(ps->arr); ps->arr = NULL; ps->capacity = ps->size = 0; } void SeqListPushBack(SL* ps, SLDateType x)//尾插 { SeqListCheckCapacity(ps); ps->arr[ps->size] = x; ps->size++; } void SeqListPopBack(SL* ps)//尾删 { assert(ps->size > 0); ps->size--; } void SeqListPushFront(SL* ps, SLDateType x)//头插 { SeqListCheckCapacity(ps); //挪动数据 int end = ps->size - 1; while (end >= 0) { ps->arr[end + 1] = ps->arr[end]; end--; } ps->arr[0] = x; ps->size++; } void SeqListPopFront(SL* ps)//头删 { assert(ps->size > 0); //挪动数据 int begin = 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; ++begin; } ps->size--; } //顺序表查找 //找到了返回x位置的下标,没有找到返回-1 int SeqListFind(SL* ps, SLDateType x) { int i = 0; for (i = 0; i < ps->size; i++) { if (ps->arr[i] == x) { return i; } } return -1; } // 顺序表指定pos下标位置插入x void SeqListInsert(SL* ps, size_t pos, SLDateType x) { 温柔的写法 //if (pos > ps->size || pos < 0) //{ // printf("pos invalid!\n"); //} //粗暴的写法 assert(pos <= ps->size || pos >= 0); SeqListCheckCapacity(ps); //挪动数据 int end = ps->size - 1; while (end >= pos) { ps->arr[end + 1] = ps->arr[end]; end--; } ps->arr[pos] = x; ps->size++; } //顺序表删除pos位置的数据 void SeqListErase(SL* ps, size_t pos) { assert(pos < ps->size || pos >= 0); int begin = pos + 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; ++begin; } ps->size--; }
好啦,小雅兰今天的内容就到这里啦,数据结构的的确确是一个麻烦的东西,还要加油呀!!!