【算法设计与分析】期末考试知识总结(知识超浓缩版)

news2024/11/18 14:02:06

目录

简要介绍

·复杂度

 ·迭代

插入排序

二分查找

快排划分

选择排序

计数排序

基数排序

桶排序

·递归

递归式的计算-四种方法

 欧几里得算法

汉诺塔问题

快速排序

 归并排序

 堆排序

·分治

二维极大点问题

一维最邻近点对

二维最邻近点对

 逆序对的数目

 凸包

最大字段和问题

循环赛日程表

维诺图程序设计

补充

·减治

DFS

 BFS

拓扑排序

 生成排列(O(n!))

 生成子集(O(2^n))

淘汰赛冠军问题

快速幂

俄罗斯农民乘法

​编辑 假币问题

约瑟夫斯问题

 ·变治

实例简化

表示改变

AVL树

2-3 Tree

霍纳法则计算多项式

补充

问题规约

·贪心

找零钱问题

埃及分数

活动选择问题

背包问题和最优装载

哈夫曼编码

普里姆算法

 克鲁斯卡尔算法

迪杰斯特拉算法

多级调度问题

旅行商问题

删s个数,剩下数最小 

 ·动态规划

0-1背包问题

 斐波那契数列

矩阵连乘问题

 最长公共子序列

 编辑距离

 弗洛伊德算法

最优二叉查找树

最大子段和 

​最长上升子序列

·图

强连通分量-kosaraju算法

 强连通分量-Tarjan算法

Tarjan算法求关节点、桥

贝尔曼-福特算法

SPFA

·分支限界法

 FIFO分支限界检索4-皇后问题状态空间树

FIFO分支限界检索15-迷问题状态空间树

优先队列分支限界法解01-背包

 优先队列分支限界解多段图单源最短路

  优先队列分支限界解一般图单源最短路

优先队列式分支限界解TSP

 ·NP理论


简要介绍

根据重要程度,此文章选取了笔者本学期算法课程中的少部分内容,但是由于时间紧迫,不能对每一个地方都展开叙述,而侧重于对知识点的引导,很多地方都会是一笔带过,因此,本文章不适用于算法的0基础学习而非常适用于对算法知识的巩固和复习,大家可以根据自己学校的要求或者是自己的兴趣寻找相关内容的详解。

写这篇文章主要的目的是记录自己第一次系统地学习算法的收获,希望对以后的回顾有所帮助,同时也对即将来临的期末考试添砖加瓦。当然也希望能够稍微帮助到读者复习算法这门课程。

此文章的内容来源大多为老师ppt,所以内容梳理的顺序也将按照ppt的次序,最后在此感谢老师!

·复杂度

时间复杂度取决于待处理数据的状态和问题规模

空间复杂度取决于程序量、数据量、数据结构等

T(n):算法所需要的操作次数

五种算法复杂度的表示:

常见复杂度类型:常量型(constant)、多项式型(polynomial)、对数型(logarithmic)、指数型(exponential)、阶乘型(factorial)

 ·迭代

迭代的核心是利用变量原值推出新值

循环不变量:在循环的过程中保持不变的性质

插入排序

循环不变量:一些元素已经排好序,剩下的元素没有排序

是稳定排序,是就地排序,时间复杂度:O(n^{2}

void InsertSort(List a)
{
	for(int i=1;i<a.length;i++)
	{
		int j=i;
		int B=a[i];
		while(j>0&&a[j-1]>B)
		{
			a[j]=a[j-1];
			j--;
		}
		a[j]=B;
	}
}

二分查找

循环不变量:维护一子序列,如果目标在原序列中,那么目标也一定在子序列中

时间复杂度:O(logn)

为了解决边界问题,二分查找形成以下两种模板,假设序列存在重复元素,第一个模板可以返回重复元素的第一个值,第二个可以返回重复元素的第二个值。

//model 1
int find(int x)
{
	while(l<r)
	{
		int mid=l+r>>1;
		if(q[mid]>=x)r=mid;
		else l=mid+1;
	}
	return r;
}
//model2
int find(int x)
{
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(q[mid]<=x)l=mid;
		r=mid-1;
	}
	return r;
}

快排划分

快排有一个很重要的步骤叫做划分,如果用迭代法来设计这个算法,可以先考虑其循环不变量:维护两个指针:i,j,使得A[p...i]<=pivot,A[i+1...j-1]>pivot,A[r]=pivot,pivot为基准,pr是原序列左右边界

时间复杂度为O(n)

partition(A,p,r)
{
	x=A[r];
	i=p-1;
	for(j from  p to r-1)
	{
		if(A[j]<=x)
		{
			i++;
			exchange(A[i],A[j]);
		}
	}
	exchange(A[i+1],A[r]);
	return i+1;
}

