数据结构(一)线性表顺序存储实现
(一)线性表的抽象数据类型定义
定义:由零个(空表)或多个数据元素组成的序列
ADT: 线性表List Data: 线性表的数据对象集合为{a1,a2,...,an},每个元素类型为DataType。除了第一个无前驱,最后一个无后继, 其他每个元素都有一个字节前驱和直接后继结点。数据元素间关系一对一。 Operation: InitList(*L);//初始线性表,创建空表 ClearList(*L);//清空线性表数据 ListEmpty(L);//判断列表是否为空 ListLength(L);//获取线性表的长度 GetElem(L,i,* e);//获取指定位置的元素,返回在指针元素中 LocateElem(L,e);//查找元素在线性表中的位置 ListInsert(*L,i,e);//向线性表中指定位置插入元素 ListDelete(*L, i, *e);//删除指定位置处的元素
(二)线性表的实现
#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 //线性表最大存储空间,使用数组实现 typedef int Status; //用Status来表示函数返回状态码,是上面定义的OK等数据状态 typedef int ElemType; //ElemType是表示我们存储数据的类型,根据情况而定,这里设置为int typedef struct { ElemType data[MAXSIZE]; //数组存储数据元素 int length; //线性表当前长度 }SqList; //表名 //四个基本操作,初始,清空,判断是否为空,获取长度 Status InitList(SqList* L); Status ClearList(SqList* L); Status ListEmpty(SqList L); int ListLength(SqList L); //四个元素操作,插入,删除,两种查找 Status GetElem(SqList L,int i,ElemType* e); int LocateElem(SqList L, ElemType e); Status ListInsert(SqList* L, int i, ElemType e); Status ListDelete(SqList* L, int i, ElemType* e); void PrintList(SqList L); /* 复习注意: 我们要进行初始,增加,删除,是需要在原来线性表中进行,所以需要用到引用对原数据进行操作, 而对于查找,获取长度,不需要对原来数据进行操作,我们直接对赋值后的局部变量(含有原线性表的所有数据)参数进行操作,即可 */ //四个基本操作,初始,清空,判断是否为空,获取长度 //初始线性表 Status InitList(SqList* L) { L->length = 0; for (int i = 0; i < MAXSIZE; i++) { L->data[i] = 0; } return OK; } //清空线性表数据 Status ClearList(SqList* L) { L->length = 0; for (int i = 0; i < MAXSIZE; i++) L->data[i] = 0; return OK; } //判断列表是否为空 Status ListEmpty(SqList L) { if (L.length == 0) return TRUE; return FALSE; } //获取线性表的长度 int ListLength(SqList L) { return L.length; } //四个元素操作,插入,删除,两种查找 //获取指定位置的元素,返回在指针元素中 Status GetElem(SqList L, int i, ElemType* e) { if (L.length == 0 || i<1 || L.length < i) return ERROR; *e = L.data[i - 1]; return OK; } //查找元素在线性表中的位置 int LocateElem(SqList L, ElemType e) { int i = 0; if (L.length == 0) return 0; for (; i < MAXSIZE;i++) if (L.data[i] == e) break; if (i >= MAXSIZE) return 0; return i+1; //注意线性表中的位置不是按照数组一样从0开始,而是按照我们正常习惯1开始的 } //向线性表中指定位置插入元素 Status ListInsert(SqList* L, int i, ElemType e) { int k; if (L->length == MAXSIZE)//线性表满 return ERROR; if (i<1 || i>L->length+1) return ERROR; //开始进行插入操作,先判断元素插入位置,在将后面元素向后移动一位,进行插入 if (i <= L->length) { for (k = L->length; k >= i;k--) //只考虑插入到i=1(数组0),会简单点 L->data[k] = L->data[k - 1]; } L->data[i - 1] = e; L->length++; return OK; }
//删除指定位置处的元素 Status ListDelete(SqList* L, int i, ElemType* e) { int k; if (L->length == 0 || i < 1 || i > L->length) return ERROR; //删除元素,是将后面元素前移,再将最后的元素删除 *e = L->data[i - 1]; if (i < L->length) { for (k = i; k < L->length; k++) L->data[k - 1] = L->data[k]; } L->data[L->length-1] = 0; L->length--; return OK; } //打印链表数据 void PrintList(SqList L) { printf("print list begin...\n"); for (int i = 0; i < L.length; i++) { printf("%d ", L.data[i]); } printf("\nprint list end...\n"); } int main() { SqList L; ElemType e; printf("1.InitList\n"); InitList(&L); printf("2.1 ListInsert 1-10\n"); for (int i = 1; i <= 10; i++) ListInsert(&L, i, i*i + 1); printf("2.2 ListInsert 5\n"); ListInsert(&L, 5, 256); printf("2.3 ListInsert 19 Error\n"); if (ListInsert(&L, 19, 99) == ERROR) printf("ListInsert(&L, 19, 99) Error:Out of range"); PrintList(L); printf("3.ListDelete the first:"); ListDelete(&L, 1, &e); printf("%d\n", e); printf("4.ListDelete the end:"); ListDelete(&L, ListLength(L), &e); printf("%d\n", e); PrintList(L); printf("5.1 find element use GetElem by index(6): "); GetElem(L, 6, &e); printf("%d\n", e); printf("5.1 find element:256 index by LocateElem:"); printf("%d\n", LocateElem(L, 256)); printf("6.Get List length:%d\n", ListLength(L)); printf("7.ClearList\n"); ClearList(&L); if (ListEmpty(L)==OK) printf("8.ListEmpty\n"); system("pause"); return 0; }
1.InitList initial List.... 2.1 ListInsert 1-10 2.2 ListInsert 5 2.3 ListInsert 19 Error ListInsert(&L, 19, 99) Error:Out of rangeprint list begin... 2 5 10 17 256 26 37 50 65 82 101 print list end... 3.ListDelete the first:2 4.ListDelete the end:101 print list begin... 5 10 17 256 26 37 50 65 82 print list end... 5.1 find element use GetElem by index(6): 37 5.1 find element:256 index by LocateElem:4 6.Get List length:9 7.ClearList 8.ListEmpty 请按任意键继续. . .
注意点:
我们进行操作线性表是从1开始的,而数组索引是从0开始的,这是我们需要注意的地方
(三)实现两个线性表的并集
//实现两个线性表的并集 void unionL(SqList* La, SqList Lb) { ElemType e; int i = 0; int La_length = ListLength(*La); int Lb_length = ListLength(Lb); for (i = 1; i <= Lb_length; i++) { GetElem(Lb, i, &e); if (!LocateElem(*La, e)) ListInsert(La, La_length++, e); } }
int main() { SqList La,Lb; InitList(&La); InitList(&Lb); for (int i = 1; i <= 10; i++) { ListInsert(&La, i, 3 * i); ListInsert(&Lb, i, 2 * i); } unionL(&La, Lb); PrintList(La); system("pause"); return 0; }
print list begin... 3 6 9 12 15 18 21 24 27 2 4 8 10 14 16 20 30 print list end... 请按任意键继续. . .
(四)地址计算方法(查找操作)时间复杂度
假设ElemType类型数据占用C个字节存储单元,那每个元素相隔C个字节Loc(ai+1)=Loc(ai)+C
地址计算方法Loc(ai)=Loc(a1)+(i-1)*C --> 获取存储位置的时间性能为O(1)
(五)插入,删除操作时间复杂度
若是在表尾部直接插入或者删除<应用时存取数据>,则此时的时间复杂度为O(1),是最好的情况,
若是在表头进行插入或者删除,此时是最坏的情况,意味着需要移动所有的元素向前或者向后,这时的事件复杂度是O(n),
否则若是在中间进行插入删除,位置为i,需要对元素进行遍历,将元素进行后移或者前移n-i,此时的平均移动次数和最中间元素移动相同,为(n-1)/2,平均时间复杂度还是O(n)。
(六)总结
线性表的顺序存储结构, 在存(是默认放在最后位置)或读取数据时,时间复杂度都是O(1) 在插入或者删除时时间复杂度都是O(n). 所以他比较适合于元素个数不太变化,而更多是存取数据的应用。
(七)优缺点
优点
1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间。
2.可以快速的存取表中的任意位置的元素。
缺点
1.插入和删除操作需要移动大量的元素 2.当线性表长度变化较大时,难以确定存储空间的容量 3.任意造成存储空间的碎片