(一)线性表的抽象数据类型定义
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...
");
for (int i = 0; i < L.length; i++)
{
printf("%d ", L.data[i]);
}
printf("
print list end...
");
}
int main()
{
SqList L;
ElemType e;
printf("1.InitList
");
InitList(&L);
printf("2.1 ListInsert 1-10
");
for (int i = 1; i <= 10; i++)
ListInsert(&L, i, i*i + 1);
printf("2.2 ListInsert 5
");
ListInsert(&L, 5, 256);
printf("2.3 ListInsert 19 Error
");
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
", e);
printf("4.ListDelete the end:");
ListDelete(&L, ListLength(L), &e);
printf("%d
", e);
PrintList(L);
printf("5.1 find element use GetElem by index(6): ");
GetElem(L, 6, &e);
printf("%d
", e);
printf("5.1 find element:256 index by LocateElem:");
printf("%d
", LocateElem(L, 256));
printf("6.Get List length:%d
", ListLength(L));
printf("7.ClearList
");
ClearList(&L);
if (ListEmpty(L)==OK)
printf("8.ListEmpty
");
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.任意造成存储空间的碎片