选择排序

循环不变量:A [1...j-1]是已排序的元素,且该已排序的元素由数组最小j-1个元素组成

时间复杂度O(n^{2}),不是稳定排序,是就地排序

for j=1 to A.length-1
	k=j;
		for i=j to A.length
			if A[i]<A[j]
				k=i;
	temp=A[k];
	A[k]=A[j];
	A[j]=temp;			

计数排序

循环不变量:已经有i个元素被放在输出数组中正确位置,辅助数据结构表示每一个键下次放置的位置

时间复杂度:O(n),是稳定排序,不是就地排序

基数排序

循环不变量:键已经按照最低i-1位有效数字进行排序

时间复杂度O(n),是稳定排序,不是就地排序

桶排序

循环不变量:前i-1个键已经被正确地放在桶中,且桶中的元素已经排好序

时间复杂度O(n),是稳定排序,不是就地排序

·递归

递归:重复调用函数自身实现循环

迭代递归的比较:前者是自己执行很多次,每次排除一个,直到找到结果,后者是每次循环包含自己的请求,子问题携带原问题,一次次缩小范围,最后找到结果

尾递归:1.在尾部调用自身2.可以通过优化,使得计算仅占用常量栈空间

备忘录方法与递归:

相同点:都是自顶向下

不同点:前者用表格保存已经求解的子问题答案,下次需要时直接查看

递归式的计算-四种方法

方法一:先猜、后证明

 

可以看到最后一般要给出的是常数c所要满足的要求 

方法二:递归树

根据递推式子画出递归树,计算每一层的T(n),求和即为最终结果,方法核心在于计算每一层的T(n)和树的层数,下面这个例子,底部高度不平整时,简单放缩即可:

方法三:迭代法

这种方法是最容易理解也是适用范围最差的,放个例子

 方法四:主理论法

 先举几个例子:

满足上述第三种情况,所以T(n)=O(n^{2}

这个实际上也时case3,因为case3中f(n)看的是下界

使用方法四的注意事项:f(n)不能不单调、不能不是多项式、b必须是常数

方法四的特殊情况:

例如: 

 欧几里得算法

时间复杂度O(logn)

int gcd(a,b)
{
	return !b?a:gcd(b,a%b);
}

汉诺塔问题

快速排序

时间复杂度O(nlogn),是不稳定排序,是就地排序

void quicksort(int q[],int l,int r)
{
	if(l>=r)return;
	int i=l-1,j=r+1,x=q[l+r>>1];
	while(i<j)
	{
		do i++;while(q[i]<x);
		do j--;while(q[j]>x);
		if(i<j)swap(q[i],q[j]);
	}
	quicksort(q,l,j);
	quicksort(q,j+1,r);
}

 归并排序

时间复杂度:O(nlogn),是稳定的排序,是就地排序

void mergesort(int q[],int l,int r)
{
	if(l>=r)return;
	int mid=l+r>>1;
	mergesort(q,l,mid);
	mergesort(q,mid+1,r);
	int k=0,i=l,j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(q[i]<=q[j])temp[k++]=q[i++];
		else temp[k++]=q[j++];
	}
	while(i<=mid)temp[k++]=q[i++];
	while(j<=r)temp[k++]=q[j++];
	for(int i=l,k=0;i<r;i++,k++)q[i]=temp[j];
}

 堆排序

down调整

void down(int u)
{
	int t=u;
	if(u*2<=cnt&&h[u*2]<h[t])t=u*2;
	if(u*2+1<=cnt&&h[u*2+1]<h[t])t=u*2+1;
	if(u!=t)
	{
		swap(h[u],h[t]);
		down(t);
	}
}

up调整:

void up(int u)
{
	while(u/2&&h[u/2]>h[u])
	{
		swap(h[u/2],h[u]);
		u/=2;
	}
}

建堆+排序

for(int i=n/2;i;i--)down(i);
while(m--)
{
	cout<<h[1];
	h[1]=h[cnt--];
	down(1);
}

时间复杂度:O(nlogn)

不是稳定的排序但是就地排序

·分治

思想:将原问题分成更小的子问题,递归地解决这些子问题,然后把解决之后的子问题结果合成原问题的答案

二维极大点问题

 解决策略:如果S(指的是原点集)只有一个点,以极大点返回,否则,找到一条垂直X轴的线评分点集为Sl,Sr,递归地从Sl,Sr中找最大点,最后,从Sr中找到y坐标最大的点,舍弃Sl中y坐标小于此值的所有点(这一步本质就是子问题答案的合并)

