数据结构-----复习(严蔚敏版)part1
线性表部分:
线性结构的特点:在数据元素的非空有限集中,
(1)存在唯一的一个被称作“第一个”的数据元素;
(2)存在唯一的一个被称作“最后一个”的数据元素;
(3)除第一个之外,集合中每个数据元素均只有一个前驱;
(4)除最后一个之外,集合中每个数据元素均只有一个后继。
顺序表:
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。
假设线性表的每个元素需占用l个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则线性表中第i+1个数据元素的存储位置LOC(a
i+1
)和第i个数据元素的存储位置LOC(ai)之间满足下列关系:
LOC(ai+1) = LOC(ai)+l
一般来说,线性表的第i个数据元素ai的存储位置为: LOC(ai+1) = LOC(ai)+(i-1)*l,表达式中 LOC(ai)是线性表的第一个数据元素a1的存储位置,通常称作线性表的起始位置或基地址。
线性表的这个机内表示称为线性表的顺序存储结构或顺序映像,通常,称这种存储结构的线性表为顺序表。
特点:只要确定了存储线性表的起始位置,线性表中任一数据元素都可随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。
可以用以下数据结构表示一个一维数组:
#define LIST_INIY_SIZE 100 //线性表存储空间初始分配量
#define LISTINCREMENT 10 //线性表存储空间分配增量
typedef struct{
ElemType *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配的存储容量
}SqList;
线性链表:
单链表:
特点是用一组任意的存储单元存储线性表的数据元素(这些存储单元可以是连续的,也可以是不连续的)。
因此为了表示每个数据元素与其直接后继数据元素之间的逻辑关系,对于数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。
这两部分信息组成数据元素ai的存储映像,称为节点。其中存储数据元素信息的域称为数据域,存储直接后继存储位置的域称为指针域。
n个节点结成一个链表,即为线性表(a1,a2,a3,...an
)的链式存储结构。
指针为数据元素之间的逻辑关系的映像,则逻辑上相邻的两个数据元素其存储的物理位置不要求紧邻,因此,这种存储结构为非顺序映像或链式映像。单链表存储结构:
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
头指针,指向链表中的第一个节点,若头指针为空,则表示链表为“空”表,其长度为“零”。
有时,我们在单链表的第一个节点之前附设一个节点,称为头结点,它的数据域可以不存储任何信息,或者存储线性表的长度之类的附加信息,它的指针域存储指向第一个节点的指针。若线性表为空表,则头结点的指针域为“空”。
<div style="font-family: 微软雅黑; white-space: normal; background-color: rgb(255, 255, 255);"><pre name="code" class="plain">Status GetElem_L(LinkList L ,int i, ELemType &e){
//L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
p = L->next; j = 1; //初始化,p指向第一个节点,j为计数器
while(p && j < i){ //顺指针向后查找,直到p指向第i个元素或p为空
p = p -> next; ++j;
}
if( !p || j > i) return ERROR; //第i个元素不存在
e = p -> data; //取第i个元素
return OK;
}
Status ListInsert_L(LinkList &L, int i, ElemType e){
//在带头结点单链线性表L中第i个位置之前插入元素e
p = L; j = 0;
while(p && j < i - 1){
p = p -> next;
++j;
}
if(!p || j > i - 1) return ERROR;
s = (LinkList) malloc (sizeof(LNode));
s -> data = e; s ->next = p ->next;
p -> next = s;
return OK;
}
Status ListDelete_L(LinkList &L , int i, ELemType & e){
//在呆头结点的单链线性表L中,删除第i个元素,并由e返回其值
p = L ; j = 0;
while(p -> next && j < i - 1){
p = p -> next; ++j;
}
if(!(p -> next) || j > i - 1) return ERROR;
q = p -> next; p -> next = q -> next;
e = q -> data; free(q);
}
void CreateList_L(LinkList &L , int n){
//逆位序输入n个元素的值,建立带表头节点的单链线性表L。(头插法)
L = (LinkList) malloc (sizeof(LNode));
L -> next = NULL;
for(int i = n; i > 0; --i){
p = (LinkList) malloc (sizeof(LNode));
scanf(&p -> data);
p -> next = L -> next; L -> next = p;
}
}
void MergeList_L(LinkList & La, LinkList & Lb, LinkList &Lc){
//已知单链线性表La和Lb的元素按值非递减排列
//归并La和Lb得到新的单链线性表Lc,Lc的元素也是按值非递减排列。
pa = La -> next; pb = Lb -> next;
Lc = pc =La;
while(pa && pa){
if(pa -> data <= pb -> data){
pc -> next = pa; pc = pa; pa = pa -> next;
}else{
pc -> next = pb; pc = pb; pb = pb -> next;
}
pc -> next = pa ? pa : pb;
free(Lb);
}
}
循环链表:
它的特点是表中最后一个节点的指针域指向头结点,整个链表形成一个环,由此,从表中任一节点出发均可找到表中其他结点。
循环链表的操作和线性链表基本一致,差别仅在与算法中的循环条件不是p或者p->next是否为空,而是它们是否等于头指针。
双向链表
在双向链表的节点中有两个指针域,其一指向直接后继,另一指向直接前趋,C语言可以描述为:
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode,*DuLinkList;
还有一个特性:d为指向表中某一个结点的指针,则d ->next -> prior = d ->prior -> next = d。
双向循环链表:
存在两个环,头结点的前趋指向尾结点,尾结点的后继指向头结点。
双向链表的插入操作:
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e){
//在带头结点的双链循环线性表L中第i个位置之前插入元素e,i的合法值为1 <= i <= 表长+1
if(!(p = GetElemp_Dul(L,i)))
return ERROR;
if(!(s = (DuLinkList)malloc (sizeof(DuLNode))))
return ERROR;
s -> data = e;
s -> prior = p -> prior; p -> prior -> next = s;
s -> next = p; p -> prior = s;
return OK;
}
双向链表的删除操作:
Status ListDlete_DuL(DuLinkList &L, int i, ElemType &e){
//删除带头结点的双链循环线性表L的第i个元素,i的合法值为1 <= i <=表长
if(!(p = GetElemP_DuL(L,i))) return ERROR
e = p -> data;
p -> prior -> next = p -> next;
p -> next -> prior = p -> prior;
free(p); return OK;
}