数据结构与算法分析学习笔记
1.2什么是结构
结构:实体+关系
数据结构:
1.按照逻辑关系组织起来的一批数据
2.按一定的存储方法把它存储在计算机中
3.在这些数据上定义了一个运算的集合
数据结构的逻辑组织:
线性结构:如线性表(表,栈,队列,串等)
非线性结构:如树(二叉树,Huffman树,二叉检索树等)、图(有向图,无向图等)
图 >= 数 >= 二叉树 >= 线性表
数据的存储结构:
关系元组:
顺序:存储单元的顺序地址(如数组)
链接:指针的地址指向关系(如链表)
四类:顺序、链接、索引、散列
抽象数据类型,简称ADT(Abstract Data Type)
定义了一组运算的数学模型
与物理存储结构无关
使软件系统建立在数据之上(面向对象)
ADT不关心存储细节
模块化的思想的发展
隐藏运算实现的细节和内部数据结构
软件复用
抽象数据结构二元组
<数据对象D,数据操作P>
先定义逻辑结构,再定义运算
逻辑结构:数据对象及其关系
运算:数据操作(函数)
例如:栈的抽象数据类型ADT
逻辑结构:线性表
操作特点:限制访问端口
-只允许在一段进行插入、删除操作
-入栈(push)、出栈(pop)、取栈顶(top)、判栈空(isEmpty)
算法
问题—算法—程序
·问题(problem)一个函数
从输入到输出的一种的映射
·算法(algorithm)一种方法
对特定问题求解过程的描述,是指令的有限序列
·程序(program)
是算法在计算机程序设计语言中的实现
基本算法分类
穷举法
顺序找K值
回溯、搜索
八皇后、数和图遍历
递归分治
二分法找K值、快速排序、归并排序
贪心法
Huffman编码树、最短路Dijkstra算法、最小生成树Prim算法
动态规划
最短路Floyd算法
算法复杂性分析
时间复杂度:事前预估算法时间开销T(n)与问题规模n的关系
在算法表达式中,可以只考虑阶数更高的部分。
加法规则:
T(n) = T1(n) +T2(n) = O(f(n)) + O(g(n)) = O( max( f(n) , g(n) ) )多项相加,只保留最高阶的项,且系数变为1.
如:T1(n) = 3n+3 >> T1 = O(n) T2(n) = n2+3n+1000 >> T2(n) = O(n2)
乘法规则:
T(n) = T1(n) X T2(n) = O(f(n)) X O(g(n)) = O( f(n) X g(n) ) 多项相乘,都保留
如:T3(n) = n3 + n2log2n >> T3(n) = O(n3) + O(n2log2n)
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
结论一:顺序执行的代码只会影响常数项,可以忽略
结论二:只需挑循环中的一个基本操作分析它的执行次数与n的关系即可
结论三:如果有多层嵌套循环,只需关注最深层循环循环了几次
空间复杂度:程序运行时的内存需求,用S(n)表示
概念补充:算法原地工作----算法所需内存空间为常数
一、表
数据结构中的表,指的是一对一关系的数据所组成的数据结构。同理,表示一对多数据结构的是树,多对多关系的数据结构是图。
数据结构中的表结构主要分为两种:顺序表和链表。
概念补充:数据结构三要素——逻辑结构、数据的运算、存储结构(物理结构)
线性表(一种逻辑结构)
定义:具有相同数据类型的n(n>0)个数据元素的有限数列,其中n为表长,当n=0时,线性表是一个空表。
顺序表―—用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
顺序表的实现——静态分配(使用数组)和动态分配
C——malloc、free函数 C++——new、delete关键字
顺序表的特点:
一、随机访问,即可在O(1)时间内找到第i个元素,任意位序的元素
二、存储密度高,每个节点只存储数据
三、拓展容量不方便(即使采用动态分配的方式实现,拓展长度的时间也比较高)
四、插入,删除操作不方便,需要移动大量的元素
单链表
优点:不要求大片连续空间,改变容量方便。
缺点:不可以随机存取,要耗费一定空间存放指针。
封装的好处:避免重复代码,简洁,易维护。
静态链表:
单链表:各个结点在内存中星罗棋布,散落天涯。
静态链表:分配一整片连续的内存空间,各个结点集中安置。用数组的方式实现的链表。
顺序表(顺序存储)与链表(链式存储)的比较:
Round 1:逻辑结构:
属于线性表,都是线性结构。
Round 2:存储结构:
循序表:
优点:支持随机存储、存储密度高。
缺点:大片连续空间分配不方便,改变容量不方便。
链表:
优点:离散的小空间分配方便,改变容量方便。
缺点:不可随机存取,存储密度低。
Round 3:基本操作:(创销、增删、改查)
创:
顺序表:需要预分配大片连续空间。若分配空间过小,则之后不方便拓展容量;若分配空间过大,则浪费内存资源。
链表:只需要分配一个头结点(也可以不要头结点,只声明一个头指针),之后方便拓展。
销:
循序表:1)静态分配,系统自动回收空间。2)动态分配(有malloc):需要手动free。
链表:依次删除各个结点(free)。
增删:
顺序表:插入、删除元素将后续元素都后移、前移。时间复杂度是O(n),时间开销主要来自移动元素。(若数据元素很大,则移动的时间代价很高)
链表:插入、删除元素只需修改指针即可。时间复杂度为O(n),时间开销主要来自查找目标元素。(查找元素的时间代价更低)
查:
顺序表:1)按位查找:O(1) 2)按值查找:O(n),若表内的元素有序,可在O(log2n)时间内找到。
链表:1)按位查找:O(n) 2)按值查找:O(n)
应用场景:
表长难以预估、经常要增加、删除元素 --链表
表长可以预估、查询(搜索)操作较多 --顺序表
栈(Stack)
栈:是只允许在一端进行插入或删除操作的线性表。特点:后进先出。(Last in first out)
重要术语:栈顶(允许插入和删除的一端),栈底(不允许插入和删除的一端),空栈,栈顶元素,栈底元素。
顺序栈:用顺序存储方式实现的栈。(共享站:两个栈共享同一片空间。)
链栈:用链表存储方式实现的栈。
队列(Queue)
队列是只允许在一端进行插入,在另一端删除的线性表。(队列的特点:先进先出First In First Out)
重要术语:队头(允许删除的一端),队尾(允许插入的一端),空队列,队尾元素,队头元素。
队列的顺序实现(包含一种循环队列)
队列的链式实现
双端队列:只允许从两端插入、两端删除的线性表。
输入受限的双端队列:只允许从一端插入,两端删除的线性表
输出受限的双端队列:只允许从两端插入,一端删除的线性表
串
串,即字符串(string),是由零个或多个字符组成的有限序列。
子串:串中任意个连续的字符组成的子序列。
数和二叉树
树是n(n>=0)个结点的有限集合,n=0时,称为空树。
而任意非空树应该满足:
1)有且仅有一个特定的称为根的结点。
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集合,其中每一个集合本身又是一棵树,称为根结点的子树。
小结论:
n个结点的树中只有n-1条边。
基本术语:
祖先结点,子孙结点,双亲结点,孩子结点,兄弟结点
树中的一个结点的子结点的个数称为该结点的度。(树中最大度数称为树的度)
度大于0的结点称为分支结点,度为0的结点称为叶子结点。
结点的层次(最高的为第一层或第零层)
结点的高度(自下往上数)
结点的深度(自上往下数)
有序树和无序树
路径: 树中两个结点之间的路径是由这两个结点之间经过的结点序列构成的。
路径长度: 路径上所经历边的个数。
森林: m(m>=0)棵互不相交的树的集合。
树的性质:
树中的结点数等于所有结点的度数加一。
度为m的树中第i层上至多有mi-1个结点。
高度为h的m叉树至多有(mh - 1)/(m-1)个结点。
二叉树的顺序存储用的是数组
二叉树的链式存储用的是链表
完全二叉树的遍历使用的是递归
先序遍历(根左右)
中序遍历(左根右) 可以采用栈的思想
后序遍历(左右根)
层次遍历 可以采用队列的思想
ps:名词辨析: 扫描是指仅仅经过该结点;
访问是指查看该结点内的数据;
贪心算法:(适用于最优解问题)
基本思想:每一步都得到最优解(局部最优解),再由局部最优解得到最后的整体问题的最优解。
贪心算法是从问题的某一个初始状态出发,通过逐步构造最优解的方法向给定的目标前进,并期望通过这种方法产生出一个全局最优解的方法。
求中位数:
方法一:int mid = (left + right)/2;
方法二:int mid = left + (rigth - left)/2;
分析: 方法一严格意义上有bug的,这是因为left和right特别大的时候,left + right有可能会发生整型溢出,所以推荐方法二。