数据结构速成

基础概念:

数据元素是数据的基本单位(独享一个名字),它可能包含多个数据项(数据不可分割的最小单位),数据元素的集合叫数据对象

数据结构包括集合结构,线性结构,树形结构,图状结构,这些属于逻辑结构,数据结构还有物理(存储)结构,为顺序存储结构和链式存储结构

算法评价标准包括正确性,可读性,健壮性,高效率与低存储

以时间复杂度和空间复杂度度量算法的效率

加工型运算:改变受访问元素的内容                    引用型运算:不改变受访问元素的值,只提取一些信息作为结果

顺序表逻辑相邻元素物理位置必须相邻,链表不需要相邻

检索有顺序存取,直接存取,按关键字存取(有简单询问,区域询问,函数询问,布尔询问四种方法)

修改有插入一个记录,删除一个记录,更新一个记录

基础:A:链表List(也可以是线性表),结构体变量,元素e,数组其他数据结构元素都是e,Array,称A,栈Stack,称S,队列Queue,称Q,串String,称S,广义表GList,称L,树Tree,称T,二叉树BiTree,称T

B、a、Inti前缀:创建一个空的数据结构变量,一个参数,如栈的&S

b、Destroy前缀:销毁一个数据结构变量,一个参数如&S

c、Clear前缀:清空当前数据结构变量,一个函数参数如&S

d、Empty后缀:判断当前数据结构是否为空,一个参数如S

e、Length后缀(链表、串、数组、广义表):给出当前数据结构元素个数,一个参数如链表的L

f、Get前缀:获取数据结构中某处元素,链表跟Elem后缀,参数(L,I,&e),返回L中的第i位到e,栈跟Top,参数(s,&e),返回栈顶元素到e,队列跟Head,参数(Q,&e),返回队头元素到e,广义表跟Head,取广义表的头,跟Tail,取广义表的尾,参数都是L(表示一个新的广义表变量)

0、递归:嵌套调用自己,缩小问题规模直到解决开头要放返回的条件,中间是递去前要执行的任务,然后调用自己,在调用自己代码下写归来后要执行的任务,通常用传入函数的参数为返回条件,递归函数分为在递去和在归来时解决问题,每次调用递归时,要改传入函数参数以缩小问题规模,循环比递归更快,但是有时候递归简单递归n次即把函数打开n层,递去时一层层打开,归来时一层层关闭,函数调用消耗函数调用栈内存,递归太多次栈溢出

1、时间复杂度(和频度表达式不同,频度表达式是计算最内层循环执行的真正次数):在代码执行n次时,最内层循环执行次数与n的关系忽略倍数、常数,如n、n^2等,主要有:O(1)(执行次数与n无关)<O(log2n)(折半查找)<O(n)(一层循环)<O(nlog2n)<O(n^2)(两层循环)<O(n^3)(三层循环)<O(2^n)<O(n!)<O(n^n),另外有O(max(m^2,n^2))等,要考虑最好、最坏、平均时间复杂度,递归函数时间复杂度为递归次数*循环次数

2、空间复杂度:O(1)(占用空间与n无关),一个一维数组空间复杂度O(n),二维O(n^2),递归函数嵌套调用自己,递去时空间增长,归来时逐层释放,空间复杂度是每层递去前空间复杂度*递归次数,至少是O(n),归来时申请的空间很快释放,不算入

3、栈后进先出,n个元素进栈,出栈排序方法有(Cn、2n)(2n取n的组合)/n+1

4、队列先进先出,常用空队列、满队列、入队、出队、队头、队尾,常用数组做牺牲一个存储单元(循环队列尾指针指向下一个元素)初始化:tou=wei=0 队空:tou==wei 队满:(wei+1)%len==tou队长(头针指向前一个元素,尾针指尾也有这个结果):(wei-tou+len)%len

若tou,wei均指向真正的头尾,初始化tou=0,       wei=len-1,空:(wei+1)%len==tou满:(wei+2)%len==tou 长:(wei-tou+1+len)%len

增加一个实际长度变量(循环队列)队空:cd==0 满:cd==len 长:cd若初始化tou=0,wei=len-1 空:cd==0 满:cd==len 长:cd

5、匹配括号:左括号入栈,遇到右括号出栈前、中(正常的表达式)、后缀表达式:运算符在两个要运算数字前、中、后面a+b-》ab+(后) +ab(前) 规则:如果左边运算符可以先算,就先左边故a+b-c-》ab+c- -+abc 运算顺序:先括号,再乘除,再加减两规则结合:

a+b-c*d-》ab+cd*- -+ab*cd

a+b-c*d/e+f-》ab+cd*e/-f+ +-+ab/*cdef

a+b-c*(d/e)+f-》ab+cde/*-f+ +-+ab*c/def

6、中转后:操作数(这个前后都有)、运算符顺序一定是从左到右,用栈做计算具体方法(注意若含4x,应该做4*x处理):

A、一个字符串存表达式

B、一个栈存各种运算符

C、从左到右扫描中缀表达式:

a、数字和字母直接加到字符串后面

b、左括号直接入栈

c、若遇到右括号,用循环依次(后进先出)弹出栈中运算符并加到字符串后面,弹出左括号后中止循环

d、若遇到加减乘除运算符:进入循环,{先获取栈顶元素(最后进的),若栈顶运算符比现在碰到的运算符优先级高或一样,弹出它并加到字符串后面,若栈空或栈顶运算符优先级比现在的低或碰到了左括号,中止循环),循环结束后,现在的运算符入栈

e、扫描完中缀表达式后,弹出栈中剩下的运算符,加到字符串后面

f、得到后缀表达式后,再从左到右扫描,数字和字母直接入栈,如果遇到运算符弹出栈顶两个元素,先进的 +-*/ 后进的,算完将结果入栈,算到最后不入栈,显示结果

