目录
前言
顺序表是数据结构中的一种重要形式,它属于线性表的一种顺序存储结构,其逻辑上相邻的元素在物理存储位置上也相邻。
一、顺序表的定义和特点
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
顺序表具有以下特点:顺序表可以根据需要动态地增加或减少存储空间,以适应不同数量的数据元素;顺序表中逻辑上相邻的元素在物理存储位置上也相邻;由于顺序表在物理上是连续存储的,因此可以通过索引直接访问任何位置的元素。
二、顺序表分类
静态顺序表:使用定长数组存储元素。静态顺序表的存储空间在编译时就已确定,无法在运行时改变。因此,它适用于存储数量固定且已知的数据元素。静态顺序表的缺陷在于存储空间固定,扩展性差,灵活性不足。
typedef int data_t; #define N 128 // 数组大小已定 struct sqlist_t { data_t data[N]; int last; }; typedef struct sqlist_t sqlist; typedef struct sqlist_t * sqlink; //与下面写法等价 /* typedef int data_t; #define N 128 typedef struct { data_t data[N]; int last; }sqlist, *sqlink; */
动态顺序表:使用动态开辟的数组来存储数据元素。动态顺序表允许在运行时根据需要自动调整存储空间的大小,以适应不同数量的数据元素。这种数据结构在实际应用中非常有价值,因为它既保持了顺序表随机访问的高效性,又克服了静态顺序表存储空间固定、扩展性差的缺点。
typedef int data_t; typedef struct { data_t *data; // 指向动态分配的数组的指针 int len; // 数组长度 int last; } sqlist,* sqlink;
三、基本操作
1、静态顺序表
创建顺序表
对于静态顺序表而言,其相当于一个已知的数组,其长度大小固定。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef int data_t; #define N 128 // 数组大小已定 typedef struct { data_t data[N]; int last; }sqlist, *sqlink; sqlink list_create() //创建顺序表,返回顺序表地址 { sqlink L; L =(sqlink)malloc(sizeof(sqlist)); if (L == NULL) { printf("list malloc failed\n"); return L; } memset(L, 0, sizeof(sqlist));//将顺序表中成员的初始值都设置为0 L->last = -1;//数组索引,开始时设为1 return L;//返回顺序表指针 }
顺序表插入与删除
创建完完成之后,就可以对顺序表插入数据了,需要参数为:插入的数据,以及插入的位置。
int list_insert(sqlink L, data_t value, int pos) { int i; //判断顺序表是否满了 if (L->last == N-1) { printf("list is full\n"); return -1; } //判断位置是否合理 if (pos < 0 || pos > L->last+1) { printf("Pos is invalid\n"); return -1; } //移动数据,留出插入数据的位置 for (i = L->last; i >= pos; i--) L->data[i+1] = L->data[i]; //插入数据,并更新数据长度 L->data[pos] = value; L->last++; return 0; }
删除指定位置的数据也是同理,需要只需要将指定位置的后面的数据依次往前移动一个单元即可,然后把尾部索减一。
int list_delete(sqlink L, int pos) { int i; if (L->last == -1) { printf("list is empty\n"); return -1; } if (pos < 0 || pos > L->last) { printf("delete pos is invalid\n"); return -1; } for (i = pos+1; i <= L->last; i++) L->data[i-1] = L->data[i]; L->last--; return 0; }
顺序表释放
在使用完顺序表之后,需要将其内存释放,因为这里是使用动态内存分配的空间。
int list_free(sqlink L) { if (L == NULL) return -1; free(L); L = NULL; return 0; }
其他操作
如顺序表的清除,检验是否为空,当前数据的长度,打印显示,数据查找并返回索引等操作。
int list_clear(sqlink L) //清除顺序表 { if (L == NULL) return -1; memset(L, 0, sizeof(sqlist)); L->last = -1; return 0; } int list_empty(sqlink L) //判断顺序表是否为空 { if (L->last == -1) return 1; else return 0; } int list_length(sqlink L) //返回顺序表长度 { if (L == NULL) return -1; return (L->last+1); } int list_show(sqlink L) //顺序表遍历打印 { int i; if (L == NULL) return -1; if (L->last == -1) printf("list is empty\n"); for (i = 0; i <= L->last; i++) { printf("%d ", L->data[i]); } puts("\n"); return 0; } int list_locate(sqlink L, data_t value) //数据查找,并返回位置 { int i ; for (i = 0; i <= L->last; i++) { if (L->data[i] == value) return i; } return -1; }
2、动态顺序表
创建顺序表
与静态顺序表不同,动态顺序表不依赖于固定大小的数组,而是使用动态内存分配(如C语言中的malloc函数)来管理存储空间。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef int data_t; typedef struct { data_t *data; // 指向动态分配的数组的指针 int len; // 数组长度 int last; } sqlist,* sqlink; sqlink sqlist_create(int len) //创建长度为len的顺序表 { sqlink L; if ((L =(sqlink)malloc(sizeof(sqlist))) == NULL) //动态分配顺序表结构体的内存 { printf("malloc sqlist failed\n"); return NULL; } if ((L->data = (data_t *)malloc(len * sizeof(data_t)))==NULL) //动态分配结构体中数组的大小 { printf("malloc data failed\n"); free(L);//数据内存分配失败时,最好将前面分配的结构体内存给释放掉 return NULL; } memset(L->data, 0, len*sizeof(data_t));//初始化数组 L->len = len;//初始化长度 L->last= -1; return L; }
其他操作
相较于静态顺序表,动态顺序表只是自定义了存储数据空间的大小,将原来已知的固定的N改为可变的len成员,其他与静态顺序表无异,所以动态顺序表的插入、删除、清空、打印等操作与静态顺序表相同,不再举例展示。区别于静态顺序表,这里使用了两次malloc进行动态内存分配,在顺序表内存时,需要进行两次free来释放,即malloc要与free配对使用。
四、总结
1、空间利用率:静态顺序表的空间利用率可能较低,因为无法在运行时调整存储空间的大小。而动态顺序表虽然可以动态调整存储空间的大小,但在扩容时可能会产生一定的空间浪费。
2、元素类型:顺序表可以存储任意类型的元素,包括基本数据类型和结构体等。但是,在使用时需要注意元素类型的一致性,以便正确地访问和修改元素的值。
顺序表的使用和操作有很多,本文只是做个简介。
有误之处望指正!!