第五课:线性表的本质
线性表(List)是零个或是多个数据元素的集合
注意(这些要求有点儿类似于数组,但是和数组是有区别的,不要混淆):
1. 线性表中的数据元素之间是有序的
2. 线性表中的数据元素的个数是有限的
3. 线性表中的数据元素的类型必须相同
线性表的专业定义:
线性表是具有相同类型的n(n≥0)个数据元素的有限序列 (a1, a2, .... an)
an是表项, n表示长度
线性表的性质:
1. a0是线性表的第一个元素,只有一个后继;
2. an是线性表的最后一个元素,只有一个前驱;
3. 除了a0和an之外的其他元素ai,既有前驱,也有后继;
4. 线性表能够逐项访问和顺序存取
第六课:线性表的相关操作
常用操作:
1. 创建线性表
2. 销毁线性表
3. 清空线性表
4. 在线性表中插入元素
5. 从线性表中删除元素
6. 获取线性表中某个元素的位置
7. 获取线性表的长度
问题:如何在程序中表达和使用线性表?
线性表在程序中表现为一种特殊的数据类型,相应的操作,在程序中表现为一组函数
示例:
List List_Create(); void List_Destory(List ist); void List_Clear(List list); int List_Insert(List list, ListNode node, int pos); ListNode List_Delete(List list, int pos); ListNode ListGet(List list, int pos); int List_Length(List list);
问题:
1. 线性表的各个函数是如何实现的?
2. 有几种线性表的实现方式呢?
3. 每种实现方式的优缺点是什么?
第七课:线性表的顺序存储结构(数组的方式实现)
顺序存储的定义: 线性表的顺序存储结构,是用一段地址连续的存储单元依次存储线性表的数据元素 -- 这有些类似于数组
例如:从地址0xAABBCCDD开始存储线性表的n个元素:
a1 。。。。ak 。。。。。。an
所以,在C语言中,线性表的顺序存储结构可以用一维数组来实现
需要厘清的要点:
1. 存储空间的起始位置
2. 线性表的最大容量:MAXSIZE
3.线性表的当前长度:length
示例:
#define MAXSIZE 20 typedef struct _tag_List { char node[MAXSIZE]; int length; } List;
相关操作的具体实现:
1. 获取线性表中的元素的操作
1.1 判断线性表是否合法
1.2 判断位置是否合法
1.3 直接通过数组下标的方式获取元素
示例代码:
char Get(List list, int pos)
{
char ret = -1;
//判断线性表和位置是否合法
if((list != NULL) && (0 <= pos) && (pos < list->length))
{
//获取元素
ret = list->node[pos];
}
return ret;
}
2. 插入元素算法
2.1 判断线性表时候合法
2.2 判断插入位置是否合法
2.3 把最后一个元素一直到插入位置的元素顺序后移一个位置
2.4 将新元素插入
2.5 线性表长度加1
示例代码:
int Insert(List list, char c, int pos) { // 1 判断线性表时候合法 int i = 0; int ret = (list != NULL); // 2 判断插入位置是否合法 ret = ret && (list->length + 1 <= MAXSIZE); ret = ret && (0 <= pos); if(ret) { if(pos >= list->length) { pos = list->length; } // 3 把最后一个元素一直到插入位置的元素顺序后移一个位置 for(i=list->legth; i>pos; i--) { list->node[i] = list->node[i-1]; } // 4 将新元素插入 list->node[i] = c; // 5 线性表长度加1 list->length++; } return ret; }
3. 删除元素
3.1 判断线性表时候合法
3.2 判断删除位置是否合法
3.3 将元素取出
3.4 将删除位置后的元素分别向前移动一个位置
3.5 线性表长度减1
示例代码:
char Delete(List list, int pos) { char ret = -1; int i = 0; // 1 判断线性表时候合法 // 2 判断删除位置是否合法 if((list != NULL) && (0 <= pos) && (pos <= list->length)) { // 3 将元素取出 ret = list->node[pos]; // 4 将删除位置后的元素分别向前移动一个位置 for(i=pos+1; i<list->length; i++) { list->node[i-1] = list->node[i]; } // 5 线性表长度减1 list->length--; } return ret; }
顺序存储结构的缺点:
在插入和删除元素的时候,有时候需要移动大量的元素,
例如,假如这个顺序表有几十万个数据,要插入或是要删除的的元素是在线性表的最前面,
那么就需要把这几十万个元素依次前移或是依次后移,这会降低程序的执行效率
一个完整的示例:
SeqList.h
#ifndef _SEQ_LIST_H_ #define _SEQ_LIST_H_ typedef void SeqList; typedef void SeqListNode; SeqList *SeqList_Create(int capactiy);//创建并返回一个容量为capactiy的空的线性表 int SeqList_Capactiy(SeqList *list);//返回线性表的最大容量 void SeqList_Destory(SeqList *list);//销毁一个已经存在的线性表(应该在内部进行检查,如果线性表不存在,要报错) void SeqList_Clear(SeqList *list);//清空线性表中的额所有元素,使线性表恢复到创建时的状态 int SeqList_Insert(SeqList *list, SeqListNode *node, int pos);//在位置pos处插入一个新的元素node,返回值为1表示插入成功, 0表示插入失败 SeqListNode *SeqList_Delete(SeqList *list, int pos);//删除pos位置处的一个元素,返回值为被删除的元素, NULL表示删除失败 SeqListNode *SeqList_Get(SeqList *list, int pos);//获得位置pos处的元素,返回值是pos处的元素,NULL表示获取失败 int SeqList_Length(SeqList *list);//返回线性表中所有元素的个数 //还可以添加一个修改元素的函数 和一个查询函数(增 删 改 查) #endif
SeqList.c
#include <stdio.h> #include <malloc.h> //用于动态申请释放内存 #include "SeqList.h" typedef unsigned int TSeqListNode;//把线性表中的数据元素定义为unsigned int,是为了能够复用, typedef struct _tag_SeqList { int capacity; //线性表的最大容量 int length; //线性表的长度 TSeqListNode *node; }TSeqList; SeqList *SeqList_Create(int capactiy)//创建并返回一个空的线性表 { TSeqList *ret = NULL; if (capactiy >= 0)//判断参数是否合法 { // 申请的内存空间为什么要加上sizeof(TSeqListNode) * capactiy,因为线性表要存储数据,结构体中的第三个元素只是是一个指针, // 它指向的是实际的数据的存储空间的地址,这样可以把线性表的属性(容量,长度,数据)和实际数据进行分开处理 ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(TSeqListNode) * capactiy); if (ret != NULL)//判断申请的空间是否成功 { ret->capacity = capactiy;// ret->length = 0; //刚开始创建,还没有数据,长度就是0 ret->node = (TSeqListNode *)(ret + 1); } //printf("%p\n", ret); } //if (ret != NULL)//判断申请的空间是否成功 //{ // ret->capacity = capactiy;// // ret->length = 0; //刚开始创建,还没有数据,长度就是0 // ret->node = (TSeqListNode *)(ret + 1); //} return ret; } void SeqList_Clear(SeqList *list)//清空线性表中的额所有元素,使线性表恢复到创建时的状态 { TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 if (slist != NULL) { slist->length = 0; } } int SeqList_Length(SeqList *list)//返回线性表中所有元素的个数 { int ret = -1; TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 if (slist != NULL) { ret = slist->length; } return ret; } int SeqList_Capactiy(SeqList *list)//返回线性表的最大容量 { int ret = -1; TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 if (slist != NULL) { ret = slist->capacity; } return ret; } int SeqList_Insert(SeqList *list, SeqListNode *node, int pos)//在位置pos处插入一个新的元素node,返回值为1表示插入成功, 0表示插入失败 { TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 int ret = (slist != NULL); int i = 0; ret = ret && (slist->length+1 <= slist->capacity);// 插入之后的线性表的长度应该要小于或等于线性表的最大长度 ret = ret && (pos >= 0);//判断插入的位置是否合法 if (ret) { if (pos >= slist->length) //如果待插入的位置大于线性表的长度,把要插入的元素直接放在线性表的最后一个位置 { pos = slist->length; } for (i = slist->length; i > pos; i--)//挪动元素位置,这一步可能是最耗时的 { slist->node[i] = slist->node[i-1]; } slist->node[i] = (TSeqListNode)node; // 在空出来的地方插入元素 slist->length++; //线性表的长度要加1 } return ret; } SeqListNode *SeqList_Delete(SeqList *list, int pos)//删除pos位置处的一个元素,返回值为被删除的元素, NULL表示删除失败 { TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 SeqListNode *ret = SeqList_Get(list, pos); //先取出要删除的元素 int i = 0; if (ret != NULL) { for (i = pos+1; i<slist->length; i++) { slist->node[i-1] = slist->node[i]; } slist->length--; } return ret; } SeqListNode *SeqList_Get(SeqList *list, int pos)//获得位置pos处的元素,返回值是pos处的元素,NULL表示获取失败 { TSeqList *slist = (TSeqList *)list; //做一次强制类型转换 SeqListNode *ret = NULL; //这里需要注意 if ((slist != NULL) && (0 <= pos) && (pos <= slist->length)) { ret = (SeqListNode *)(slist->node[pos]); } return ret; } void SeqList_Destory(SeqList *list)//销毁一个已经存在的线性表(应该在内部进行检查,如果线性表不存在,要报错) { free(list); }
main.c
#include <stdio.h> #include <stdlib.h> #include "SeqList.h" int main(int argc, char *argv[]) { // 1. 顺序存储测试程序: int i=0, j=1, k=2, x=3, y=4, z=5; int index = 0; SeqList *list = SeqList_Create(10000); SeqList_Insert(list, &i, 0); SeqList_Insert(list, &j, 0); SeqList_Insert(list, &k, 0); SeqList_Insert(list, &x, 0); SeqList_Insert(list, &y, 0); SeqList_Insert(list, &z, 0); for (index = 0; index<SeqList_Length(list); index++) { int *p = (int *)SeqList_Get(list, index); printf("%d\n", *p); } printf("\n"); while (SeqList_Length(list) > 0) { int *p = (int *)SeqList_Delete(list, 0); printf("%d\n", *p); } SeqList_Destory(list); // 2. 链式存储测试代码: return 0; }