T(n)=O(nlogn)

一维最邻近点对

第一种策略是直接排序,然后做一次遍历

第二种是分治,先求左边和右边,再合并,怎么合并,就是比较左边部分的最右边和右边部分的最左边的距离和左边部分最近距离和右边部分最近距离的大小关系,取最小,复杂度O(nlogn)

二维最邻近点对

分治步骤:1.按y坐标排序2.若只有一个点,返回距离无穷3.(分)方法与二维极大值点分法相同     4.(治)求d=min(dl,dr)  5.(合)对于L-d~L的板上的点p,每一个p找到在L~L+d的半板中y落在yp+d~yp-d中所有点,如果p与这个区域的点距离d’<d,d=d',d最终值即所求

O(nlogn)

 逆序对的数目

数逆序对,只需要在归并排序的代码中做一点“手脚”

 凸包

方法:若点集S中不多于五个点,穷举得到凸包,找一条垂直于x轴的中线平分S为Sl,Sr,递归地在Sl和Sr中构建凸包,使用合并程序合并Sl,Sr

合并程序

选择一个内部点p,得到几个相对于p具有递增极角的点序列,然后将这3个序列合并为1个序列,最后使用Graham扫描逐个检查这些点,并消除引起自反角的点

Graham扫描举例:

 

 

 总体时间复杂度O(nlogn)

最大字段和问题

举个栗子:

1 3 -2 -2 1 -2 2 6 -1 4

左边: 最大为4(1+3),右边:最大为8(2+6),合并的时候看中间,先从中间(1和-2之间)往左边直到看见负数停止,只有1,先从中间(1和-2之间)往有边直到看见负数停止,什么也没有,所以合并的时候max3=1

综上max=8

循环赛日程表

n个选手的表,可通过为n/2个选手设计表来决定,n/2......可通过...n/4,....,直到位2个选手设表就简单了,比如a和b比赛设表:

a     b

b     a

T(n)=T(n/2) + (n/2)*(n/2)------》 T(n)= O(n^{2})

维诺图程序设计

分治法构造Delaunay三角剖分法

首先对点集按x坐标排序,若点击内点个数<=3,那么可以直接构造,否则拆分,最后合并这两个子点集的角最优三角剖分,求对偶图,即S的·Voronoi图

O(nlogn)

补充

常见的如归并排序、快速排序也在分治范畴之中,但在前面提过,此处没有重复写

·减治

使问题变成与原问题相同的但是规模更小的子问题,解决这个子问题,延伸子问题的解决方法,从而得到原问题的解决方案

种类:

减少一个常量(如插排、拓排、全排列、求子集、dfs、bfs、堆排序)

减少一个常量因子(如二分查找、快速幂、俄罗斯乘法、约瑟夫斯、假币问题)

可变尺寸减治(如欧几里得算法、nim游戏、快排划分)

DFS

DFS(G)
{
	count=0
	每个顶点标记为0
	for each vertex v in V do:
		if v 标记为0
			dfs(v) 
}
dfs(v)
{
	count+=1;
	v标记为count
	for each vertex w adjacent to v:
		if w is marked with 0
			dfs(w) 
}

 时间复杂度:邻接矩阵O(v^2)v为顶点数

邻接表 O(v+e) e为边数

 BFS

BFS(G)
{
	count=0
	每个顶点标记为0
	for each vertex v in V do:
		if v 标记为0
			bfs(v) 
}
bfs(v)
{
	count+=1;
	v标记为count
	queue q
	q.push(v)
	while q!=empty:
		a=front of q
		for each vertex w adjacent to a:
			if w is marked with 0:
				count+=1
				marked w with count
				add w to the q
		remove a from q
}

 复杂度同DFS

拓扑排序

1.基于dfs:

 2.减一法

时间复杂度O(v+e)

bool toposort()
{
	int hh=0,tt=-1;
	for(int i=1;i<=n;i++)
	if(!d[i])q[++tt]=;
	while(hh<=tt)
	{
		int t=q[hh++];
		for each w adjacent to t:
			if(--d[w]==0)
			q[++tt]=j;
	}
	return tt==n-1;(如果不相同则有环)
}

 生成排列(O(n!))

插入法

 Johnson-Trotter法

 字典生成顺序排列

 生成子集(O(2^n))

1.可以直接使用减治法:在n-1规模的集合的所有子集中添加第n个元素

2.也可以使用位运算法,略

淘汰赛冠军问题

n个元素两两分组分别比较,得到n/2个优胜者,对其再分组,得到n/4个优胜者,以此类推,得到冠军

T(n)=n(1-1/2^k)=O(n)

快速幂

俄罗斯农民乘法

 假币问题

