一、线性表的定义
线性表是具有相同数据类型的n个数据元素的有限序列。
其中第一个元素称为表头元素,最后一个元素称为表尾元素。
除表头元素外,每个元素都有自己的直接前驱。
除表尾元素外,每个元素都有自己的直接后继。
二、线性表的特点
- 元素个数有限
- 逻辑上具有顺序
- 元素都是数据元素
- 每个元素数据类型相同,意味着占有相同大小的存储空间
- 具有抽象性
三、线性表的顺序表示(顺序表)
线性表的顺序存储又称顺序表(用一组连续的存储单元依次存储线性表中的元素)。
它的特点是逻辑上相邻的两个元素在物理位置上也相邻。
3.1顺序表存储类型
typedef struct seqList{
ElemType data[MaxSize]; //顺序表元素
int length; //顺序表当前长度
}seqList;
3.2顺序表的插入操作 O(n)
bool LinkInsert(seqList *L,int i,ElemType e)
{
if(i < 1 || i > L-> length + 1) //插入的位置不能小于1或者大于当前长度下下个位置
return false;
if(L->length >= MaxSize) // 表示存储空间已满
return false;
for(int j = L->length;j >= i; j--)
{
L->data[j] = L->data[j-1]; //依次赋值
}
L->data[i-1] = e; //插入位置赋值
L->length ++;//长度+1
return true;
}
时间复杂度为O(n)
3.2顺序表的删除操作 O(n)
bool ListDelete(seqList *list,int i)
{
//判断元素出界
if( i < 1 || i > list->length)
return false;
for(int j = i;j<=list->length-1;j++)
{
list->data[j-1] = list->data[j];
}
list->length--;
return true;
}
时间复杂度为O(n)
3.2顺序表的按值查找 O(n)
int LocateElem(seqList *L,ElemType e)
{
int i;
for(i = 0; i<L->length;i++)
{
if(L->data[i] == e)
return i+1;
}
return 0;
}
3.3顺序表总结
顺序表采用**随机存取**的方式,查找某一个位置的值的时间复杂度为O(1),但删除和插入需要移动大量的元素,其时间复杂度均为O(N)四、线性表的链式表示
4.1单链表定义
通过一组任意的存储单元来存放线性表中的数据元素,每个链表节点除了自身信息外,还需存放一个指向后继的指针。
typedef struct LNode
{
ElemType data; //数据
struct LNode *next; //指针
}LNode,*LinkList;
4.1.1头插法建立单链表(有头结点)
/*头插法建立链表
* 1.设置头结点指向NULL
* 2.输入数据域
* 3.将插入节点的后继节点指向头结点的后继节点 s->next = L->next
* 4.将头结点的后继节点指向 插入节点 L->next = s
* */
LinkList List_HeadInsert(LinkList L)
{
LNode *s ;
int x; // 数据域
L = (LinkList) malloc(sizeof(LNode));
L->next = NULL;
scanf("%d",&x);
while(x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x ;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
4.1.2头插法建立单链表(无头结点)
LinkList List_HeadInsertWithoutHeadNode(LinkList L)
{
LNode *s; //工作指针
int x; //数值
L = (LinkList) malloc(sizeof(LNode));
L->next = NULL;
scanf("%d",&x);
L->data = x;
while(x!=9999)
{ scanf("%d",&x);
if(x == 9999)
break;
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L;
L = s;
}
}
4.2.1尾插法建立单链表(有头结点)
/*
* 尾插法建立链表
* 1.建立头结点
* 2.建立插入节点 以及 <尾指针节点指向 L >
* 3.输入数据域
* 4.将尾指针 的 后继节点指向 插入节点
* 5.将尾指针指向插入节点
* 6.将尾指针后继节点指向NULL
*/
LinkList List_TailInsert(LinkList L)
{
int x;
L = (LinkList)malloc(sizeof(LNode));
scanf("%d",&x);
LNode *s; LNode *r = L;
while(x != 99990)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x ;
r->next = s;
r = s ;
scanf("%d",&x);
}
r->next = NULL;
return L;
}
4.2.1尾插法建立单链表(无头结点)
LinkList List_TailInsertWithoutHeadNode(LinkList L) {
L = (LinkList) malloc(sizeof(LNode));
int x;
LNode *s;
scanf("%d", &x);
L->data = x;
LNode *q ;
q = L;
while (x != 9999) {
scanf("%d",&x);
if (x == 9999)
break;
s = (LNode *) malloc(sizeof(LNode));
s->data = x;
q->next = s;
q = s;
}
q->next = NULL;
return L;
}
4.3按序号查找结点值 O(n)
/*
* 按序号查找节点值
* 1.判断查找的位序是否非法
* 2.建立工作指针 以及位序变量
* 3.进入循环当 工作指针的next非空 并且 位序变量小于所要查找位序的值
*/
LNode *GetElem(LinkList L,int i)
{
if(i == 0 )
return L;
if(i < 1)
{
return NULL;
}
int j = 1; //当前序号
LNode *s = L ->next;
while(s != NULL && j < i)
{
s = s->next;
j ++ ;
}
return s ;
}
4.2按值查找表结点 O(n)
/*
* 按值查找 表节点
*
*/
LNode *LocateElem(LinkList L,ElemType e)
{
LNode *s = L->next;
while(s != NULL && s->data != e)
{
s = s->next;
}
return s ;
}
4.3 插入结点操作
/*
* 在位序x的地方插入节点
* 1.判断插入位置是否非法
*
*/
void InsertNode(LinkList L,int x,ElemType e)
{
if( x < 1 || x >Length(L) + 1)
{
return;
}
LNode *s = GetElem(L,x-1);
LNode *q = (LNode *)malloc(sizeof(LNode));
q->data = e;
q ->next = s->next;
s->next = q;
}
4.4 删除结点操作
void DeleteNode(LinkList L,int x)
{
if(x < 1 || x > Length(L))
return;
LNode *s = GetElem(L,x-1);
LNode *p =s->next;
s->next = p->next;
free(p);
}
4.5 求表长(时间复杂度O(n))
int Length(LinkList L)
{
int len = 0;
LNode *p = L->next;
while (p != NULL)
{
p = p ->next;
len ++;
}
return len;
}
五、双链表
5.1双链表定义
typedef struct DNode{
ElemType data;
struct DNode * prior,*next;
}DNode,*DLinklist;
5.2插入操作(图示)
5.3删除操作(图示)
六、循环链表
6.1循环单链表(图示)
与单链表区别在于最后一个指针指向的是头结点
6.2循环双链表(图示)
当没有元素时L的首指针和尾指针都指向自己
六、静态链表(预先分配一块连续的内存空间)
借助数组描述链式存储结构,以next == -1作为结束标志
typedef struct {
ElemType data;
int next;
}SLinkList[MaxSize]
七 总结
- 顺序表既可以随机存取,也可以顺序存取,区分于链表的顺序存取。(注:线性表的顺序存储结构是一种随机存储的存储结构)
- 顺序存储逻辑上相邻的物理上也相邻,而链式存储逻辑上相邻的物理上未必相邻
- 按序查找时间复杂度顺序表优于链表(O(1) and O(n)
- 插入删除操作时,顺序表需要移动大量元素,而链表只需要修改相应的指针即可
八 错题
8.1单链表操作的时间复杂度
在一个长度为n的带头结点的单链表h上,设有尾指针r,则执行(B)操作与链表的表长有关。
A删除单链表第一个元素O(1)
B删除单链表中的最后一个元素O(n)
C在单链表第一个元素前插入一个新元素O(1)
D在单链表最后一个元素后插入一个新元素O(1)
B虽然有尾指针但是删除之后还要寻找倒数一个元素使他的指针指向NULL)
8.2静态链表的指针问题
静态链表中指针表示的是(C)
A.下一个元素的地址
B.内存储器地址
C下一个元素在数组中的位置
D左链或右链指向的元素的地址
(指针又称游标,指向的是结点的相对地址(数组下标)
8.3各种链表操作的时间复杂度
设对n(n>1)个元素的线性表的运算只有4种:删除第一个元素;删除最后一个元素;在第一个元素之前插入新元素;在最后一个元素之后插入新元素,则最好使用(C)
A.只有尾结点指针没有头结点指针的循环单链表
B.只有尾结点指针没有头结点指针的非循环双链表
C.只有头结点指针没有尾结点指针的循环双链表
D.既有头结点指针又有尾结点指针的循环单链表