数据结构和算法-线性表

数据结构和算法-线性表

定义(Linear List)

线性表是具有相同数据类型的n(n>=0)个数据元素的有序序列.

顺序存储: 顺序表
链式存储: 单链表,双链表,循环链表,静态链表(数组实现)

Notes:
(1) 表中元素有限
(2) 逻辑上的顺序性
(3) 表中数据元素的类型都相同,每个元素都战友相同大小的存储空间.
(4)线性表是一种逻辑结构;顺序表和链表是值存储结构.

线性表的基本操作

InitList(&L);		//构造一个空的线性表。成功返回0出错返回-1
Length(L);			//返回L中数据元素个数
DestroyList(&L);	//销毁线性表L
ListEmpty(L);		//若L为空表,则返回TRUE,否则返回FALSE
GetElem(L,i);		//返回L中第i个数据元素的值
LocatElem(L,e);		//按值查找操作
ListInsert(&L,i,e);	//在L中第i个位置上插入新的数据元素e
ListDelete(&L,i,&e)	//删除L的第i个数据元素,并用e返回其值

顺序表的定义

线性表的顺序存储也叫作顺序表.
顺序存储: 把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中.

顺序表的实现–静态分配

#define MaxSize 10	//定义最大长度
typedef struct
{
	ElemType data[MaxSize];	//	用静态数组存放数据元素
	int length;				//	顺序表当前长度	
}SqList						//	静态表的类型定义(静态分配方式)
//Sq: sequence - 顺序,序列

void InitList(Sqlist &L){
	for (int i = 0; i< MaxSize; i++)
	{
		L.data[i] = 0;
	}
	L.length=0;				//顺序表的初始长度为0
}

顺序表的实现–动态分配

#define InitSize 10
typedef struct
{
	ElemType * data;		//指向动态分配的指针
	int MaxSize;			//顺序表的最大容量
	int length;				//顺序表的当前长度
} SeqList;

在这里插入图片描述
在这里插入图片描述

顺序表的特点

  • 随机访问,可以在O(1)时间内找到第i个元素,代码data[i-1]
  • 存储密度高,每个节点只存储数据元素
  • 容量扩展不方便,时间复杂度高
  • 插入删除数据不方便,需要移动大量元素,效率低.

顺序表的插入/删除

插入

在这里插入图片描述
在这里插入图片描述

如果是下标i位置插入元素e,则代码为:

void ListInsert(SqList &L,int i,int e)
{
	if (i < 0 || i >L.length)
		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] = e;
	L.length++;
	return true;
}

在这里插入图片描述

  • 时间复杂度:
    最好:O(1) ; 最坏:O(n) ; 平均:O(n)
删除

在这里插入图片描述
如果是下标i位置删除元素,则代码为:

void ListDelete(SqList &L,int i,int &e)
{
	if (i < 0 || i  > L.length-1)
		return false;
	e = L.data[i];
	for (int j = i; j < L.length -1; j++)
		L.data[j] = L.data[j+1];
	L.length--;
	return true;
}

在这里插入图片描述

  • 时间复杂度:
    最好:O(1) ; 最坏:O(n) ; 平均:O(n)

顺序表的查找

按位置查找
  • 静态分配顺序表
#define MaxSize 10 //定义最大长度
typedef struct{
	ElemType data[MaxSize]; 	//用静态的“数组”存放数据元素
	int length; 				//顺序表的当前长度
}SqList; 			//顺序表的类型定义(静态分配方式)

ElemType GetElem(SqList L, int i){
	return L.data[i-1];
}
  • 动态分配顺序表
#define InitSize 10 //顺序表的初始长度
typedef struct{
	ElemType *data; 			//指示动态分配数组的指针
	int MaxSize; 				//顺序表的最大容量
	int length; 				//顺序表的当前长度
} SeqList; 			//顺序表的类型定义(动态分配方式)