simplier version:

减半法 减n/3法

complex version:

不知道真假币谁轻

约瑟夫斯问题

例如L(6,2)=2*2+1

 ·变治

实例简化

预排序可以解决很多问题,比如查看某个元素是否唯一,一个序列中是否有重复元素,模式计算(找到列表中出现次数最多的值)包括topk问题,排序之后这种类型的问题都可以通过一次遍历解决

高斯消元也是一个典型的变治问题,但是这个算法通常复杂度较高(达到O(n^{3})),在商业中经常使用LU分解

表示改变

AVL树

下面简单说明avl树的构造,主要针对如何进行左旋右旋等问题(这种构造方法主要用于手算的方便)

时间复杂度:O(logn)

第一步:插入5,正常插入

第二步:插入6,正常插入

第三步:插入8,插入之后发现,存在平衡因子的绝对值大于1的结点,此时需要进行调整,下面给出四种调整策略:(可以简单看一眼,看不懂没关系,会有例子的)

1.如果平衡因子绝对值大于1的结点往“导致这个平衡因子绝对值大于1的结点”的路径上是右右,则需要进行左旋(L)调整,调整方法为,从这个“平衡因子绝对值大于1的结点”开始,向“导致这个平衡因子绝对值大于1的结点”的路径数三个点,把这三个点单独拿下来,组成一个小的avl树,然后合理融入原来的avl树中(如何合理融入,下面有例子)

 2.如果平衡因子绝对值大于1的结点往“导致这个平衡因子绝对值大于1的结点”的路径上是左左,则需要进行右旋(R)调整,调整方法为,从这个“平衡因子绝对值大于1的结点”开始,向“导致这个平衡因子绝对值大于1的结点”的路径数三个点,把这三个点单独拿下来,组成一个小的avl树,然后合理融入原来的avl树中(如何合理融入,下面有例子)

3.如果平衡因子绝对值大于1的结点往“导致这个平衡因子绝对值大于1的结点”的路径上是左右,则需要进行左右旋(LR)调整,调整方法为,从这个“平衡因子绝对值大于1的结点”开始,向“导致这个平衡因子绝对值大于1的结点”的路径数三个点,把这三个点单独拿下来,组成一个小的avl树,然后合理融入原来的avl树中(如何合理融入,下面有例子)

4.如果平衡因子绝对值大于1的结点往“导致这个平衡因子绝对值大于1的结点”的路径上是右左,则需要进行右左旋(RL)调整,调整方法为,从这个“平衡因子绝对值大于1的结点”开始,向“导致这个平衡因子绝对值大于1的结点”的路径数三个点,把这三个点单独拿下来,组成一个小的avl树,然后合理融入原来的avl树中(如何合理融入,下面有例子)

继续回到第三步的调整中,我们可以发现,刚插入8这个结点时,结点5的平衡因子绝对值是2>1,所以从这个点开始往“导致这个点平衡因子绝对值大于1”的路径上走,哪条路呢?就是从5往右到6,再从6往右到8,因为是“右右”,使用策略一,即左旋(L(5)),然后从5开始,沿着这条路径取3个结点,分别是5、6、8,组成小的avl树,融入原avl树即可,但此时去掉3个结点之后,原avl树为空,所以直接放上去即可。

第四步:插入3,正常插入

第五步:插入2,发现结点6的平衡因子为2,但是结点5的平衡因子也是2,此时选哪个呢?答案是选取最下面的平衡因子绝对值大于1的结点,因为往下走是“左左”,所以右旋(R(5)),取出结点5、3、2,变成小avl树,插入下面即可。

第六步:插入4,此时结点6的平衡因子为2,往“导致结点6的平衡因子绝对值大于1”的路径上看,发现是“左右”,所以进行LR(6),取出6、3、5,变成小avl树之后,合理融入,怎么融入其实很简单,就是把5、3、6取出来之后剩下的结点,重新按avl的规则插入这个小avl树即可

第七步:插入7,做一个RL(6),最后的融入,更简单,因为取出来的结点不包含根节点,所以就直接插入原来的位置即可。

2-3 Tree

简单了解一下插入方式即可

时间复杂度是:O(logn)

 跟avl树的插入方式一样,只不过每一个结点可以容纳1~2个结点,当结点数量到达三个时,就会“分裂”成一个小avl树

霍纳法则计算多项式

时间复杂度为O(n)

补充

堆排序、快速幂 也是经典的变治中的“表示改变”类别,但前面已经提过,不再赘述

问题规约

例子一:ab最小公倍数可以转换为求ab的最大公约数,然后用a*b除以这个公约数

例子二:通过将图的相邻矩阵提升到n次方来计算图中长度为n的路径的数量

