一、线性表的定义和特点
n(n>=0) 个数据特性相同的元素构成的有限序列,称为线性表。
当n=0时,称为空表。
非空的线性表或线性结构,其特点(定义、判断依据)
- 存在唯一的一个被称作 "第一个" 的数据元素。(开始结点)
- 存在唯一的一个被称作 "最后一个" 的数据元素。(终端结点)
- 除第一个元素之外,结构中的每个数据元素均只有一个前驱。
- 除最后一个元素之外,结构中的每个数据元素均只有一个后继。
二、线性表的类型定义
- InitList(&L):构造一个空的线性表L。(初始化)
- ListLength(L):返回L中数据元素的个数。(求长度)
- GetElem(L,i,&e):用e返回L中第 i 个数据元素的值。(读取)
- LocatElem(L,e):返回 L 中第1值与 e 相同的元素在 L 中的位置。
- ListInsert(&L,i,e):在L中第 i 个位置之前插入新的数据元素e,L的长度加1。
- ListDelete(&L,i):删除L的第 i 个数据元素,L的长度减1。
三、线性表的顺序表示和实现
顺序表是连续的。
它的特点是逻辑上相邻的数据元素,其物理位置也是相邻的。
优点:查找。 缺点:插入、删除。
1、初始化
Status InitList(SqList &L) //构造一个空的顺序表L
{
L.elem = new ElemType [MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的数据空间
if ( !L.elem ) exit ( OVERFLOW ); //存储分配失败退出
L.length = 0; //空表长度为0
return OK;
}
时间复杂度为O(1)。
2、取值
Status GetElem(SqList L,int i,ElemType &e)
{
if ( i < || i > L.lengthen ) return ERROR; //判断i值是否合理,若不合理,返回ERROR
e = L.elem [i-1]; //elem[i-1] 单元存储第i个数据元素
return OK;
}
时间复杂度为O(1)。
3、查找
int LocateElem(SqList L,ElemType e) //在顺序表L中查找值为e的数据元素,返回其序号
{
for ( i = 0 ;i < L.length; i++ ) //查找成功,返回序号i+1
if ( L.length[i] == e) //查找失败,返回0
return i+1
return 0;
}
平均时间复杂度为O(n)。
4、插入
Status ListInsert(SqList &L,int i,ElemType e)
{ //构造一个空的顺序表L中第i个位置插入新的元素e
if ((i < i) || (i > L.length + 1)) return ERROR; //i值不合法
if (L.length == MAXSIZE) return ERROR; //当前存储空间已满
for(j = L.length - 1;j > = i - 1;j--)
L.elem[j+1] = L.elem[j]; //插入位置及之后的元素后移
L.elem[i-1] = e; //将新元素e放入第i个位置
++L.length; //表长加1
return OK;
}
平均时间复杂度为O(n)。
5、删除
Status ListDelete(SqList &L,int i)
{ //在顺序表L中删除i个元素
if ((i < i) || (i > L.length + 1)) return ERROR; //i值不合法
for(j = i;j < = L.length-1; j--)
L.elem[j-1] = L.elem[j]; //被删除元素之后的元素前移
--L.length; //表长减1
return OK;
}
平均时间复杂度为O(n)。
四、线性表的链式表示和实现
1.单链表的定义
用一组任意的存储单元存储线性表的数据元素。(存储单元可以是连续的,也可以不连续)
用每个数据元素所带的指针来确定其后继元素的存储位置。
空表first指向数据域为空,last指向指针域为空。
优点:插入、删除。 缺点:访问。
2.单链表的初始化
Status InitList(SqList &L) //构造一个空的顺序表L
{
L=new LNode; //生成新结点作为头结点,用头指针L指向头结点
L->Next=Null; //头结点的指针域置空
return OK;
}
3.单链表的取值
Status GetElem(SqList L,int i,ElemType &e)
{
P=L->next; //初始化,P指向首元结点
j=1; //计数器j初赋值为1
While((p!=NULL)&&(j<i))
{
p=p->next; //p指向下一个结点
j++;
}
return OK;
}
平均时间复杂度为O(n)。
4.单链表的查找
int LocateElem(SqList L,ElemType e)
{
p=L->next;
while(p && p->date! = e)
p=p->next;
return p;
}
平均时间复杂度为O(n)。
5.单链表的插入
先连后断
链表插入的核心语句:
Step 1:s->next=p->next;
Step 2:p->next=s
6.单链表的删除
删除动作的核心语句(要借助辅助指针变量 q):
q = p->next; //首先保存 b 的指针,靠它才能找到 c;
p->next=q->next; //将 a、c 两结点相连,淘汰 b 结点;
free(q) ; //彻底释放 b 结点空间
7.创建单链表的优点
对第一个位置的操作同其他位置一样,无须特殊处理。
无论链表是否为空,其头指针指向头结点的非空指针不会出现空表。
8.单链表的前插法
- 创建一个只有头结点的空链表
- 生成一个新结点*p
- 输入元素值赋给新结点*p的数据域
- 将新结点*p插入到头结点之后
9.单链表的后插法
- 创建一个只有头结点的空链表
- 尾指针r初始化,指向头指针
- 生成一个新结点*p
- 输入元素值赋给新结点*p的数据域
- 将新结点*p插入到尾结点*r之后
- 尾结点*r指向新的尾结点*p
10.循环链表
- 首尾相接的链表
- 最后一个结点指向了表头结点
- 没有NULL简化操作
- 往往加入表头结点
主要语句段:
p = B->next->next ;
B->next = A ->next ;
A->next = p ;
时间复杂度为O(1)
11.双向链表的插入操作
s->next = p ; p->prior->next = s ;
s->prior = p ->prior ; p->prior = s ;
12.双向链表的删除操作
p ->prior->next = p->next ;
p->next->prior = p ->prior ;
五、内容补充
1.每个存储结点都包含两部分:数据域和指针域。
2.在单链表中,除了首元结点外,任一结点的存储位置由其直接前驱结点的指针域的值指示。
3.在链表中设置头结点有什么好处?
头结点即在链表的首元结点之前附设的一个结点,该结点的数据域可以为空,也可存放表长度等附加信息,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便。
4.如何表示空表?
(1)无头结点时,当头指针的值为空时表示空表;
(2)有头结点时,当头结点的指针域为空时表示空表。
5.链表的数据元素有两个域,不再是简单数据类型,编程时该如何表示?
因每个结点至少有两个分量,且数据类型通常不一致,所以要采用结构数据类型。
6. 顺序存储和链式存储的区别和优缺点?
顺序存储时,逻辑上相邻的数据元素,其物理存放地址也相邻。顺序存储的优点是存储
密度大,存储空间利用率高;缺点是插入或删除元素时不方便。
链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,
另一部分存放表示结点间关系的指针。链式存储的优点是插入或删除元素时很方便,使用
灵活。缺点是存储密度小,存储空间利用率低。
◆ 顺序表适宜于做查找这样的静态操作;
◆ 链表宜于做插入、删除这样的动态操作。
◆ 若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;
◆ 若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。