是指
具有相同特性的数据元素的一个有限序列
可以含有相同值的元素
长度
所含元素的个数
元素
成线性关系
作用
一般表示
(a1,a2,a3…)
特性
有穷性
一致性
序列性:
- 所有元素的相对位置是线性的,存在唯一的开始元素、终端元素
- 每个元素有唯一的前驱元素、后继元素
- 各元素在线性表中的位置只取决于序号
- 线性表中可以存在两个值相同的元素
基本运算
ListLength(L)=5
顺序存储结构
按逻辑顺序,依次存储到存储器中一片连续存储空间中
顺序表类型定义
typedef struct {//用数组实现顺序存储结构
ElemType data[MaxSize];//elemtype为抽象类型
//data成员存放元素
int length;
}SqList;
注意:逻辑位序和物理位序相差1
算法参数说明
1、顺序表指针?
传递的不是顺序表本身,而是顺序表指针
*2、顺序表指针引用SqList &L?
void CreateList(SqList *&L,ElemType a[],int n)
为什么要加&:
将执行结果回传给实惨
输出型参数均使用&,不论参数值是否改变
顺序表基本运算
//建立顺序表
void InitList(SqList*& L) {
L = (SqList*)malloc(sizeof(SqList));//为顺序指针L分配线性表的空间
L->length = 0;
}
//销毁线性表
void DestroyList(SqList*& L) {
free(L);
}
//判断是否为空表
bool ListEmpty(SqList* L) {
return (L->length == 0);//是双==,不是=
}
//求线性表长度
int ListLength(SqList* L) {
return(L->length);
}
//输出线性表
void DispList(SqList* L) {
for (int i = 0; i < L->length; i++)//挨个元素输出
{
printf("%d", L->data[i]);
}
printf("\n");
}
//求某个元素值
bool GetElem(SqList* L, ElemType& e,int i) {//bool型函数,返回t或f,但是函数中进行了值传递
if (i<1||i>L->length)//检测合法性
{
return false;
}
e = L->data[i - 1];//引用传参,将该元素传出放在e中
return true;
}
//按元素值查找
int LocateElem(SqList* L, ElemType e) {
int i = 0;
while (i < L->length&&L->data[i]!=e)//合法性和元素值一起检查
{
i++;
}
if (i >= L->length)
return 0;
else
{
return i + 1;//因为数组从0开始,顺序表从1开始
}
}
//插入数据元素,bool型
bool ListInsert(SqList*& L, int i, ElemType e) {
int j;
if (i<1 || i>L->length + 1)//越界检测
return false;
i--;//
for (j=L->length;j>i;j--)
{
L->data[j] = L->data[j - 1];//将i-1往后移动
}
L->data[i] = e;//插入元素
L->length++;//长度增加
return true;
}
//删除数据元素,bool型
bool ListDelete(SqList*& L, int i, ElemType& e) {
int j;
if (i<1 || i>L->length)
return false;
i--;//将逻辑序号转化为物理序号
e = L->data[i];
for (j = i; j < L->length; j++)//实际是移动i下标,前移
L->data[j] = L->data[j + 1];
L->length--;
}
bool型:判断空表/插入/删除
引用&传参:建表/销毁/插入/删除
此算法在移动元素上耗时最多
i=n+1时,移动次数为0
i=1时,移动次数为n
插入算法的平均时间复杂度是O(n)
i=n时,移动次数为0
i=1时,移动次数为n-1
删除算法的平均时间复杂度是O(n)
实际问题1
方法一:用原表,删一些元素(根据下标直接换人)
**思想:**删除=保存剩下的
k的作用:
- 记录元素个数,随时更新
- 成为新的元素下标,便于直接用原有表
方法二:用原表,前移元素(踢走不要的)
例题2
- 此题不要求有大小排序
- 所以直接交换即可
方法一:双向查找,交换位置
方法二:前后依次查找,换到空位上
时间复杂度都是O(n)
关于交换元素:
当需要连续交换多个元素时:
==》效率低
而:
tmp=a;
a=b;
b=c;
c=tmp;
==》效率高
单链表
单链表的优点
- 对于每个结点无需特殊处理
- 对于空/非空无需特殊处理
存储密度:
数据本身/总空间大小
顺序表存储密度=1,链表存储密度<1
单链表中节点类型的定义
单链表特点:
访问过一个节点后,只能接着访问后继节点,不能访问前趋节点
==》引入双链表
基本运算
删除:
p->next=p->next->next;
插入:
s->next=p->next;
p->next=s;
建立单链表:
1、头插法
链表元素顺序和数组本身顺序相反
(节点顺序与逻辑次序相反)
//1、头插法
void CreatListF(LinkList*& L, ElemType a[], int n) {
LinkList* s;//单链表的节点指针s,s是指针!!!
int i;
L = (LinkList*)malloc(sizeof(LinkList));//为表分配空间
L->next = NULL;//创建头节点,next指向空
for (i = 0; i < n; i++)
{
s = (LinkList*)malloc(sizeof(LinkList));//为节点分配空间
s->data = a[i];//s是节点指针,有data和next两个域,其中next是指针
s->next = L->next;//即插入的原操作
L->next = s;//将s指针插到表头指针后面
}
}
头插法特点:插入到头结点之后,原来的头前面
2、尾插法
L是头结点,s是数据节点
//2、尾插法
void CreateListF2(LinkList*& L, ElemType a[], int n) {
LinkList*s,*r;//*r是一个新的指针
int i;
L = (LinkList*)malloc(sizeof(LinkList));//创建头结点
r = L;//先指向头结点
for ( i = 0; i < n; i++)
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;//将*s插入*r之后
r = s;//
}
r->next = NULL;
}
销毁线性表:
需要逐一释放全部节点