例子三:将最大化问题转化为最小化问题,反之亦然

例子四:线性规划

例子五:农夫过河问题,转换为在图中找路径的问题,用四维0-1向量表示(人,狼,羊,菜)在河西岸的状态(在河西岸则分量取1,否则取0),共有24 =16 种状态.在河东岸的状态类似

·贪心

贪心算法两个基本的要素:1.最优子结构属性2.贪心选择策略

贪心思想:对问题求解时,总是做出当前看来是最好的选择,不从整体最优加以考虑,得到的是某种意义上的局部最优解

贪心算法优势:容易编写

贪心算法劣势:难以证明准确性、不能总是保证全局最优

找零钱问题

贪心策略:先抓25美分硬币,直到超过金额,然后是1美分硬币,然后是5美分硬币,再是1美分,即尽量从大的开始选择

失败例子:

find minimum quantity of 4,3,1 to make up 6 cents:

4+1+1 错误

3+3 正确

贪心选择何时可以得到整体最优解?当可换硬币单位是C的幂(C为正整数)

埃及分数

 举个例子:7/8

第一轮循环A=7 B=8

E=2 则第一次减去的是1/2

分数变成3/8

第二次E=3 则第二次减去的是1/3

分数变成1/24

因为A==1,所以结束循环

综上:7/8=1/2+1/3+1/24 

活动选择问题

贪心策略:选择最早结束的:

背包问题和最优装载

 对于分数背包问题,直接按性价比优先策略,即可满足整体最优O(nlogn)

但是对于0-1背包,按上述策略,不能保证全局最优

最优装载:

此处仅涉及一个属性,即重量,所以贪心策略:优先装填重量小的,可以实现全局最优

哈夫曼编码

时间复杂度O(nlogn)

信息熵、编码效率、编码冗余度计算举例

 哈夫曼树的构造已经在数据结构和离散数学中很熟悉了,此处就不说了

普里姆算法

O(n^{2}

int prim()
{
	memset(dist,0x3f,sizeof dist);
	int res=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(t==-1||dist[t]>dist[j]))
			t=j;
		}
		if(i&&dist[t]==INF)return INF;
		if(i)res+=dist[t];
		st[t]=true;
		for(int j=1;j<=n;j++)dist[j]=min(dist[j],g[t][j]);
	}
	return res;
}

 克鲁斯卡尔算法

时间复杂度:O(ElogE)

使用了并查集的知识

int kruskal()
{
	从小到大按边的权值排序
	for(int i=1;i<=n;i++)p[i]=i;//并查集 
	int res=0;int cnt=0;
	for(int i=0;i<m;i++)
	{
		a=第i条边的左顶点
		b=第i条边的右顶点
		w=第i条边的权值 
		a=find(a);
		b=find(b);
		if(a!=b)
		{
			p[a]=b;
			res+=w;
			cnt++;
		}
	} 
	if(cnt<n-1)return INF;
	return res;
}

迪杰斯特拉算法

时间复杂度O(n^{2}

int dijkstra()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	for(int i=1;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(t==-1||dist[t]>dist[j]))
			t=j;
		}
		for(int j=1;j<=n;j=+
		dist[j]=min(dist[j],dist[t]+g[t][j]);
		st[t]=true;
	}
	if(dist[n]==0x3f3f3f3f)return -1;
	else return dist[n];
}

多级调度问题

这是一个np完全问题

策略是:最长时间优先,但是它不能保证整体最优

设能同时运行的进程数为m,总共有n个进程

当n<=m时,O(1)

当n>m时,O(nlogn)

旅行商问题

有两种策略

