数据结构——线性表知识总结
一、线性表的定义
是零个或多个具有相同类型的数据元素的有限序列。数据元素的个数定义为线性表的长度 。长度等于零时称为空表。一个非空表通常记为L = ( a 1 , a 2 ,……, a n ) 。
二、线性表的性质
每个数据元素的类型相同,每个元素之间存在唯一的顺序关系,如在英文字母表字母B的前面是字母A, 而字母B的后面是字母C。
三、线性表的抽象数据类型定义
ADT List
Data:
线性表中的数据元素具有相同类型,相邻元素具有前驱和后继关系 。
Operation:
InitList:
前置条件:线性表不存在
功能:线性表的初始化
后置条件:一个空的线性表
无输入输出
DestroyList:
前置条件:线性表已存在
功能:销毁线性表
后置条件:释放线性表所占用的存储空间
无输入输出
Length :
前置条件:线性表已存在
功能:求线性表的长度
后置条件:线性表不变
无输入,输出为线性表中数据元素的个数
Get :
前置条件:线性表已存在
功能:在线性表中取序号为 i 的数据元素
后置条件:线性表不变
输入为元素的序号 i,如果序号合法,返回序号为 i 的元素值,否则抛出异常
Locate :
前置条件:线性表已存在
输入:数据元素 x
功能:在线性表中查找值等于 x 的元素
输出:如果查找成功,返回元素 x 在表中的序号,否则返回 0
后置条件:线性表不变
Insert :
前置条件:线性表已存在
输入:插入位置 i ;待插元素 x
功能:在线性表的第 i 个位置处插入一个新元素 x
输出:若插入不成功,抛出异常
后置条件:若插入成功,表中增加了一个新元素
Delete:
前置条件:线性表已存在
输入:删除位置 i
功能:删除线性表中的第 i 个元素
输出:若删除成功,返回被删元素,否则抛出异常
后置条件:若删除成功,表中减少了一个元素
Empty :
前置条件:线性表已存在
输入:无
功能:判断线性表是否为空表
输出:若是空表,返回 1 ,否则返回 0
后置条件:线性表不变
PrintList:
前置条件:线性表已存在
输入:无
功能:按位置的先后次序依次输出线性表中的元素
输出:线性表的各个数据元素
后置条件:线性表不变
四、顺序表
特点:线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素。
作用:线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
优点:无需为表示结点间的逻辑关系而增加额外的存储空间(因为逻辑上相邻的元素其存储的物理位置也是相邻的);可方便地随机存取表中的任一元素。
缺点:插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低;由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配,因此当表长变化较大时,难以确定合适的存储规模。
五、线性表的链式存储结构
链式存储分配特点:根据线性表的长度动态的申请存储空间,以解决顺序存储中存在的存储空间难以确定的问题。
链式存储结构的实现:单链表、双向链表、循环链表。
六、指针变量
变量三要素:名字、内存地址、值。
变量的左值:内存地址。
变量右值:值。
六、单链表
通过指针把它的一串存储结点链接成一个链。
存储结点由两部分组成: data字段 、link字段
头结点:如果链表有头节点,则链式结构中的第一个节点称为头结点:其数据域可以存储一些附加信息,如链表长度;其指针域指向链表中的第一个节点。
结点的定义:由于结点的元素类型不确定,所以采用C++的模板机制。
template <typename T>
struct Node
{
T data;
Node<T> *next; //此处<T>也可以省略
};
头插法:
带头结点:
template <class T>
LinkList<T>:: LinkList(T a[ ], int n) {
first=new Node<T>; //生成头结点
first->next=NULL;
Node<T> *s;
for (int i=0; i<n; i++){
s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
s->next=first->next;
first->next=s;
}
}
不带头结点:
{
first=NULL;
for(int i=0;i<n;i++) {
s=new node<T>;
s->data=a[i];
s->next=first;
first=s;
}
}
尾插法:
带头结点:
template <class T>
LinkList<T>:: LinkList(T a[ ], int n) {
Node<T> *r,*s; //尾指针
first=new Node<T>; //生成头结点
r=first;
for (int i=0; i<n; i++) {
s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
r->next=s; r=s; //插入到终端结点之后
}
r->next=NULL; //单链表建立完毕,将终端结点的指针域置空
}
不带头结点:
node<T> *r;
head=NULL;
if(n<=0)return;
s=new node<T>;
s->data=a[0];
s->next=head;
head=s;
r=head;
for(int i=1;i<n;i++)
{
s=new node<T>;
s->data=a[i];
r->next=s;
r=s;
}
按位置插入:
1 工作指针p初始化,计数器初始化
2 查找第i-1个节点,并使工作指针p指向该节点
3 若查找不成功(P==NULL),说明位置错误,抛出位置异常,否则
3.1 生成一个元素值为x的新节点s
3.2 将s插入到p之后
查找:
按位置查找:
1 工作指针P初始化,计数器初始化
2 执行下列操作,直到p为空或指向第i个节点
2.1 工作指针后移
2.2 计数器增1
3 若p为空,则第i个元素不存在,抛出位置异常;否则查找成功,返回节点p的数据元素
删除:
八:循环链表
将单链表或者双链表的头尾结点链接起来,就是一个循环链表。
不增加额外存储花销,却给不少操作带来了方便
从循环表中任一结点出发,都能访问到表中其他结点。
q->next==first。
头插法:
template <class T>
CycleLinkList<T>:: CycleLinkList(T a[ ], int n,int k)
{
first=new Node<T>; //生成头结点
first->next=first;
Node<T> *s;
for (int i=1; i<n; i++)
{
s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
s->next=first->next;
first->next=s;
}
}
尾插法:
template <class T>
CycleLinkList<T>:: CycleLinkList(T a[ ], int n) {
first=new Node<T>; //生成头结点
Node<T> *r,*s;
r=first; //尾指针初始化
for (int i=0; i<n; i++) {
s=new Node<T>;
s->data=a[i];
r->next=s;
r=s;
}
r->next=first; //单链表建立完毕,将终端结点的指针域指向头结点
}
非循环→循环:
p=first;
while(p->next)
{
p=p->next;
}
p->next=first
九、双向链表
单链表的主要不足之处是:link字段仅仅指向后继结点,不能有效地找到前驱,双向链表增加了一个前驱指针,弥补了单链表的不足。
template <class T>
struct DNode{
T data;
DNode<T> *llink;
DNode <T>*rlink;
};
last
特点:由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。设指针p指向双链表中某一结点,则有下式成立: p->llink->rlink = p = p->rlink->llink
p之后插入结点:
q->rlink=p->rlink;
q->llink=p;
p->rlink=q;
q->rlink->llink=q;