//指针依然可以使用数组一样下标的方式来访问,系统会自动按照指针指向的数据类型的大小来向后移动
ElemType GetElem(SqList L, int i){
	return L.data[i-1];
}
  • 时间复杂度:
    O(1)
    因为顺序表的各个数据元素在内存中连续存放,因此可以更具起始地址和数据元素的大小立刻找到第i个元素。即“随机存取”特性
按值查找
#define InitSize 10 	//顺序表的初始长度
typedef struct{
	ElemType *data; 	//指示动态分配数组的指针
	int MaxSize;		//顺序表的最大容量
	int length;		    //顺序表的当前长度
} SeqList; 		//顺序表的类型定义(动态分配方式)

//在顺序表L中查找第一个元素值等于e的元素,并返回其位序
int LocateElem(SeqList L,ElemType e){
	for(int i=0;i<L.length;i++)
	{
		if(L.data[i]==e)
			return i+1; 	//数组下标为i的元素值等于e,返回其位序i+1
	}
	return 0;				//退出循环,说明查找失败
}

  • 时间复杂度:
    最好:O(1) ; 最坏:O(n) ; 平均:O(n)

单链表的定义

优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针

  • 代码实现单链表节点
    在这里插入图片描述
  • 申请一个新的结点
struct LNode * p = (struct LNode *) malloc(sizeof(struct LNode));
  • 使用typedef关键字
    在这里插入图片描述
//typedef <数据类型> <别名>
int  x = 1;
int* p;
//等价于
zhengshu x = 1;
zhengshuzhizhen p;
//同理
typedef struct LNode LNode;
typedef struct LNode *LinkList;
LNode * p = (LNode *) malloc(sizeof(LNode));

要表示一个单链表时,只需声明一个头指针 L ,指向单链表的第一个结

在这里插入图片描述
或:
在这里插入图片描述

代码可读性更强

单链表的实现-不带头结点的单链表

在这里插入图片描述
在这里插入图片描述

单链表的实现-带头结点的单链表(使用更广泛)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

单链表的插入/删除

按位置插入(带头结点)

在这里插入图片描述
Tips:
(1) 最后两句代码一定不能颠倒!
(2) 要在第i个位置插入节点。需要找到的是第i-1个节点,也就是从头结点开始往后next的次数为i-1次。

  • 平均时间复杂度:
    O(n)
按位置插入(不带头结点)

在这里插入图片描述
Tips:
(1) 不带头结点的代码书写不方便,推荐用带头结点的。
(2) 注意审题!

指定节点后插入操作

在这里插入图片描述

  • 平均时间复杂度:
    O(1)
指定节点前插入操作
  • 核心思想:
    构造一个新的节点s插入到节点p的后面,然后把节点p和节点s的值交换,就可以实现把新节点s插入到p之前的操作。

  • 若给定的时元素值e
    在这里插入图片描述

  • 若给定的时元素节点s
    在这里插入图片描述

  • 时间复杂度
    O(1)

按位置删除

在这里插入图片描述

  • 平均时间复杂度:
    O(n)
指定节点的删除
  • 核心思想:
    (1) 找到要删除节点p的后续节点q
    (2) 把q节点的数据域赋给p
    (3) 让p直接指向q的后继节点
    (4) 释放q节点内存在这里插入图片描述
  • 时间复杂度
    O(1)

Tips:
(1) 如果要删除的节点是最后一个节点,此时这种巧办法不可行!还得使用笨办法。

单链表的查找

按位查找(带头结点)

在这里插入图片描述

  • 平均时间复杂度:
    O(n)
按值查找

在这里插入图片描述

单链表的建立(带头节点)