第一钟是最近邻点策略,时间复杂度O(n^{2}

第二种是最短链接策略,时间复杂度O(nlogn)

删s个数,剩下数最小 

题意为:有一个高精度的正整数,去掉其中的s个数字之后,剩下的数字组成的新数最小:

贪心策略:每一次删除首个上升子序列的末字符,这种策略的正确性可以用反证法轻松证得

 ·动态规划

思想:将待求问题分解成n个阶段,按顺序求解子阶段,前一子问题的解为后一子问题的求解提供了有用的信息

动态规划和分治:

相同点:原问题分解成子问题,先求解子问题,从中得到原问题的解

不同点:1.动态规划中的子问题不是相互独立的,分治法中的子问题相互独立

2.动态规划碰到相同的子问题只需要查询答案,而分治法会重新求解,效率更低

动态规划和贪心:

相同点:都是求解最优化问题,都有最优子结构性质(原问题的最优解包含子问题最优解)

不同点:1.动态规划是自底向上,贪心是自顶向下

2.动态规划依赖于各子问题的解,只有在解出相关子问题之后,才能做出选择,而贪心法不依赖于子问题的解,仅在当前状态下做出最好选择

何时备忘录?何时动态规划?

所有子问题都需要至少求一次:动规  else 备忘录

0-1背包问题

0-1背包问题是一个np-hard问题,但是不是一个npc问题

蛮力法:O(2^{n}

贪心:无法求得最优解

动规:O(nw),n为物品个数,w为背包容量

递归式:其中B[K,W]是从前K个物品中选择,W是在容量为W的情况下总价值,bk是第k个物品的价值

给出代码:

for(int i=1;i<=n;i++)
{
	for(int j=1;j<=m;j++)
	{
		f[i][j]=f[i-1][j];
		if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
	}
}

空间复杂度优化为一维:

for(int i=1;i<=n;i++)
{
	for(int j=v[i];j>=0;j--)
	{
		f[j]=max(f[j],f[j-v[i]+w[i]);
	}
}

 斐波那契数列

for i=2 to n do
	A[i]=A[i-1]+A[i-2]
return A[n]

矩阵连乘问题

m[i,j]表示从矩阵i开始一直连乘到矩阵j所需要的乘法次数,比如a1:2*5矩阵

a2:5*3矩阵,a3:3*8矩阵则m[1,3]=2*5*3*8=240

矩阵连乘问题就是在矩阵之间添加括号确定乘法的次序(因为矩阵乘法满足结合律,不满足交换律)从而使得m值最小,下面是递推关系,易知i==j时,m[i,j]==0

 填表的次序与0-1背包不同,如下所示

答案是位置是在第一行的最后一列处

由S数组确定加括号的位置 

 最长公共子序列

蛮力法:O(n2^{m}

递推式:

递推式很好理解,在填表的时候,首先初始化数组行列下标为0的值都为0,如果对应位置的字符相同,那么就把左上角的值加一移到右下角,否则就填入这个位置的上边和左边最大的一个值:

 得到这个值之后,如何找到“the actual longest common sequence”?

可以从表的最右下角向左上角反推,当反推到行列有一个下标为0时,把顺序倒置即可得到所求序列,但有时候可以存在多个lcs,这时候再向上推的时候,优先向上面和左边走,就能产生分叉,如下图所示。

 编辑距离

时间复杂度O(n^{2}

先初始化,再根据递推式填表即可,很好理解 

 

 弗洛伊德算法

求多元最短路,时间复杂度为O(n^{3}

void floyd()
{
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
		}
	} 
}

最优二叉查找树

可以发现,这个的递推式与矩阵连乘很像,不同之处在于矩阵的初始化,和k的取值,在这里k可以取到j,而且第一个是C【i,k-1】而不是C【i,k】,但是填表顺序与之相同

最大子段和 

如图递归式

 最长上升子序列

f[i]:所有以第i个数结尾的上升子序列长度

f[i]=max(f[j]+1)  j=0,1,2...i-1

for(int i=1;i<=n;i++)
{
	f[i]=1;
	for(int j=1;j<i;j++)
	{
		if(a[j]<a[i])
		f[i]=max(f[j]+1,f[i]);
	}
}

·图

图的知识非常繁多,常见的有图的遍历、最短路、最小生成树、拓扑排序、连通分量、传递闭包、网络流、二分图、图着色问题

但是一方面像生成树、最短路、遍历前面已经说过,另一方面很多对于期末考试不是那么重要,在这一节仅补充部分图论知识

强连通分量-kosaraju算法

时间复杂度O(V+E)

 强连通分量-Tarjan算法

时间复杂度O(V+E)

 涉及两个数组:

dfn[i]:编号为i的结点在DFS的过程中的访问序号

low[i]:dfs过程中i的下方结点所能到达的开始时间最早的结点的开始结点

dfs遍历中:当u能达到v

if v不在栈中:low[u]=min{low[u],low[v]}

否则:low[u]=min{low[u],dfn[v]}

Tarjan算法求关节点、桥

贝尔曼-福特算法

时间复杂度:O(V*E)

拓展:

1.如果把循环次数变为k,那么最终得到的结果是从1号点到n号点最多经过k条边的距离

2.当循环了n-1轮之后,再检查一次边发现此时变还可以更新,说明一个问题:此图有负权环!!

SPFA

·分支限界法

根据选择下一个扩展节点不同分为FIFO搜索、LIFO搜索、优先队列搜索

两种解空间树:子集树、排列树

分支限界法和回溯法的区别

 FIFO分支限界检索4-皇后问题状态空间树

FIFO分支限界检索15-迷问题状态空间树

优先队列分支限界法解01-背包

 

 优先队列分支限界解多段图单源最短路

  优先队列分支限界解一般图单源最短路

看这张图,注意什么时候不能入堆,其余步骤省略,只放最后的结果 

 

 dijkstra算法和分支限界法在解决该问题的异同:

dijkstra算法:每一步的选择为当前步的最优,复杂度为O(n^2)

分支限界:每一步的扩散为当前耗散度的最优

优先队列式分支限界解TSP

 

 

 ·NP理论

本章介绍一些概念

P:是指能在多项式时间内解决的问题
NP:是指非确定性多项式问题。不能在多项式时间内解决或不确定能不能在多项式时间内解决,但能在多项式时间复杂度内被验证的问题;TSP,背包,图着色
NP-Complete(NPC):(1)它是一个NP问题,(2)所有NP问题都可以约化到它。
既然所有的NP问题都能约化成NPC问题,那么只要任意一个NPC问题找到了一个多项式算法,那么所有的NP问题都能用这个算法解决了,NP也就等于P了。(证明:假设NPC问题A在多项式时间内是可解的,并给定一个NP问题B。由于 A 是 NPC,因此 B 的任何实例都可以在多项式时间内转换为 A 的“等效”实例。然后可以通过假设在多项式时间内求解 A 的实例。组合这两种算法会产生多项式时间B的算法。),TSP
NP-Hard(NPH):NP难问题。所有NP问题在多项式时间内都能约化(Reducibility)到它的问题(不一定是NP问题),可以理解为,NP-Hard是比所有NP问题都难的问题。非对称TSP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/670276.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RecyclerView 低耦合单选、多选模块实现

作者&#xff1a;丨小夕 前言 需求很简单也很常见&#xff0c;比如有一个数据列表RecyclerView&#xff0c;需要用户去点击选择一个或多个数据。 实现单选的时候往往简单下标记录了事&#xff0c;实现多选的时候就稍微复杂去处理集合和选中。随着项目选中需求增多&#xff0c…

k8s的部署

二进制搭建 Kubernetes v1.20 k8s集群master01&#xff1a;192.168.92.30 kube-apiserver kube-controller-manager kube-scheduler etcd k8s集群master02&#xff1a;192.168.92.21 k8s集群node01&#xff1a;192.168.92.40 kubelet kube-proxy docker k8s集群node02…

阿里云热修复打补丁包注意事件

1、每次发布app到应用市场前&#xff0c;注意保存没有加固前的apk文件和mapping.txt 2、修复好bug&#xff0c;打包app前&#xff0c;要做的事情 &#xff08;1)先把有问题的apk的mapping.txt文件复制到/app路径下 (2)修改混淆配置&#xff1a;将-printmapping mapping.txt使…

Android蓝牙协议知识汇总

蓝牙协议下载 蓝牙技术联盟网址&#xff1a;https://www.bluetooth.com/ 在这个网址搜索&#xff0c;比如&#xff1a; 在搜索结果中找到蓝牙协议规范&#xff1a; 点击上面网址&#xff1a; 蓝牙手册里包含了部分核心协议&#xff0c;比如L2CAP、SDP、ATT、GATT&#x…

Python 100%解析svg-captcha验证码

前言 前段时间接到一个需求&#xff0c;登陆某一个网站&#xff0c;然后录入数据&#xff1b;本来以为是一个很简单的需求&#xff0c;结果遇到几个难点&#xff1a; 登陆的时候需要有验证码验证码是一个请求路径&#xff0c;每请求一次验证码都不一样 本来一开始以为是常用的…

探究 CoreData 使用索引(Index)机制加速查表究竟如何实现?

问题现象 在  App 的开发中,CoreData 到底能不能用索引机制(Index)来加速查表?如果可以,又该如何创建和使用索引呢? 这是一个连  官方文档都模棱两可,Stackoverflow 里诸多大神都闪烁其词的话题。 在本篇博文中,您将学到如下内容: 什么是 CoreData 索引(Index…

SpringBoot + Ant Design Vue实现数据导出功能

SpringBoot Ant Design Vue实现数据导出功能 一、需求二、前端代码实现2.1 显示实现2.2 代码逻辑 三、后端代码实现3.1 实体类3.2 接收参数和打印模板3.3 正式的逻辑3.4 Contorller 一、需求 以xlsx格式导出所选表格中的内容要求进行分级设置表头颜色。 二、前端代码实现 2…

20230524 taro+vue3+webpack5+pdfjs时打包pdfjs进不来的问题

关闭taro的terser就可以了 terser:{enable:false }

UE中创建异步任务编辑器工具(Editor Utility Tasks)

在UE中我们往往需要执行一些编辑器下的异步任务&#xff0c;例如批量生成AO贴图、批量合并静态模型等&#xff0c;又不想阻碍主线程&#xff0c;因此可以使用Editor Utility Tasks直接创建UE编辑器下的异步任务。 如果你不太了解UE编辑器工具&#xff0c;可以参考这篇文章&…

Spring Boot 中自定义数据校验注解

Spring Boot 中自定义数据校验注解 在 Spring Boot 中&#xff0c;我们可以使用 JSR-303 数据校验规范来校验表单数据的合法性。JSR-303 提供了一些常用的数据校验注解&#xff0c;例如 NotNull、NotBlank、Size 等。但是&#xff0c;在实际开发中&#xff0c;我们可能需要自定…

2023年6月24日(星期六):骑行明郎

2023年6月24日(星期六)&#xff1a;骑行明郎&#xff0c;早8:30到9:00&#xff0c; 大观公园门囗集合&#xff0c;9:30点准时出发 【因迟到者&#xff0c;骑行速度快者&#xff0c;可自行追赶偶遇。】 偶遇地点: 大观公园门囗集合&#xff0c;家住南&#xff0c;东&#xff0c…

(二叉树) 100. 相同的树 ——【Leetcode每日一题】

❓100. 相同的树 难度&#xff1a;简单 给你两棵二叉树的根节点 p 和 q&#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q …

使用代理ip做网页抓取需要注意什么

现在&#xff0c;很多公司为达成目标&#xff0c;都需要抓取大量数据。企业需要根据数据来作出重大决定&#xff0c;因此掌握准确信息至关重要。互联网上有许多宝贵的公共数据。问题是如何轻松采集这些数据&#xff0c;而无需让团队整天手动复制粘贴所需信息?网页抓取的定义越…

Qt学习11:Dialog对话框操作总结

文章目录 QDialogQDialogButtonBoxQMessageBoxQFileDialogQFontDialogQColorDialogQInputDialogQProgressDialog 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 QDialog Qt中使用QDialog来实现对话框&#xff0c;QDialog继承自QWidget&#xff0c;对话框分为**三种**&…

尿的唰唰和笑的哈哈

很多人说看不懂&#xff0c;不知道哪个是真哪个是假。我说都是真的。不同心不同理。全球并不同炎凉。窦唯有句歌词&#xff1a;天堂地狱皆在人间。何勇有句歌词&#xff1a;有人减肥&#xff0c;有人饿死没粮。&#xff08;1&#xff09;产业我过去说过顶天立地。立地&#xff…

专利背后的故事 | 一种异常信息检测方法和装置

Part01 专利发明的初衷 用户和实体行为分析&#xff08;UEBA&#xff09;在2018年入选Gartner为安全团队建议的十大新项目。UEBA近几年一直受到国内安全厂商的热捧。但是对于UEBA的理解&#xff0c;以及具体落实的产品方案&#xff0c;各厂商虽然明显不同&#xff0c;但在对账…

Go应用性能优化的8个最佳实践,快速提升资源利用效率!

作者&#xff5c;Ifedayo Adesiyan 翻译&#xff5c;Seal软件 链接&#xff5c;https://earthly.dev/blog/optimize-golang-for-kubernetes/ 优化服务器负载对于确保运行在 Kubernetes 上的 Golang 应用程序的高性能和可扩展性至关重要。随着企业越来越多地采用容器化的方式和 …

HOOPS Native Platform 2023 cRACK

将高级 3D 工作流程添加到桌面和移动应用程序 HOOPS 原生平台集成了三种用于桌面和移动应用程序开发的先进 HOOPS 技术&#xff0c;包括高性能图形 SDK、CAD 数据访问工具包和 3D 数据发布 API。 ​ ​ 构建 3D 原生应用 借助桌面和移动设备上的 HOOPS 原生平台&#xff0c;快…

一个初级程序员该在哪接项目练手?

作为一个初级程序员&#xff0c;想要通过兼职接单赚钱&#xff0c;离不开项目练手。但不得不说&#xff0c;初级程序员想要通过接私活获取收入还是相对比较困难的&#xff0c;如果对接私活比较感兴趣的朋友&#xff0c;可以参考这条路径&#xff1a; 在GitHub上学习大佬的项目…

【WebLogic】WebLogic 10.3.6.0部署应用包后报错

问题背景&#xff1a; WebLogic 10.3.6.0部署应用包后出现报错【posted content exceeds max post size】&#xff0c;此报错会导致应用部署的目标服务实例无法成功启动。 报错信息截图如下所示&#xff1a; 根据报错信息&#xff0c;查询相关MOS文档&#xff0c;发现问题原因是…