7、函数调用栈:每次进入下一层函数调用时,就将上一层变量存到函数调用栈里,故递归函数空间复杂度至少o(n),能循环不递归

补充1、ed5ef3964a20496a956fc5a27102a39f.jpg

a803b83375474e529442c0df21c2eef0.jpg

 1为广义表画法,3为哈希表画法(不存结点的邻接表),邻接表应该加上结点元素

广义表以A=((a), (b, (c, d)), ((e), f))为例,tail为取除第一个元素外所有元素并加括号,((b, (c, d)), ((e), f)),head为取外层括号内第一个元素,不加括号 (b, (c, d)),再tail,((c, d)),再head,(c, d),再tail,(d),再tail,()

8、二维数组行优先地址:a[i][j]地址是a[0][0]地址+(i*(j的最大值加1)+j)*sizeof结构

a[0…10,2…20],表示行从0到10有地址,列从2到20有地址,若a[0][2]地址为10,每元素占4位,则到a[5][5]有5行3列,地址为10+(5*(20-2+1)+3)*4=402

列优先地址(背记,现想会错):a[i][j]地址是a[0][0]地址+(j*(i的最大值加1)+i)*sizeof结构

注意二维数组坐标从0,不要漏算列数

注意矩阵行数列数默认从1开始,满足a[i][j]=a[j][i]的是对称矩阵,上三角列号大于行号,下三角小于,只存一个三角和对角线数据,画图推每个元素在一维数组公式,注意分上下三角,一维数组从0开始

i>j时公式前面全用i,i<j全用j

9、下三角矩阵:对角线、下三角区有不同值,上三角区值都一样,上三角矩阵类似,把上三角相同的值存在一维数组最后一位

10、对角(带状)矩阵:三对角即对角线加上下各一条与对角线平行的线,五对角是上下各2条,只有线上的有不同值,其他为0(不存进数组),要求n*n方阵

11、稀疏矩阵:非0元素比较少,可用三元组和十字链表存储

12、kmp算法匹配字符串:目标串(被找的串)指针不回溯,模式串(想在目标串中找的串)若和目标串前n个字符匹配成功,若匹配失败,找到匹配成功的内容(如找ababac在c失败,则找到ababa),找最长的公共前后缀(前缀有a、ab、aba、abab,后缀有a、ba、aba、baba,前后缀都是从左到右,前缀最长不包括最后一个字符,后缀不包括第一个,公共的有a、aba,找最长的是aba,长度是3),没有前后缀或没有一样的前后缀为0,模式串指针回溯到3+1位置(计模式串a[0]为1)处,不要忘加1,回溯后继续和目标串比较

13、kmp实现:1、建立next数组,记录在模式串每个位置失败时应该回到的位置减1,next[1]固定填0(不动)、next[2]固定填1(注意next从0开始时要各减1,0变-1) 2、把next数组改成nextval数组(优化),第0位填-1,再从左到右看,看每个位置要回溯的地方,若两处元素不同,用原来next的值,如果相同,用要回溯位置的nextval值(使next、nextval都可以用j=next[j]回溯)

14、树的结点为0是空树,非空树只有一个根结点,根结点没有前驱结点,其他的只有一个前驱结点,可以有0或多个后继结点,n>0时其余结点可以分成m个互不相交的子树(注意要互不相交),结点的叉(度)是以这个结点为根的子树个数,度为0叫叶结点或终端结点,不为0叫分支节点或非终端结点,非根结点可叫内部结点,树的叉(度)是树中全部结点的度的最大值(不是和)

15、以某结点为根结点的所有结点都是这个结点的子孙结点,直接分叉出这个结点的结点是它的祖先结点,直接分叉出它的祖先节点的结点也是它的祖先节点,包括根结点,但是只有直接分叉出这个结点的结点是它的父结点,只有这个结点直接分叉出的结点是它的子结点,只有和它拥有同一个父结点的结点是它的兄弟结点,有序树是树中结点的各子树从左到右含义不同的树,无序树无次序

16、根结点可以定义成第0或1层,边是连接各结点的线,n结点树有n-1边,路径是从结点n1到nj的所有节点组成的序列,路上n结点则路径长n-1,从根到每个结点只有一条路径,结点深度是从根结点到这个结点的线数,根结点深度为0,结点高度是从这个结点到叶结点的最长路径的长度,所有叶结点高度为0,一棵树深度或高度是树结点最大层数(默认根结点为1层,不要和结点的混淆),森林是m棵互不相交的树的集合

17、性质:A、树中结点总数是所有结点的度数加一

B、m叉树第i层(i>=1)上至多可以有m的i-1次方个结点

C、m叉树可以全部结点的度小于m,可以为空,度为m的树至少一个结点度为m,至少m+1个结点(不要忘记根)

D、高度为h(h>=1)的m叉树至多有(m^h -1)/(m-1)个结点(等比数列求和,二叉树2^h -1个)

E、有n结点的m叉树最小高度为logm(n(m-1)+1),向上取整(用性质D推的)

18、二叉树:有空树、只有根结点、只有左子树、只有右子树、左右子树都有五种一棵非空二叉树,n为结点总数,n0为度为0结点数,n1为度为1结点数,n2为度为2结点数,n=n0+n1+n2=n1+2n2+1(按树叉)n0=n2+1(二式减出)

n个结点2n个指针,n+1个空指针,n-1个存地址

A、高为h且有2^h-1结点的树是满二叉树(排满了),对满二叉树从根到叶且从左到右编号,对编号为i结点,左子结点编号2i,右子节点2i+1,父节点i/2向下取整