尾插法
LinkList List_TailInsert(LinkList &L){ 		//正向建立单链表
	int x; //设ElemType为整型
	L=(LinkList)malloc(sizeof(LNode)); 		//建立头结点
	LNode *s,*r=L; 							//r为表尾指针
	scanf("%d",&x); 						//输入结点的值
	while(x!=9999){ 						//输入9999表示结束
		s=(LNode *)malloc(sizeof(LNode));
		s->data=x;
		r->next=s;
		r=s; 								//r指向新的表尾结点
		scanf("%d",&x);
	}
	r->next=NULL; 							//尾结点指针置空
	return L;
}
头插法
LinkList List_HeadInsert(LinkList &L){ 		//逆向建立单链表
	LNode *s;
	int x;
	L=(LinkList)malloc(sizeof(LNode)); 		//创建头结点
	L->next=NULL; 							//初始为空链表
	scanf("%d",&x); 						//输入结点的值
	while(x!=9999){ 						//输入9999表示结束
		s=(LNode*)malloc(sizeof(LNode)); 	//创建新结点
		s->data=x;
		s->next=L->next;
		L->next=s; 							//将新结点插入表中,L为头指针
		scanf("%d",&x);
	}
	return L;
}

Tips:
(1) 重要应用:链表的实际顺序和输入的顺序是相反的,链表的逆置!

双链表

双链表的定义

在这里插入图片描述

初始化(带头指针)在这里插入图片描述

判断双链表是否为空.等价于判断(L.next == NULL)?,即头节点后是否有下一个节点.

双链表的插入

在这里插入图片描述
注意顺序!!!

双链表的删除

在这里插入图片描述
别忘记判断p是否为最后一个节点,即其next节点是否为NULL!

循环列表

循环单链表
  • 定义
    在这里插入图片描述

  • 初始化
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

循环双链表
  • 表头节点的prior指向标为节点; 表尾节点的next指向头结点

  • 初始化
    在这里插入图片描述

  • 判空和表尾节点的判断同循环单链表

  • 插入
    在这里插入图片描述
    与双链表的区别在于: 不再需要额外判断p节点是不是最后一个节点了.

静态链表

定义
  • 核心思想: 使用数组下标来指明下一个元素的位置.
    在这里插入图片描述
    在这里插入图片描述
    两种写法都可以.

  • 使用:
    在这里插入图片描述
    或:
    在这里插入图片描述

静态链表的操作
  • 查找:
    从头结点出发挨个往后遍历结点

  • 插入位序为 i 的结点: 点是否为空?
    (1) 找到一个空的结点,存入数据元素
    (2) 从头结点出发找到位序为 i-1 的结点
    (3) 修改新结点的 next
    (4) 修改 i-1 号结点的 next

  • 删除某个结点:
    (1) 从头结点出发找到前驱结点
    (2) 修改前驱结点的游标
    (3) 被删除结点 next 设为 -2(初始化时亦应该设置各节点为此值)

顺序表VS链表比较

  • 逻辑结构: 都属于线性表,都是线性结构
  • 存储结构:
    <顺序表>
    优点:支持随机存取、存储密度高
    缺点:大片连续空间分配不方便,改变容量不方便
    <链表>
    优点:离散的小空间分配方便,改变容量方便
    缺点:不可随机存取,存储密度低
  • 基本操作
    <创建>
    顺序表: 需要预分配大片连续空间。
    顺序表-静态数组: 容量不可改变
    顺序表-动态数组: 容量可改变,但需要移动大量元素,时间代价高
    链表: 只需分配一个头结点(也可以不要头结点,只声明一个头指针),之后方便拓展
    <销毁>
    顺序表: 修改 Length = 0
    顺序表-静态数组: 系统自动回收空间
    顺序表-动态数组: 需要手动 free
    链表: 依次删除各个结点(free)
    <增删>
    顺序表: 插入/删除元素要将后续元素都后移/前移;时间复杂度 O(n),时间开销主要来自移动元素
    链表: 插入/删除元素只需修改指针即可;时间复杂度 O(n),时间开销主要来自查找目标元素
    <查找>
    顺序表: 按位查找:O(1);按值查找:O(n),若表内元素有序,可在O(log 2 n) 时间内找到
    链表: 按位查找:O(n),按值查找:O(n)
  • 总结:
    表长难以预估、经常要增加/删除元素 ——链表
    表长可预估、查询(搜索)操作较多 ——顺序表
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值