线性表:
具有相同数据类型的n个数据元素的有限序列,该序列所含个数叫做线性表的长度,用n (n≥0)表示;
注意:n可以等于0 ,表示线性表为空表;线性表可以是有序的,也可以是无序的。
逻辑特性:
只有1个表头元素、表头元素没有前驱,表尾元素没有后继,除表头和表尾元素之外,其它元素只有1个直接前驱,也只有一个直接后继。
1、顺序表
顺序表就是把线性表中的所有元素按照其逻辑顺序依次存储到从指定的存储位置开始的一块连续的存储空间中。
特点:
- 随机访问特性
- 占用连续的存储空间(分配好之后不可改变)
- 做插入操作的时候要移动多个元素
1.1 顺序表结构体定义
typedef struct
{
int data[maxSize]; //存放元素
int length; //存放长度
}Sqlist; //类型定义
1.2 初始化顺序表
void initList(Sqlist &L)
{
L.length = 0;
}
1.3 求指定位置元素的算法
int getElem( Sqlist L , int p , int &e)
{
if(p<0 || (p>(L.length - 1)) )
return 0;
e=L.data[p];
return 1;
}
1.4 按元素值查找算法
int findElem( Sqlist L , int e)
{
int i;
for(i=0 ; i<L.length ; ++i)
if ( e ==L.data[i])
return i;
return -1;
}
1.5 插入数据元素(原位置元素后移一位)
int insertElem(Sqlist &L , int p ,int e)
{
int i;
if(p<0 || p>L.lenth || L.length == maxsize)
return 0;
for(i = L.length-1 ; i >= p ; --i)
L.data[i+1] = L.data[i];
L.data[p] = e;
++(L.length);
return 1;
}
1.6 删除指定位置元素(后面元素前移1位)
int deleteElem(Sqlist &L , int p ,int &e)
{
inti ;
if(p<0 || p<(L.length-1) )
return 0;
e = L.data[p];
for(i = p ; i < L.length -1 ; ++i )
L.data[i] = L.data[i+1];
--L.length;
return 1;
}
2、链表
在链表存储中,每个结点不仅包含所存元素的信息,还包含元素之间逻辑关系的信息
特点:
- 不支持随机访问;
- 结点的存储空间利用率较顺序表稍低一些;
- 链表中当前位置是由其前驱结点中的地址信息所指示的,
- 而不是其相较于初始位置的偏移量来确定。
- 支持存储空间的动态分配;
- 做插入操作的时候,无需移动多个元素。
注解:
结点是内存中一片由用户分配的存储空间,只有地址来表示它的存在,没有显式的名称,因此我们在分配链表结点内存时,同时定义一个指针来存储这片空间的地址,(这个过程通俗叫指针指向结点),并且常用这个指针的名称来作为结点的名称。
LNode * A = (LNode * )malloc(sizeof ( LNode ));
用户分配了 1 个LNode型空间,也就是构造了1个LNode型的结点,这时候定义了1个名字为A的指针来指向这个结点,同时我们把A也当作这个结点的名字。
注意:
这里A命名了两个东西:1个时结点,另1个是指向这个结点的指针。
2.1 单链表(simgly Linked List)
链表再插入的时候可以达到 O(1) 的时间复杂度,但是查找一个结点或者访问特定编号的结点,则需要 O(n) 的时间。
带头结点的单链表中。头指针 head 指向头结点,头结点的值域不含任何信息;
从头结点的后继结点开始存储数据信息;
头指针head始终不等于NULL,head -> next等于NULL的时候,链表为空
不带头结点的单链表中的头指针 head 直接指向开始结点;
当head等于NULL的时候,链表为空
两者的区别:带头结点的单链表中有一个结点不存储信息(仅存储一些描述链表属性的信息,如表长等),只是作为标志,而不带头结点的单链表的所有结点都存储信息。
2.1.1 单链表结点定义
typedef struct LNode
{
int data; //数据域
char sex;
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
2.1.2 尾插法建立链表
void CreateListR(LNode *& C , int a[] ,int n )
{
Lnode *s ,*r;
int i;
C=(LNode *)maloc(sizeof(LNode));
C->next = NULL;
r = C;
for(i=0 ; i<n ; ++i)
{
s = (LNode *)maloc(sizeof(LNode));
s->data = a[i];
r->next = s;
r = r->next;
}
r->next = NULL;
}
2.1.3 头插法建立链表
void CreatListF(LNode *&C , int a[] , int n)
{
LNode *s;
int i;
C = (LNode *)maloc(sizeof(LNode));
C->next = NULL;
for(i=0 ; i<n ;++i)
{
s = (LNode *)maloc(sizeof(LNode));
s->data = a[i];
s->next = c->next;
c->next = s;
}
}
2.1.4 删除结点
要将单链表的第 i 个结点删除,必须现在单链表中找到第 i-1个结点,再删除其后继 i结点
q = p->next;
p->next = p->next;
free(q);
2.1.5 查找
为了实现查找,定义 1个结点,指针变量 p ,让它沿着链表一直走到表尾,每遇到1个新结点就检查其值是否为x,是则证明找到,不是则继续检查下1个结点。
int findAndDelete(LNode *C ,int x)
{
LNode *p,*q;
p = C;
while(p->next != NULL)
{
if(p->next->data == x)
break;
p = p->next;
}
if(p->next == NULL)
return 0;
else
{
q = p->next;
p->next = p->next-next;
free(q);
return 1;
}
}
2.2 双链表
单链表只能由开始结点走向终端结点,而不能由终端结点反向走到开始结点,
双链表就是在单链表结点上增添了一个指针域,指向当前结点的前驱。
同样,双链表也分为带头结点的双链表和不带头结点的双链表,
类似于单链表带头结点的双链表,当head->next为NULL时链表为空。
不带头结点的双链表,当head为NULL时链表为空。
2.2.1 双链表结点定义
typedef struct DLNode
{
int data; //数据域
char sex;
struct DLNode *prior; //指向前驱结点的指针
struct DLNode *next; //指向后继结点的指针
}DLNode; //定义双链表结点类型
2.2.2 尾插法建立双链表
void CreatDListR(DLNode *&L , int a[] , int n)
{
DLNode *s,*r;
int i;
L = (DLNode *)maloc(sizeof(DLNode));
L->prior = NULL;
L->next = NULL;
r = L;
for(i=0 ; i<n ;++i)
{
s = (DLNode *)maloc(sizeof(DLNode));
s->data = a[i];
r->next = s;
s->prior = r;
r = s;
}
}
2.2.3 查找结点的算法
如果找到,则p中内容是结点地址(break 结束)
如果没找到,则p遍历整个链表,p=NULL而结束
DLNode * findNode( DLNode *C , int x)
{
DLNode *p = C->next;
while( p != NULL )
{
if(p->data == x)
break;
p = p->next;
}
return p;
}
2.2.4 插入新结点
假设在双链表中p所指的结点之后插入一个结点s
s->next = p->next;
s->prior = p;
p->next = s;
s->next->prior = s;
2.2.5 删除结点
删除双链表中p结点的后继结点
q = p->next;
p->next = q->next;
q->next->prior = p;
free(q);
2.3 循环单链表
将单链表的最后1个指针域(空指针)指向链表中的第1个结点即可;
如果循环单链表是带头结点的则最后1个结点的指针域要指向头结点;
如果循环单链表不带头结点,则最后1个指针域要指向开始结点。
循环单链表可以实现从任1个结点出发访问链表中的任何结点;
带头结点的循环单链表,当head等于head->next时,链表为空;
不带头结点的循环单链表,当head等于NULL时,链表为空。
2.4 循环双链表
将终端结点的next指针指向链表中的第1个结点,将链表中第1个节点的prior指针指向终端结点,当head等于NULL时,不带头结点的循环双链表为空,带头结点的循环双链表中是没有空指针的。其空状态下,head->next和next->prior必然都等于head,所以判断其是否为空,只需要检查head->next和head->prior两个指针中的任意一个是否等于head指针即可。
以下四句任意一句为真都可以判断双链表为空
head->next == head;
head->prior == head;
head->next == head && head->prior == head;
head->next == head || head->prior == head;