线性表
线性表:0个或多个数据元素的有限序列,记{a1,a2,...,ai-1,ai,...an}
长度为n,为0记为空表
线性表应该具有的操作:
ADT List
Data
{a1,a2,...,an},每个元素类型为DataType,除第一个元素外,每一个元素有且只有一个直接前驱元素,除最后一个元素外有且只有一个直接后继元素。
Operation
InitList(*L); 初始化操作,建立一个空的线性表L
ListEmpty(L); 若线性表为空,返回true,否则返回false
ClearList(*L); 将线性表清空
GetElem(L,i,*e); 将线性表L中的第i个位置元素返回给e
LocateElem(L,e); 在线性表中查找与e值相等的值,若查找成功返回序号,若失败,返回0
ListInsert(*L,i,e); 在线性表L中第i个位置插入元素e
ListDelete(*L,i,*e); 删除线性表中第i个位置元素,并用e返回其值
ListLength(L); 返回线性表L的元素个数
endADT
线性表的顺序存储结构
用一段地址连续的存储单元依次存储线性表的数据元素
在C/C++中表示为数组
*注意区分数组长度与线性表长度,前者指开辟的存储空间的大小,基本不变;后者指表中元素数据的个数,随着插入和删除是不断变化的。
主要优点:可快速存取表中任意位置的元素
主要缺点:插入和删除需要移动大量的元素
线性表的链式存储结构
节点 = 数据域+指针域
若节点只包含一个指针域,即只知后者,则叫做单链表
头结点:在第一个存储数据的节点之前设置一个头结点,用于存储长度等附加信息,但是并不是必须的;
头指针:指向链表中第一个节点的存储位置,若有头结点,则指向头结点,若无头结点,则指向第一个数据节点,该指针是必须的;
最后一个节点的指针域指向空。
*分辨头节点、头指针、第一个数据节点、最后一个数据节点(笔者自己称为尾节点)以及尾指针等概念;
单链表
节点构造:
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
元素读取(读取第i个元素数据):
Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p; //声明指针
p = L->next; //L为头指针,p指向头节点下一个,即第一个数据
j = 1 //j为计数器,当前p指向第一个数据
while(p&&j<i) //p不为空且计数j没超过i
{
p = p->next; //指向下一个节点
++j; //计数++
}
if(!p||j>i)
return error; //返回错误,即第i个数据不存在
*e = p->data; //给e赋值
return ok;
}
单链表插入(插入到第i个数据之前)
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j;
LinkList p,s; //定义两个节点指针
p = *L; //p指向头指针,注意读取时的代码是指向第一个数据
j = 1; //计数
while(p&&j<i) //p非空且计数少于i
{
p = p->next;
++j;
} //上述while结束,此时p指向第i-1个节点
if(!p||j>i) //没有第i个节点
return error;
s = (LinkList)malloc(sizeof(Node)); //动态生成新节点
s->data = e; //插入节点数据部分赋值
s-next = p->next; //插入节点指针部分指向第i个节点
p->next = s; //第i-1节点指针部分指向插入节点s
return ok;
}
单链表删除(删除第i个数据)
Status ListInsert(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q; //定义两个节点指针
p = *L; //p指向头指针,注意读取时的代码是指向第一个数据
j = 1; //计数
while(p->next&&j<i) //p->next非空且计数少于i,注意此处与插入不同
{
p = p->next;
++j;
} //上述while结束,此时p指向第i-1个节点
if(!p||j>i) //没有第i个节点
return error;
q = p->next; //q记录第i个节点
p->next = q->next; //将p节点(第i-1)的指针部分指向第i+1个节点
*e = q->data; //将第i个节点的数据给e
free(q); //释放q节点占用的内存
return ok;
}
整表创建(头插入)
void CreatListHead(LinkList *L,int n)
{
LinkList p; //创建节点
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node)); //创建头节点、头指针
(*L)->next = NULL; //头节点指向空,即空表
for(i=0;i<n;i++) //创建n个节点
{
p = (LinkList)malloc(sizeof(Node)); //创建节点
p->data = rand()%100+1; //节点数据部分赋值
p->next = (*L)->next; //节点插入到头结点和第一个数据之间
(*L)->next = p;
}
}
整表创建(尾插入)
void CreatListHead(LinkList *L,int n)
{
LinkList p,r; //创建节点
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node)); //创建头节点、头指针
r = *L; //*r指向尾部的节点
for(i=0;i<n;i++) //创建n个元素
{
p = (LinkList)malloc(sizeof(Node)); //创建节点
p->data = rand()%100+1; //节点数据部分赋值
r->next = p; //旧尾部的指针部分指向新节点
r = p; //更新尾部
}
r->next = NULL; //链表结束,尾部指向空
}
整表删除
Status ClearList(LinkList *L)
{
LinkList p,q;
p = (*L) -> next; //p指向第一个数据节点
while(p) //p非空
{
q = p -> next; //q为p下一个节点
free(p); //释放p;
p = q; //跟新p为第一个节点
}
(*L) -> next = NULL; //头节点指针指向空
return ok;
}
单链表与顺序存储结构比较
主要优势:插入与删除更方便;不需要固定分配空间,元素个数也不受限制。
主要缺点:查找的复杂度增加到了O(n)。