数据结构与算法基础01
王卓老师的数据结构与算法第一部分的笔记
线性表
-
具有相同特性的数据元素的一个有限序列。
-
由n(n>=0)个数据元素(节点)所组成的有限序列。
-
同一线性表中的元素必定具有相同特性,数据元素间的关系是线性关系。
一元多项式的运算
每一项的系数存一个数组。
数组下标来描述指数。
如果存储稀疏多项式
两个表,一一对应,分别记录下标和指数。
稀疏多项式的运算:
-
创建一个新数组c
-
从头遍历比较a和b的每一项
- 指数相同,对应系数相加,若其和不为0,则在C中增加一个新项
- 指数不相同,则将指数较小的项复制到C中
-
一个多项式已遍历完毕时,将另一个剩余项依次复制到c中即可
顺序存储结构存在的问题
- 存储空间分配不灵活
- 运算的空间复杂度高
线性表的顺序存储结构
-
把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
-
简言之,逻辑上相邻,物理上也相邻。
-
线性表的第一个数据元素a1的存储位置,称作线性表的起始位置或基地址。
-
线性表顺序结构占用一片连续的存储空间。知道某个元素的存储位置就可以计算其他元素的位置。
顺序表长度可变的方法
- 用宏定义来定义长度,然后做一维数组。
- 数组的类型,用结构体事先定义好了,这样数组的类型也是可以改变的。
用指针分配数组的空间可以相当于动态分配
C语言的内存动态分配
- malloc(m)函数,开辟m字节长度的地址空间,并返回这段空间的首地址
- sizeof(x)运算,计算变量x的长度
- free(p)函数,释放指针p所指变量的存储空间,即彻底删除一个变量。
引用类型作形参的三点说明
(1)传递引用给函数与传递指针的效果是一样的
(2)引用类型作形参,在内存中并没有产生实参的副本,它直接对实残操作;而一般变量作参数,形参与实参就占用不同的存储单元,所以形参变量的值是实残传递的副本。因此,当参数传递的数据量较大时,用引用比用一般变量传递参数的时间和空间效率都好。
(3)指针参数虽然也能达到使用引用的效果,但在被调函数中需要重复使用“*指针变量名 ”的形式进行运算,着很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。
操作算法中常用到的预定义常量和类型
//函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status 是函数的类型,其值是函数结果的状态代码
typedef int Status;
typedef char ElemType;
算法一:线性表的初始化
Status InitList_Sq(SqList &L)
{
L.elem = new ElemType[MAXSIZE];
if(!L.elem)
{
exit(OVERFLOW);
}
return OK;
}
线性表2(链表)
- 用一组物理位置任意的存储单元来存放线性表的数据元素。
- 这组存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。
- 链表中元素的逻辑次序和物理次序不一定相同。
单链表
-
单链表是由头指针唯一确定,因此单链表可以用头指针的名字来命名
-
各结点由两个域组成
- 数据域:存储元素数值数据
- 指针域:存储直接后继节点的存储位置
-
结点:数据元素的存储映像。由数据域和指针域两部分组成
-
链表:n个节点由指针链组成一个链表
它是线性表的链式存储映像,称为线性表的链式存储结构
概念补充
-
单链表,双链表,循环链表
- 结点只有一个指针域的链表,称为单链表或线性链表
- 结点有两个指针域的链表(一个数据域,两个指针域),称为双链表
- 首尾相接的链表称为循环链表(分为循环单链表和循环双链表)
-
头指针:指向链表中第一个结点的指针
-
首元结点:是指链表中存储第一个数据元素的结点
-
头结点:是在链表的首元节点之前附设的一个结点(有时存在)
- 一般链表的存储结构有两种形式
- 不带头结点
- 带头结点
-
如何表示空表
- 无头结点时,头指针为空时表示空表
- 有头结点时,当头结点的指针域为空时表示空表
-
在链表中设置头节点有什么好处?
-
便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头节点的非空指针,因此空表和飞控表的处理也就统一了。
-
便于首元节点的处理
首元结点的地址位置保存在头节点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无需进行特殊处理;
-
-
头节点的数据域内装的是什么?
不是数据元素,可以为空,也可以加入数据长度等。
-
链表(链式存储结构)的特点
- 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
- 访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等(顺序存取法)与顺序表的存储方式不同。
单链表的定义和表示
- 数据类型+指针,指针型的变量
typedef struct Lnode{//声明结点的类型和指向结点的指针类型
ElemType data;//结点的数据域
stryct Lnode *next;//结点的指针域
}Lnode,*LinkList;//LinkList为指向结构体Lnode的指针类型
-
定义链表L:LinkList L;(通常这样定义)
-
定义结点指针p:LNode *p;等价于LinkList p;
两种定义方式:
typedef Struct student{ char num[8]; char name[8]; int score; struct student *next; }Lnode,*LinkList;
typedef Struct{ char num[8]; char name[8]; int score; }ElemType; typedef struct Lnode{ ElemType data; struct Lnode *next; }Lnode,*LinkList;
通常使用第二种形式
单链表的基本操作的实现
- 单链表的初始化(带头结点的单链表)
- 构造一个空表
- 生成新结点作头结点,用头指针L指向头结点
- 将头结点的指针域置空
- 构造一个空表
Status InitList_L(LinkList &L){
L = new LNode;//或L = (LinkList)malloc(sizeof (LNode));
L->next = NULL;
return OK;
}
判断链表是否为空
-
空表:链表中无元素,称为空链表(头指针和头结点仍然在)
【思路】判断头结点指针域是否为空
单链表的销毁(销毁后不存在)
【思路】从头指针开始,一次释放所有结点;
先将L指向自己的指针域(指向下一个结点),然后再将上面的结点删掉;
结束条件:L==NULL
循环条件:L!=NULL
Status DestroyList_L(LinkList &L)
{
//销毁单链表L
Lnode *p;//或LinkList p;
while(L){
p=L;
L=L->next;
delete p;//free(p);
}
}
清空链表
链表仍存在,但链表中无元素,成为空链表(头指针和头结点仍然在)
【算法】依次释放所有结点,并将头结点指针域设置为空
q=L->next;
delete p;
反复执行:p = q;q = q ->next;
结束条件:p == NULL
循环条件:p!=NULL
Status ClearList(LinkList &L){
Lnode *p