B、完全二叉树(若总结点数为n,叶结点数为2/n向下取整,若n为偶数,度为1结点有一个,若n为奇数,度为1结点有0个):对完全二叉树从根到叶且从左到右编号,它的编号和满二叉树一一对应(只有最后一层可以缺,且从左到右右结点旁边不能没有左结点

C、二叉排序(查找)树:树上任意结点如果有左右子树,则左子树所有结点元素值都小于这个结点,右子树都大于

19、存储:A、从0开始数组存完全二叉树,对下标i结点,左子结点下标2i+1,右子结点2i+2,父结点(i-1)/2向下取整,普通二叉树补成完全后存

B、普通二叉树用链表存,给左右子结点和父结点各一个指针

20、二叉树遍历:A、层次遍历:先把第一层元素入队,然后出队,再把它的子结点入队,接着每出队一个结点就把它的子结点(左先入)入队,没有子就不入

B、先序遍历:先访问根,再左子树,再右子树(父->兄->长孙->长曾孙(无子)->幼曾孙->幼孙),对每棵子树都这样,递归法:if(gen==null)return;访问 ;digui(gen->zzs);digui(gen->yzs);

栈法:根开始,沿树左结点,入栈前访问,依次入栈,直到左结点为空,再栈顶元素出栈,如果出栈元素有右结点,把右结点当子树再从根开始入栈,没有右结点继续出栈,如果栈空表示完成while(i!=null ||栈不空){if(i!=null){访问;入栈;继续处理左子树;}else{出栈;处理右子树}}

C、中序遍历:先访问左子树,再根结点,再右子树(长曾孙->长孙->幼曾孙(无子)->长子->幼孙长子->幼孙->幼孙幼子->父,

递归法:if(gen==null)return; digui(gen->zzs);访问;digui(gen->yzs);

栈法:根开始,沿树左结点,入栈前访问,依次入栈,直到左结点为空,再栈顶元素出栈,出栈后访问,如果出栈元素有右结点,把右结点当子树再从根开始入栈,没有右结点继续出栈,如果栈空表示完成 while(i!=null ||栈不空){if(i!=null){访问;入栈;继续处理左子树;}else{出栈;访问;处理右子树}}

D、后序遍历:先左子树,再右子树,再根结点(长曾孙->幼曾孙->长孙->幼孙长子->幼孙幼子->幼孙->长子->幼子长孙->幼子幼孙->幼子长子->幼子幼子(无子)->幼子->父),

递归法:if(gen==null)return;digui(gen->zzs);digui(gen->yzs); 访问;

栈法:建立判断结点j;while(i!=null || 栈不空){if(i!=null){入栈;访问左结点;(根开始)}else{读取栈顶元素,不出栈;if(右子树存在&&i->yjd!=j){i=i->yjd;右子树根结点入栈;i=i->zjd;}else{出栈;访问结点;i=j;i=null;}}}

E、先序遍历序列+中序构造二叉树:先序子树根结点是第一个元素,中序子树的根结点把序列从中间分开,左子树结点一定在左边,右的一定在右边

F、层次+中序构造:层次第一个结点是根结点,根据结点在中序位置,割出左右子树,根据割后的中序序列,从层次中提出左右子树的层次(层次子树结点顺序和整棵树结点顺序一样),再递归

G、后序遍历序列+中序构造二叉树:后序子树根结点是最后一个元素,中序子树的根结点把序列从中间分开,左子树结点一定在左边,右的一定在右边

H、二叉树线索化(增加两个标记,记录左右指针用于指向孩子还是作为线索):在遍历中若遇到空指针,将空指针指向它这种遍历中的前驱或后继结点(此时这个指针就是线索),若这个结点没有长子,左指针指向序列中的前驱结点,若没有幼子,右结点指向遍历序列的后继结点,即如果前驱结点右指针为空,将前驱结点右指针指向当前结点,如果当前结点左指针为空,将当前结点左指针指向前驱结点,若没有应该指向的前驱或后继结点则指空,同时有左右结点则不动,为了保留树形结构,在二叉树结构中增加两个标记,分别表示zz和yz存的是结点还是线索,

I、先序线索二叉树求后继:如果当前结点右指针存的是线索,右指针指向的结点是后继结点,如果当前结点右指针存的是结点,取左子结点,如果左子结点为空取右子结点,如果都是结点,取左边

J1、后序线索二叉树求后继:若当前结点是二叉树的根,则后继为空,若当前结点是它fq的yz,(若为zz,还要加上它的fq没有yz的条件),则当前结点的后继是它的fq,若当前结点是它fq的zz,且它的fq有yz,则当前结点的后继是在它fq的右子树上按后序遍历的第一个结点

J、后序线索二叉树求前驱:如果当前结点左指针存的是线索,左指针指向的结点是前驱,如果当前结点左指针存的是结点,取右子结点,如果右子结点为空,取左子结点

K1、中序线索二叉树求后继:如果当前结点右指针存的是线索,右指针指向的结点是后继,如果当前结点右指针存的是结点,右子树中序遍历的第一个(最左)结点是后继结点

K、中序线索二叉树求前驱:如果当前结点左指针存的是线索,左指针指向的是前驱,如果当前结点左指针存的是结点,左子树中序遍历序列的最后一个(最右)结点是前驱

L、树存储结构:树、二叉树都可以用二叉链表存,孩子兄弟表示法最常用,孩子指针指向第一个子结点(可以有很多孩子),兄弟指针指向下一个兄弟,形成链表,

双亲表示法存在数组里,用int伪指针存双亲结点数组下标,根结点下标为0,伪指针固定为-1(伪指针和数据放在树的结构体里,如根的zz伪指针为0)

孩子表示法把每个结点的孩子存在一个单链表里,n结点有n孩子链表(叶结点孩子链表为空),孩子结点链表结构体存孩子结点数组下标和指向下一结点指针,结点结构体里存数据、孩子结点结构体变量,树结构体存结点结构体数组,树中结点数量,数据全存在数组里,求孩子直接遍历链表找下标,找双亲从数组头开始遍历每一个链表看自己在哪里

M、树、森林和二叉树:树用孩子兄弟表示法存储,将指针理解为孩子兄弟得到树,理解为左右子结点得到二叉树,两种理解都能得到唯一的树,树可以转换为唯一的二叉树,二叉树不能转为唯一的树,区别在把右指针理解为孩子还是兄弟,     森林有m棵不相交的树,但是把第n棵树的根结点视为第一棵树根结点的兄弟,就可以用二叉链表存储:先把每棵树转换成二叉树,再把根结点相连,纸上把右指针掰到与孩子相连,二叉树转森林切断所有右指针,把兄弟和双亲连上,若只有一个子结点,摆到中间

N、树的遍历:

a、先根遍历:若树非空,先访问根结点,再依次先根遍历每一棵子树,结果与对应二叉树先序遍历相同

b、后根遍历:若树非空,先依次后根遍历每一棵子树,再访问根结点,结果与对应二叉树中序遍历相同

c、层次遍历:与二叉树层次遍历方法相同,要借助队列,结果不同

O、森林的遍历:

a、先序遍历:结果和对应二叉树先序遍历相同,若森林非空,从第一棵树开始,依次对每一棵树先根遍历

b、中序遍历:结果和对应二叉树中序遍历相同,若森林非空,从第一棵树开始,依次对每棵树进行后根遍历

P、二叉排序(搜索)树,英文缩写BST(注意往里面插入元素时间复杂度O(n)!):对一非空二叉排序树,若左子树不空,则左子树上所有结点的值小于根结点的,若右子树不空,则右子树上所有结点的值都大于根结点的,它的左右子树也都是二叉排序树,

二叉排序树中序遍历结果是各结点从小到大排列的,因为左子树<根<右子树,查找时把要找元素和根比,小于根走左边,大于根走右边,插入元素时也是这样走,比最后一个根小插左,大插右,用序列造二叉排序树以第一个元素为根依次插入,

相同序列造的二叉排序树是唯一的,相同元素不同序列建的二叉排序树不同,二叉排序树先序遍历结果再排序是原来的树

Q、删除二叉排序树结点:如果是叶结点,可以直接删,注意如果是根结点,要把根赋空,若待删除的结点只有左子树或右子树,让子树替代自己,注意如果要删的是根,要把子树的根结点赋给根,如果同时有左右子树,让左子树最右侧(最大,注意不是最靠近要删的结点,此时它一定没有右子树,更简单)结点替代自己,然后删掉左子树最右侧结点,也可以让右子树最左侧结点做同样的事,注意如果左子树没有右子树,要让它的左指针为空,平时让第二右的右指针为空,此时不行

R、查找效率分析:a、查找长度:查找操作中,需要对比关键字的次数

b、平均查找长度(ASL):查找成功时,这个结点所在的层数是需要比较的次数(1层开始)

ASL=(1*1+2*2层元素数+……+n*n层元素数)/总元素数

查找失败时,总是停在空指针处,ASL=(n*n层所有结点下属空指针数+(n+1)*n+1层所有结点下属空指针数+……)/空指针总数,判断指针不算查找长度

S、平衡二叉树,缩写AVL:树上任意结点左右子树深度差不超过1,

a、结点平衡因子:左子树高度-右子树高度(任意结点都有,左右都是空,因子是0,平衡二叉树结点因子只能是0、1、-1),在构造二叉排序树时,插入或删除结点后,只要调整最小不平衡子树,整棵树恢复平衡

b、插入时:从最小不平衡子树根结点出发,如果外侧(如左边的左子树)子树更高(插了元素),只要旋转一次,如果内侧子树更高(如左边的右子树),要转两次,哪边的子树高就往另一边转,让它这边的子结点代替原来子树的根结点,把子结点弹出的元素接到原来子树根结点缺的位置上,把树变矮,把结点用整数表示,方便判断它在调整时放在什么位置,(*gen)->fq->yz=(*gen)->yz因为递归时gen自己就是上一个结点的zz或yz,所以把gen->fq->yz改了就相当于改了gen自己,然后如果再把gen改回来,gen->fq->yz也就白改了,不管怎么搞都有bug,反而保存新gen,最后再把gen赋过去是可行的,同样执行了需要的操作,注意递归时被修改指针可能是gen自己

9c554a967a9646009572670061961ef9.jpg

 f1f7e9fbeece409db578dd82166d8dff.jpg

T、高度为hAVL树最少结点数递推公式:C1=1,C2=2,C3=4,Ch=C(h-1)+C(h-2)+1,每加一个最左结点使高度加一,为平衡在原来树中无子者加一左子,度为1结点下加一右子

U、哈夫曼树(最优二叉树):把最经常访问的结点放在根附近,1表示向yz走,0表示向zz走,用0、1组成的编号表示每一个结点,数据存在各0结点的yz里

a、结点路径:从树中一个结点到另一个结点之间边数是路径长度

b、结点的权:结点的数据等有现实意义的数字,权值越大离根越近

c、结点的带权路径长度:从根到该结点的路径长度与该结点上权值的乘积

d、树的带权路径长度(WPL):树中所有叶结点带权路径长度之和,叫WPL,其中只有WPL最小的树才是哈夫曼树,但是哈夫曼树不唯一,题目可能要求两结点合并时,权重小的为左子树,权重大的为右子树,如果两结点权重相等,本来在左边的结点为新树的左子树,新创建的树始终放在集合最右边

e、构造哈夫曼树(没有度为1的结点,题会考):在要进树的各结点中找到两个权值最小的,加上一个根结点,把根结点的权值改成两子结点权值之和,然后把它视为一个要进树的结点,重复以上操作,直到只有一个根,若有n个叶结点,共合并n-1次,树结点总数2n-1个,哈夫曼树不存在度为1的结点,数组实现,可以在数组1-n位置输入数据(因为把zz、yz初始化0,0处不能填数据,zz为0才能表示无左子结点,可以用0处zz存真正的头结点位置),然后用两个for循环找到qz最小的两个结点(严格按这个标准来,不要每次带上上一次合并的子树,否则会错),在n+1位置,使它的qz为两结点相加,zz、yz分别为找到的两个结点,两结点fq为n+1,然后从1到n+1再次进行同样的操作,到2*len-1处为止,真正的头是它

f、哈夫曼编码:是可变长度编码,用0表示左子树,1表示右子树,可以设计任何一个字符的编码都不是另一个字符的编码的前缀的编码,叫前缀编码(用1、01等表示每一个结点,可以写在一起,如101),用哈夫曼编码存数据实现数据压缩,n数据结点的哈夫曼编码最长为n-1,用长n字符串存,把n-1位置设为'\0',若此结点为它fq的zz,在bm[n-2]位置填0,否则填1,到fq为0的结点为止

19、图:元素之间关系是多对多,元素叫顶点,简称V,图的顶点不能为空,两个顶点之间的关系叫边,简称E,是有穷可空集合,V=(v1,v2,……,vn),|V|是顶点个数,E={(u,v),u属于V,v属于V},|E|是边条数,图简称G,由顶点集V和边集E组成,G=(V,E),V=(v1,v2,v3,v4,v5)表示有五个顶点,E={(v1,v2),(v1,v3),(v3,v5),(v2,v4)}表示1、2等顶点之间各有一条边,E={}表示各顶点间没有边,若存在只有一个顶点连着的边,它所在的不能叫图

A、无向图:上面的图E各边由圆括号写,是无向边,这个图是无向图

B、无向图:上面的图E各边由圆括号写,是无向边,这个图是无向图

C、简单图、多重图:不存在重复的边,不存在顶点到自身的边,多重图存在

D、度(对无向图,它的入度等于2*度):顶点的度是与该顶点关联的边的调数,全都顶点度的和是2*边数,v1连3边,则TD(v1)=3

E、度、入度、出度(对有向图):入度是以该顶点为终点的边的条数,出度是以该顶点为起点的边的条数,顶点的度是该顶点入度和出度之和,有向图中所有顶点的入度之和等于出度之和,TD(v1)=ID(v1)+OD(v1)

F、路径、回路、连通、强连通:路径是从顶点vx到顶点vy的顶点序列,回路是第一个顶点和最后一个顶点相同的路径(也叫环),简单路径是在路径序列中,顶点没有重复出现的路径,简单回路是除第一个和最后一个顶点外,其他顶点没有重复出现的回路,路径长度是路径上【边】的条数,顶点到顶点距离是顶点之间【最短路径】的长度,如果没有路径,记为【无穷(∞)】,在无向图中,如果vx到vy有路径,表示vx和vy是连通的,在有向图中,如果vx到vy和vy到vx都有路径,表示vx和vy是强连通的(有向图路径、回路要看方向,强连通可以不是原路返回)

G、连通图、强连通图:连通图(对无向图):任意两个顶点都是连通的,顶点数为n连通图,最少有n-1条边,顶点数为n非连通图,最多可能有C<2><n-1>条边(孤立一个点,其他的与剩下的各点都连通),强连通图(对有向图):任意两个顶点都是强连通的,顶点数为n强连通图,最少有n条边(一个环),顶点数为n的非强连通图最多可能有2*(c<2><n-1>)+n-1条边(各顶点与剩下的顶点互相强连通,并都指向孤立的顶点,孤立顶点没有路径指向其他顶点)

H、子图、生成子图:从原图中拿出部分顶点和部分边都是子图(要保证拿完还是图),生成子图包含了原图的全部顶点和若干条边

I、连通分量、强连通分量:无向图中,极大的连通子图叫连通分量(是连通子图(只有一个顶点也算),每个连通子图包含所有和它连通的顶点和边(两个顶点也算),在有向图中,极大的强连通子图叫强连通分量(是强连通子图,每个强连通子图包含所有和它强连通的顶点和边(一个孤立顶点也算)

J、生成树、生成森林:无向连通图中,生成树是包含了全部顶点的极小连通子图(连通图,全部顶点边最少),生成树包含全部顶点和尽可能少的边(n-1)在无向非连通图中,连通分量的生成树构成生成森林

K、带权图、网:在图中,边若表示某种含义的数值(如顶点间距离),这个数值叫该边的权值,图的边若带上权值,该图叫带权图(或网),带权图中,某条路径上全部的边的权值之和,叫该路径的带权路径长度(权为无穷表示没路)

L、完全图:无向完全图中任意两个顶点都有一条边,顶点数为n无向完全图有C<2><n>即n*(n-1)/2条边,有向完全图中任意两个顶点都有方向相反两条边,有2*C<2><n>条边

M、特殊的图、稀疏图和稠密图:边很少的图叫稀疏图(n顶点时,一般是|E|<nlogn),反之叫稠密图,树是不存在回路的连通无向图,森林是不存在回路的非连通无向图,有向树,有且仅有一个结点的入度为0(除根外结点入度为1)根到任意结点有一条有向通路

20、图的存储:A、邻接矩阵:图结构包括顶点结构类型的一维数组做顶点表,int类型的一维数组做边表,两个int变量存顶点数和边数,边表的行表示A等各顶点到其他顶点是否有边,1为有边,0为无边,有向图可以(A,C)为1,(C,A)为0,表示A到C单向,无向图度看所在行或列上1的个数,有向图出度是行上1的个数,入度是列上1的个数,如果边带权,要先设置一个权中不会出现的数为无穷常量,然后把边的权值存到边表里(顶点自己到自己也可以填无穷),空间复杂度O(|V|^2),适合存稠密图,表示方式固定,求度、入度、出度遍历矩阵行或列,时间复杂度O(|V|)

B、邻接表(注意要画数据域和指针域,^表示空指针):顶点信息在一维数组,边信息在链表,顶点结构包括顶点信息,边结构指针(指向边链表用),边链表结构包括边指向顶点的数组下标、边的权值、边结构指针,图结构包括顶点结构一维数组、图顶点数和边数,无向图边链表存与顶点有关所有边,有向图边链表只存从它顶点出发的边,空间复杂度无向图O(|V|+2|E|),有向图O(|V|+|E|),适合存稀疏图,表示方式因边链表结点顺序是任意的,不唯一,求无向图度和有向图出度时间复杂度O(1),求有向图入度时间复杂度O(|E|)

C、十字链表(适合存有向图):顶点结构改成顶点信息+弧结构指针a(表示以该顶点为弧头的第一条边)+弧结构指针b(表示以该顶点为弧尾的第一条边),弧结构有int a存弧头顶点下标,int b存弧尾顶点下标,弧结构指针c指向弧头相同的下一条弧,弧结构指针d指向弧尾相同的下一条弧,可以加上权值变量,找同弧头顶点从a走c即可,同弧尾从b走d,注意一个弧结构结点可以分别被它弧头的a和它弧尾的b访问,边链表结点数|E|,空间复杂度O(|V|+|E|),求顶点出度、入度时间复杂度都是O(1)~O(|E|)

D、邻接多重表(适合存无向图):可以用和十字链表一样的结构,但是仅用顶点结构里的指针a指向与它连接的第一条边,弧结构里a、b依然可指弧头、弧尾,c指依附于弧头下一条边,d指依附于弧尾下一条边(无向图没有弧头弧尾,只要是边两端顶点就可以,调用时要比较a、b后选c、d),这样删边方便,把上一个结点指针改到下一个就可以,删边也方便,空间复杂度O(|V|+|E|)

21、图的操作:A、判断图G是否有顶点v到顶点w的边(v,w)或<v,w>,邻接矩阵检查边表的v行w列,邻接表检查v的边链表,时间复杂度O(1)~O(|V|)

B、列出图中与v邻接的边(包括出边和入边):邻接矩阵查v行和v列,邻接表查v的链表找出边和其他顶点链表找入边(有向图才需要),邻接矩阵时间复杂度O(|V|),邻接表O(1)~O(|V|)(出)+O(|E|)(入),O(max(|V|,|E|)

C、插入顶点:邻接矩阵、邻接表把空白处赋新顶点,顶点数加一即可,O(1)

D、从图中删除顶点v:邻接矩阵把v下面的顶点上移,把边表v行、v列这样删掉,减掉顶点数、边数,邻接表把v下顶点上移,删v的链表和其他链表中和v有关结点【这是真删除】或者在邻接矩阵中给v赋一个特殊值表示删除了,在邻接表中给v赋特殊值,删掉其他顶点链表与v有关顶点,顶点插入改成找被伪删除的点,删掉原有数据,重新利用空间,可用一个数组记录伪删顶点数量和下标,最后减掉顶点数、边数【这是伪删除

E、插入一条从v到w边:邻接矩阵无向图在v行w列,w行v列赋1,有向图只赋值v行w列,邻接表无向图在v和w链表都加一个结点,有向图只加v的

F、删掉一条从v到w边:按插入方法赋值0,删结点即可

G、求图中v的第一个邻接顶点(有向图只找从v出边的顶点),若存在则返回这个顶点,否则返回-1:邻接矩阵扫描v行,邻接表扫描v的链表

H、求图中顶点v相对于w的下一个邻接点,若w是v的最后一个邻接点,返回-1:和7差不多的操作,加上比较找到的顶点和w即可

I、广度优先搜索(BFS,类似树的层次遍历):任意找一个或多个(数组中的井)顶点,把它们入队,然后在标记数组上把它所对应的位置置为已访问,然后对第一个顶点进行访问操作,再把它出队,像这样每出队一个顶点,就把它四周(和它有边)的且未被访问过的顶点入队,灵魂是【标记数组】,最后可以得到遍历序列(同一种存储结构从同一个顶点出发的遍历序列是一样的,不同不一样)【这是连通图】,如果是非连通图,完成第一次遍历后检查标记数组,找到未访问的再一次BFS【这是非连通图】,另外有向图代码和无向图完全一样,因为只看出边,【辅助队列】辅助队列在一个顶点和全部顶点相连时元素最多,有|V|-1个,加上标记数组,空间复杂度O(n),邻接矩阵找每个顶点O(|V|),找邻接点O(|V|^2),邻接表找每个顶点O(|V|),找邻接点O(|E|)

J、广度优先生成树:从某顶点出发广度优先搜索,按它走过的边链接所有路上的结点,得到广度优先生成树,邻接矩阵从同点出发是唯一的,邻接表不唯一对非连通图搜索会得到广度优先生成森林

K、深度优先搜索(DFS,类似树的先序遍历,图有回路,所以需要标记数组):任意从一个顶点开始访问,然后入栈,再访问和它关联的第一个顶点,入栈再访问和第一个关联顶点关联的顶点,入栈,直到访问到没有关联顶点的顶点,访问它但是不入栈(或者先入再出),再访问与它上一个顶点关联但未被访问的顶点,入栈,重复上面的操作,直到栈空,空间复杂度O(n),时间复杂度和广度优先一样,生成树、生成森林方法和广度优先一样,有向图从不同顶点开始搜索次数(非强连通图时)可能不同

L、最小生成(代价)树:所有边权值之和最小

a、普里姆算法:从任意顶点(一般是编号最小的点)开始构建生成树,然后依次把代价最小的顶点(看所有与已连接顶点连接的边(另一端的顶点未连接),找到权值最小的连上,如(1,2)表示连接1,2两顶点的边)纳入生成树,直到所有顶点都纳入为止,适用稠密图(边多),时间复杂度O(n^2)要两个数组,一个存已加入子树顶点(已加入用1,未加入用0),一个存未加入子树顶点及代价(已加入用0,未加且可加的用权值表示,不能加的用-1表示),用一个循环遍历除初始化顶点外的顶点,根据遍历到的顶点重新计算代价并记下最小的

b、克鲁斯卡尔算法(注意要连上所有结点):每次选择一条权值最小的边,让这条边两端的顶点连通,如果这两条边的顶点已连通,则不选,适用稀疏图,时间复杂度O(|E|log|E|)

M、最短路径:单源最短路径:某顶点出发,到其他全部顶点的最短路径顶点间最短路径:全部顶点(每对)之间的最短路径,结果是顶点间最短路径长度和从源顶点到目的顶点途经顶点序列

a、广度优先(无权图):在广度优先基础上加一个最短路径数组(存各顶点到源顶点最短路径)和前驱顶点数组(存生成树中各顶点的前驱顶点,递归查找可得到路径)

b、迪杰斯特拉算法(属于贪心算法,支持带权,但是不支持权值为负数的图,得到的树不一定是最小生成树):思想:基于bfs,但是根据边的权值增加权值个数的虚拟结点参与bfs(如权值为2,则在这条边上加2个虚拟结点)

需要标记数组、最短路径数组、前驱顶点数组,先找一个出发点,把它的前驱顶点改成-1,和它直接连的顶点的前驱改成它,计算它的最短路径数组(它的最短路径0,把和它直接连的顶点的边的权值更新为最短路径),然后进入循环,从未确定最短路径的顶点中选最短路径最小的顶点为新确定最短路径的顶点(考虑的是从根到这个顶点的路径长度),更新与新确认顶点有路径的顶点的最短路径和前驱顶点(如果新路径更短就更新(还是从根到它的长度),更长就不更新,注意是源到它的距离,要加上前面顶点到源的距离),空间复杂度O(n),时间复杂度O(n^2)

c、弗洛伊德算法(算顶点间最短路径,支持无权、带权、含负权图,属于动态规划,要求用邻接矩阵):若图有n顶点,则分n个阶段:0、初始化,在没有其他顶点中转时,求各顶点间最短路径,如果没路记为无穷,存在二维数组中,规则类似邻接矩阵1、选其中一个顶点为中转点,求各顶点间新的最短路径,一个个加入计算,最后一个为止,初始化最短路径矩阵为图的邻接矩阵,中转顶点矩阵全部置-1,表示无中转顶点,加入中转顶点后,ii行,jj列

for(ii=0;ii<|V|;ii++) 加入数组几号中转,就令kk为几,AA是最短路径矩阵

for(jj=0;jj<|V|;jj++) ,path是中转顶点矩阵 适用权值为负数的图

if(AA[ii][kk]+AA[kk][jj]<AA[ii][jj]) 空间复杂度O(n^2)

{AA[ii][jj]=AA[ii][kk]+AA[kk][jj];

path[ii][jj]=kk;}

时间复杂度O(n^3) } 完成后,如想查v1到v3的路径,看path[v1][v3]值,如果为-1,表示两顶点直接相连,如果不是-1,设中转点v2,则再递归查path[v2][v3],看是否为-1}

N、拓扑排序(遍历AOV网(记住特点是有向无回路!):用顶点表示活动,用有向边(弧)表示活动时间先后关系的有向无环图(DAG)):找到做事的先后顺序,先从图中找一个入度为0的顶点并输出,从图中删掉该顶点和全部以它为起点的有向边,然后重复这两个步骤,直到图为空,如果多个顶点入度为0,可以任选一个,结果可不唯一,准备两个数组,a存全部顶点入度,b存拓扑排序结果,一个计数变量c,计已排序顶点数量,先求全部顶点入度,存在a中,再初始化一个栈,存入度为0顶点,再全部入度为0顶点入栈,然后在栈非空情况下循环:出栈一个元素,存在b[count++]里,再for循环获取这个元素的出边,再把出边指向的顶点的入度减一,如果减完之后入度为0就把它入栈,循环完成后,如果count小于顶点数,表示有环,排序失败

O、逆拓扑排序:从图选一个出度为0顶点并输出,从图中删除该顶点和全部以它为终点的有向边,再重复这两个步骤,直到图为空,结果不一定唯一

P、关键路径:过程中最长的路径是关键路径,关键路径上的活动是关键活动,如果不能按时完成,过程将被迫延期使用AOE网,用顶点表示事件的发生,边表示活动,边的权表示活动的开销特点是只有一个入度为0点,表示整个工程开始(开始顶点),只有一个出度为0顶点,表示这个工程结束,只有在某顶点代表的事件发生后,从该顶点出发的各【有向边】代表的活动才能开始,只有指向某顶点所有有向边代表的活动结束后,该顶点代表的事件才能发生;在AOE网中,有些活动可以并行事件最早发生时间按拓扑排序计算,第一个顶点的为0,第二个是1指2的边长度,第三个以后选指向它的各顶点开始时间加上权值后最大的为时间,表示活动的边从哪个顶点出发,活动最早开始时间填这个顶点的最早发生时间最迟发生时间按逆拓扑排序算,第一个取最终时间,从第二个开始用它指向的所有顶点减掉对应边的时间,最长的是这个顶点的最迟发生时间,活动最晚开始时间用表示这个活动的边指向的事件顶点的最晚发生时间减掉这个边的权,最后活动余量用活动最晚开始时间减最早开始时间得到,活动余量为0的边和有关的顶点组成的路径是【关键路径】缩短关键活动时间,可以缩短工期,如果关键活动耗时增加,工期延长,当关键活动时间缩短到一定程度,可能变成非关键活动

22、查找概念:A、查找表:由相同数据类型的元素组成的集合,可以是线性表、树等

B、关键字:数据元素中的某个数据项,一个标识一个数据元素,是唯一的

C、查找:根据给的某值,在查找表里找一个关键字等于它的元素,如果表里有,查找成功,否则失败

D、静态查找表:查找表中的数据元素不会变化

E、动态查找表:查找表中数据会发生插入、修改、删除等变化

F、查找长度:查找运算中,要对比关键字的次数G、平均查找长度:所有查找过程中进行关键字比较次数的平均值,衡量查找算法的效率,注意包括【成功和失败】两种情况

23、查找算法:A、顺序查找:把数组a[0]位置留给哨兵放关键字,再从尾开始比是否等于哨兵,因为0处存在哨兵,一定能找到,不用比较i的大小,节省了一条语句先排序,再一个个比较找,可以用大于关键字判断失败,节省失败时查找时间,或者先比较,如果关键字大,跳三个再比,如果关键字小,再倒回去找效率分析:ASL(成功)=(1+……+n)/n=(n+1)/2 ASL(失败)=n

B、查找判定树:按查找顺序把查找成功的各点连成树,把失败的节点关键字范围作为叶结点补上去(左子结点小于父结点小于右子结点),分为成功和失败,每层查找次数是该层结点数*该层层数,把所有层查找数加起来的和除以结点数是平均查找长度(ASL)

C、折半查找判定树性质:a、中序遍历序列和查找表相同 b、若折半查找mid向下取整,树中任何子树右子树结点数-左子树结点数必为0或1(若mid向上取整,为左-右),是平衡二叉树 c、除了最后一层,其他层结点一定是满的,结点数n则树高log2(n+1)整体向上取整 d、有n+1个失败叶结点,查找成功和失败的ASl<=树高,时间复杂度O(log2n)若每个元素被查概率(需要查的次数)不同,按概率从高到低从0开始排序即可,这样更适合查找成功多的情况,如果失败多,用有序查找的方案

E、折半查找(只适用于有序的顺序表,不能是链表,注意初始mid坐标是(len-1)/2向下取整):已知数组长度len,初始化low=0,high=len-1,mid=(low+high)/2(mid向下取整,即c整数除法),然后把mid位和关键字比较,相等表示找到,若关键字大,把low移到原【mid+1】(记得要移动!),mid重新算,再进行一轮查找,关键字小则把high移到原【mid-1】,若low>high,查找失败(最后一定会low=high,此时再找不到就low>high了)

折半查找若元素数n满足2^k<n<2^(k+1),则最多查k+1次知道是否存在

24、排序及其他查找方法,插入排序第一趟之后任意元素不能确定位置(排着可能遇到更小的元素)

排序趟数即外循环次数,包括不交换的情况

堆排序建立初始堆就是进行完整的堆排序,若没说要求,默认建立小顶堆,注意因为堆排序在数组中进行,结果只要写各元素在数组中的序列,化成二叉树做完后,按层次遍历读即可得到这个序列

冒泡排序是相邻两数比较,前比后大则交换的方法找到最大数,可以记录某趟排序是否发生交换进行优化

dcb7c00c04c24e2180d2736eb20df7a1.jpg

ad22c696cdd04483ada949bd95f64857.jpg b34c03bf42bd429884a955d29f33f52c.jpg

排序算法的稳定性通俗地讲就是能保证排序前两个相等的数据其在序列中的先后位置顺序与排序后它们两个先后位置顺序相同。

因为选择排序次数与序列无关,故用它计算最多排序次数n*(n-1)/2

注意空间复杂度就是排序需要的辅助记录单元数!注意快排不稳定,一般越快越不稳定!堆排序找到最小或最大的几个数比快,堆,归并排快,采用维护一个所需个数的小顶堆或大顶堆。堆排序对每个结点进行筛运算时间复杂度O(log2n),整体O(nlog2n),大顶堆也叫大根堆

查找表0位放待查元素,则这个位置叫监视哨,根据查找概率可放头可放尾

查找索引顺序表,要先查索引表找到目标元素可能在的块,再查找块,注意概念名称

在哈希表每个位下接链表解决同义词问题叫线性探测法(在开放定址法有介绍),还有二次探测再散列,在k+i^2和k-I^2(k为原地址,i=1,2,……)中找可用地址,有效使用哈希表要确定哈希函数和解决冲突的方法

7230a12381b645f68d7911ed748bbcef.jpg

 c022f30b966b4cb3a8c3d426b114c8e5.jpg

25、经典应用

A、表达式二叉树

a、准备一个操作符栈,一个操作数栈

b、左向右遍历表达式,遇到字符先判断是操作符还是操作数,若为操作数,直接入操作数栈

c、若为操作符,先判断操作符栈是否为空,若空,直接入栈

d、若操作符栈非空,比较当前遇到的操作符和栈顶操作符的优先级,若高于栈顶优先级,直接入栈,否则把栈顶操作符出栈,从操作数栈依次出栈两个操作数作为它的左右子树,然后再把这个操作符当做根结点入操作数栈,然后当前的操作符入操作符栈

e、遍历结束后,判断操作符栈是否为空,若非空,则依次出栈操作符按上面的规则构造树

f、生成树后,分布前序,中序,后序遍历树可以分别得到前缀,中缀,后缀表达式,可以用中序遍历递归对左右子树进行根结点类型的计算

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值