数据结构3_线性表_2
总览:
线性表:
线性表是具有相同数据类型的n(n>=0)个数据元素的有序序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,其一般表示为:
L
=
(
a
1
,
a
2
,
a
3
,
a
4
,
∗
∗
∗
,
a
n
)
L = (a1,a2,a3,a4,***,an)
L=(a1,a2,a3,a4,∗∗∗,an)
a1为表头元素,an为表尾元素,除了an其他元素都有唯一的直接后继,除了a1其他元素都有唯一前驱。
线性表的特性:
- 表中元素个数有限;
- 表中元素具有逻辑上的顺序性,表中元素有先后次序;
- 表中元素都是数据元素,每个元素都是单个元素;
- 表中元素的数据类型都相同,意味着每个元素占有相同大小的存储空间;
2 链式存储:
2.1 单链表:
定义:顺序表的链式存储称为单链表,它是通过一组任意的存储单元存储线性表中的数据元素,为了建立起数据元素之间的线性关系,对于每个链表结点,除存放元素自身的信息外,还需要存放一个指向其后继的指针,
存放数据的单元称为数据域,存放地址的单元称为指针域;
结构体定义:
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;//Lnode是节点类型 *Linklist是节点指针,顺序访问时通过指针按序访问
通过把若干个结点连接在一起就形成了一个单链表:
利用单链表可以解决顺序表需要大量存储单元的缺点,但单链表附加指针域,也存在浪费空间的缺点;
由于单链表的元素离散地分布在存储空间内,所以单链表是非随机存取(不能下标访问)的存储结构;
有时为了操作方便,还会给链表增加一个不存放数据的头结点(也可以存放表长的信息),头结点的指针域指向线性表的第一个元素;
头指针和头结点:头指针永远指向链表的第一个结点,不论有没有头结点,头结点是带头链表的第一个结点,结点内部通常不存储信息;
2.1.1 单链表的基本操作:
2.1.1.1 初始化:
单链表的初始化是指构建一个空表。先创建一个空结点,不存放数据,然后令其指针域为空;
bool InitList_L(LinkList &L){
L = new Lnode; //生成一个新节点作为头节点
if(!L) return false;
L -> next = NULL;
return true;
}
2.1.1.2 建立单链表:
创建单链表分为头插法和尾插法两种,头插法是指每次将新结点插入到头结点之后,其创建的单链表和数据输入的顺序正好相反,因此也称为逆序建表;尾插法是指每次把新结点链接到链表的尾部,其创建的单链表和数据输入顺序一致,因此也称为正序建表:
2.1.1.2.1 头插法:
代码实现:
/*头插法(逆序建表)*/
void CreateList_H(LinkList &L){
int n;
LinkList s;
L = new LNode;
L -> next = NULL;//初始化一个带头结点的空链表,同上
cout<<"请输入元素个数n:"<<endl;
cin>>n;
cout<<"请依次输入n个元素:"<<endl;
while(n--){
s = new LNode;//初始化一个空结点 c++用new分配内存空间,c用malloc,见上节
cin>>s->data;//给s数据域赋值
s->next = L->next;//把L的指针域赋值给S的指针域,也就是将本来L的后继变为S的后继
L->next = s;//L的后继变为s,插入结束
}
}
时间复杂度O(n)
2.1.1.2.2 尾插法:
代码实现:
/*尾插法(正序建表)*/
void CreatList_R(LinkList &L){
int n;
LinkList s, r;
L = new Lnode;
L -> next = NULL; //初始化头节点
r = L;
cout<<"请输入元素个数n:"<<endl;
cin>>n;
cout<<"请依次输入n个元素:"<<endl;
while(n--){
s = new LNode;
cin >> s->data;
s -> next = NULL; //新建立的节点为尾结点
r -> next = s;//前任尾节点的指针域指向新尾结点
r = s; //记录当前的尾结点
}
}
时间复杂度O(n)
2.1.1.3 按序取值:
从单链表的第一个结点出发,顺指针逐个往下搜索,知道找到第i个结点为止,否则返回最后一个结点的指针null
LinkList GetElem(LinkList L, int i){
/*也可以写成LNode *GetElem(LinkList L, int i)*/
int j = 1; //计数
LinkList p = L -> next;
if(i == 0) return L;
if(i < 1) return NULL;
while(p && j<i){
p = p->next;
j++;
}
return p;
}
时间复杂度O(n)
2.1.1.4 插入结点:
类似头插法的操作,请自行实现;
实现代码片段:
p = GetElem(L, i-1);
s -> next = p -> next;
p -> next = s;
插入到给定节点后操作的时间复杂度为O(1),若如上操作需找到i-1结点,则为O(n)
2.1.1.5 删除结点:
实现代码片段:
p = GetElem(L, i-1);
q = p -> next;
p -> next = q -> next;
//free(q) 如果时c语言开辟的内存需要手动释放
时间复杂度O(n)
还有一种方法也可:
/*p为待删除结点*/
q = p -> next;
p -> data = q -> data;
p -> next = q -> next;
//free(q)
2.1.1.6 单链表习题:
设线性表L=(a1,a2,a3,a4,***,an)采用带头结点的单链表保存,表定义如本节定义;
设计出空间复杂度为O(1),时间复杂度尽可能低的算法,重新排列列表,得到线性表L=(a1,an,a2,a(n-1),a3,a(n-2)),给出算法的设计思想和时间复杂度,并用c/c++实现;