算法笔记
文章平均质量分 86
以上笔记均出自书籍《算法笔记》
李歘歘
某不知名学院毕业生、非知名厂打工人
展开
-
拓扑排序
如果一个有向图的任意顶点都无法通过一些有向边回到自身,那么称这个有向图为有向无环图(Directed Acyclic Graph,DAG)。拓扑排序是将有向无环图G的所有顶点排成一个线性序列,使得对图G中的任意两个顶点u、v,如果存在边u->v,那么在序列中uー定在v前面。这个序列又被称为拓扑序列。具体做法可以抽象为:①定义一个队列Q,并把所有入度为0的结点加入队列。②取队首...原创 2020-02-20 12:09:09 · 436 阅读 · 0 评论 -
kruskal算法
kruskal算法(读者可以将其读作“克鲁斯卡尔算法”)同样是解决最小生成树问题的一个算法。和prim算法不同,kruskal算法采用了边贪心的策略,其思想极其简洁,理解难度比prim算法要低很多。kruskal算法的基本思想为:在初始状态时隐去图中的所有边,这样图中每个顶点都自成一个连通块。之后执行下面的步骤:①对所有边按边权从小到大进行排序。②按边权从小到大测试所有边,如果当前...原创 2020-02-18 12:09:07 · 4108 阅读 · 3 评论 -
最小生成树——prim算法
最小生成树(Minimum Spanning Tree,MST)是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。下图给出了一个图G及其最小生成树T,其中较粗的线即为最小生成树的边。可以看到,边AB、BC、BD包含了图G的所有顶点,且由它们生成的树的边权之和为6,是所有生成树中权值最小的。最小生成树有3...原创 2020-02-15 10:59:53 · 13216 阅读 · 1 评论 -
Floyd算法
最短路径是图论中一个很经典的问题:给定图G(V,E),求一条从起点到终点的路径,使得这条路径上经过的所有边的边权之和最小。对任意给出的图G(V,E)和起点S、终点T,如何求从S到T的最短路径。解决最短路径问题的常用算法有Dijkstra算法、Bellman-Ford算法、SPEA算法和Floyd算法。Floyd算法(读者可以将其读作“弗洛伊德算法”)用来解决全源最短路问题,即对给定的...原创 2020-02-14 15:54:17 · 1072 阅读 · 0 评论 -
Bellman--Ford算法和SPFA算法
最短路径是图论中一个很经典的问题:给定图G(V,E),求一条从起点到终点的路径,使得这条路径上经过的所有边的边权之和最小。对任意给出的图G(V,E)和起点S、终点T,如何求从S到T的最短路径。解决最短路径问题的常用算法有Dijkstra算法、Bellman-Ford算法、SPEA算法和Floyd算法。Dijkstra算法可以很好地解决无负权图的最短路径问题,但如果出现了负权边,Dijk...原创 2020-02-14 13:32:21 · 1243 阅读 · 0 评论 -
图的遍历
图的遍历是指对图的所有顶点按一定顺序进行访问,遍历方法一般有两种:深度优先搜索(DFS)和广度优先搜索(BFS)。关于DFS和BFS以及在树中的应用请参考从树的遍历看DFS和BFS深度优先搜索(DFS)广度优先搜索(BFS)下面把它们应用于图的遍历中。1.深度优先搜索(DFS)深度优先搜索以“深度”作为第一关键词,每次都是沿着路径到不能在前进时才退回到最近的岔道口。...原创 2020-02-11 16:09:27 · 1043 阅读 · 0 评论 -
图的定义和存储
1.图的定义图,顾名思义就是地图一样的东西。抽象出来看,图由顶点(Vertex)和边(Edge)组成,每条边的两端都必须是图的两个顶点(可以是相同的顶点)。而记号G(V,E)表示图G的顶点集为V、边集为E。图一般来说,图可分为有向图和无向图。有向图的所有边都有方向,即确定了顶点到顶点的一个指向;而无向图的所有边都是双向的,即无向边所连接的两个顶点可以互相到达。在一些问题中,可以把无向图当作...原创 2020-02-10 17:01:58 · 908 阅读 · 0 评论 -
哈夫曼树和哈夫曼编码
1.哈夫曼树叶子结点的路径长度是指从根结点出发到达该结点所经过的边数。把叶子结点的权值乘以其路径长度的结果称为这个叶子结点的带权路径长度。另外,树的带权路径长度( Weighted PathLength of Tree,WPL)等于它所有叶子结点的带权路径长度之和。已知n个数,寻找一棵树,使得树的所有叶子结点的权值恰好为这n个数,并且使得这棵树的带权路径长度最小。带权路径长度最小的...原创 2020-02-07 23:25:34 · 2666 阅读 · 0 评论 -
堆及堆排序
1.堆的定义和基本操作堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。其中,如果父亲结点的值大于或等于孩子结点的值,那么称这样的堆为大顶堆,这时每个结点的值都是以它为根结点的子树的最大值;如果父亲结点的值小于或等于孩子结点的值,那么称这样的堆为小顶堆,这时每个结点的值都是以它为根结点的子树的最小值。堆一般用于优先队列的实现,而优先队列默认情况下使用的是大顶堆,因此本...原创 2020-02-06 15:39:37 · 803 阅读 · 0 评论 -
并查集
1.定义并查集是一种维护集合的数据结构,它的名字中“并”、“査”、“集”分别取自Union(合并)、Find(査找)、Set(集合)这3个单词。也就是说,并查集支持下面两个操作:①合并:合并两个集合。②査找:判断两个元素是否在一个集合。那么并査集是用什么实现的呢?其实就是用一个数组:int father[N];其中fahter[i]表示元素i的父亲结点,而父亲结点本身也是...原创 2020-02-05 17:57:19 · 1343 阅读 · 0 评论 -
平衡二叉树(AVL)
1.定义平衡二叉树使树的高度在每次插入元素后仍然能保持O(logn)的级别,这样能让查询操作仍然是O(logn)的时间复杂度。平衡二叉树由前苏联两位数学家GM.Adele-Velskil和E.M.Landis提出,因此一般也称作AVL树。AVL树仍然是一棵二叉査找树,只是在其基础上增加了“平衡”的要求。所谓平衡是指,对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1,其中...原创 2020-02-04 12:31:45 · 1313 阅读 · 0 评论 -
二叉查找树(BST)
1.定义二叉查找树( Binary Search Tree,BST)是一种特殊的二叉树,又称为排序二叉树、二叉搜索树、二叉排序树。二叉査找树的递归定义如下:①要么二叉査找树是一棵空树。②要么二叉查找树由根结点、左子树、右子树组成,其中左子树和右子树都是二叉查找树,且左子树上所有结点的数据域均小于或等于根结点的数据域,右子树上所有结点的数据域均大于根结点的数据域。从二叉査找树的定义中...原创 2020-02-04 00:01:15 · 1085 阅读 · 0 评论 -
从树的遍历看DFS和BFS
1.深度优先搜索(DFS)和树的先根遍历事实上,对所有合法的DFS求解过程,都可以把它画成树的形式,此时死胡同等价于树中的叶子结点,而岔道口等价于树中的非叶子结点,并且对这棵树的DFS遍历过程就是树的先根遍历的过程。于是可以从中得到启发:碰到一些可以用DFS做的题目,不妨把一些状态作为树的结点,然后问题就会转换为直观的对树进行先根遍历的问题。如果想要得到树的某些信息,也可以借用DFS以深度作为...原创 2020-02-03 11:55:58 · 993 阅读 · 0 评论 -
C语言变量类型及其表示范围
一般来说,基本数据类型分为整型、浮点型、字符型,C++中又包括布尔型。每种类型里面又可以分为若干种类型(为了方便记忆,只列出常用的)。如下列出了四种基本数据类型。四种基本数据类型 类型 取值范围 大致范围 整型 int -2147483648~+2147483648 (即-2^31~+(2^31-1)) ...原创 2019-10-25 11:27:33 · 12552 阅读 · 0 评论 -
树的遍历
1.树的先根遍历对一棵一般意义的树来说,总是先访问根结点,再去访问所有子树。显然,这是一个递归访问的概念,因为对根结点的子树来说,同样可以分为根结点和若干子树。这种遍历方式被称为树的先根遍历。代码如下:void PreOrder(int root){ printf("%d ",Node[root].data); //访问当前结点 for(int i=0;i<Node[root...原创 2019-10-21 21:51:03 · 1374 阅读 · 0 评论 -
树的静态写法
我们令指针域存放其所有子结点的地址(或者为其开一个数组,存放所有子结点的地址)。不过这听起来有点麻烦,所以还是建议在考试中使用其静态写法,也就是用数组下标来代替所谓的地址。当然这需要事先开一个大小不低于结点上限个数的结点数组,因此结构体node的定义会类似于下面这样:struct node{ typename data; //数据域 int child[maxn]; //指针域,存放所有...原创 2019-10-16 21:28:11 · 2318 阅读 · 2 评论 -
二叉树的静态实现
不懂树与二叉树的可以参考:树与二叉树通过下面的学习,读者应能完全不使用指针,而简单使用数组来完成二叉树的所有操作。在定义二叉树时,采用的是二叉链表的结构,如下所示:struct node{ typename data; //数据域 node* lchild; //指向左子树根结点的指针 node* rchild; //指向右子树根结点的指针 };在这个定义中,为了能够...原创 2019-10-14 19:48:51 · 1353 阅读 · 0 评论 -
二叉树的遍历——层次遍历
上一节:二叉树的遍历——先序遍历、中序遍历、后序遍历层序遍历是指按层次的顺序从根结点向下逐层进行遍历,且对同一层的结点为从左到右遍历。需要从根结点开始从上往下逐层遍历,而对同一层进行从左到右的遍历。这个过程和BFS很像,因为BFS进行搜索总是以广度作为第一关键词,而对应到二叉树中广度又恰好体现在层次上,因此层次遍历就相当于是对二叉树从根结点开始的广度优先搜索,基本思路如下:①将根结点ro...原创 2019-10-13 20:43:29 · 3585 阅读 · 0 评论 -
二叉树的遍历——先序遍历、中序遍历、后序遍历
二叉树的遍历是指通过一定顺序访问二叉树的所有结点。遍历方法一般有四种:先序遍历、中序遍历、后序遍历及层次遍历,其中,前三种一般使用深度优先搜索(DFS)实现。无论是这三种遍历中的哪一种,左子树一定先于右子树遍历,且所谓的“先中后”都是指根结点root在遍历中的位置,因此先序遍历的访问顺序是根结点→左子树→右子树,中序遍历的访问顺序是左子树→根结点→右子树,后序遍历的访问顺序是左子树→右子树→根...原创 2019-10-13 20:09:06 · 3511 阅读 · 0 评论 -
cin/cout与getline
cin与cout是C++中的输入与输出函数,需要添加头文件“include<iostream>”和“using namespace std;”才能使用。cin和cout不需要像C语言中的 scanf、 printf 函数那样指定输入输出的格式,也不需要使用取地址运算符&,而可以直接进行输入/输出,十分易用和方便。事实上,对考试而言,并不推荐读者使用cin跟cout来进行输入...原创 2019-10-11 19:17:06 · 1949 阅读 · 1 评论 -
树与二叉树
1.树的定义与性质(1)树(tree)的概念在数据结构中,树则是用来概括传递关系的一种数据结构。为了简化,数据结构中把树枝分叉处、树叶、树根抽象为结点(node),其中树根抽象为根结点(root),且对一棵树来说最多存在一个根结点;把树叶概括为叶子结点(leaf),且叶子结点不再延伸出新的结点;把茎干和树枝统一抽象为边(edge),且一条边只用来连接两个结点(一个端点一个)。这样,树就被定...原创 2019-10-10 20:15:32 · 1512 阅读 · 0 评论 -
迷宫
给定一个n*m大小的迷宫,其中*代表不可通过的墙壁,而“.”代表平地,S表示起点,T代表终点。移动过程中,如果当前位置是(x,y)(下标从0开始),且每次只能前往上下左右、(x,y+1)、(x,y-1)、(x-1,y)、(x+1,y)四个位置的平地,求从起点S到达终点T的最少步数。......*.*..*S*..***....T*上面样例S为(2,2),T的坐标为(4,3)。...原创 2019-09-27 11:24:20 · 1563 阅读 · 0 评论 -
广度优先搜索(BFS)
广度优先搜索(Breadth First Search , BFS)遍历类似于树的按层次遍历的过程,则是以广度为第一关键词,当碰到岔道口时,总是先依次访问从该岔道口能直接到达的所有结点,然后再按这些结点被访问的顺序去依次访问它们能直接到达的所有结点,以此类推,直到所有结点都被访问为止。广度优先搜索的整个算法过程很像一个队列,因此,广度优先搜索(Breadth First Search , BF...原创 2019-09-24 13:14:58 · 1832 阅读 · 0 评论 -
深度优先搜索(DFS)
深度优先搜索遍历类似于树的先根遍历,是树的先根遍历的推广,在算法竞赛和考试中经常用到,在优化图算法时,也非常方便,就像是在走迷宫过程中,当碰到岔路口时“深度”作为前进的关键词,不碰到死胡同就不回头,因此把这种搜索的方式称为深度优先搜索(Depth First Search, DFS)。深度优先搜索会走遍所有路径,并且每次走到死胡同就代表一条完整路径的形成。这就是说,深度优先搜索是一种枚举所有完...原创 2019-09-22 16:06:40 · 6362 阅读 · 1 评论 -
Link List Sorting
题目描述:A linked list consists of a series of structures, which are not necessarily adjacent in memory.We assume that each structure contains an integer key and a Next pointer to the next structure. No...原创 2019-09-21 15:14:33 · 1245 阅读 · 0 评论 -
Sharing
题目描述:To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For e...原创 2019-09-21 10:38:06 · 1508 阅读 · 0 评论 -
静态链表
一般我们使用的链表是动态链表:动态链表但若结点的地址是比较小的整数(例如5位数的地址),这样就没有必要去建立动态链接,而应该使用更方便的静态链表。静态链表的实现原理是hash,即通过建立一个结构体数组,并令数组的下标直接表示节点的地址,来达到直接访问数组中的元素就能达到访问结点的效果。另外,由于结点的访问非常方便,因此静态链表是不需要头结点的。静态链表结点定义的方法如下:st...原创 2019-09-20 18:43:54 · 1683 阅读 · 1 评论 -
链表的基本操作
链表的基本概念以及分配内存空间1.创建链表把每个结点的next指针指向下一个结点的地址即可。程序代码:#include<cstdio> //#include<stdlib>struct node { int data; //数据域 node* next; // 指针域 };//创建链表node* create(int Array[...原创 2019-09-20 12:08:35 · 5746 阅读 · 2 评论 -
链表
1.链表的概念线性表是一类很常用的数据结构,分为顺序表和链表。其中顺序表可以简单地理解成前面介绍的“数组”这个概念,而这里将要讲解一下链表。按正常方式定义一个数组时,计算机会从内存中取出一块连续的地址来存放给定长度的数组;而链表则使由若干个结点组成(每个结点代表一个元素),且结点在内存中的存储位置通常是不连续的。除此之外,链表的两个结点之间一般通过一个指针来从一个结点指向另ー个结点,因此链表的...原创 2019-09-18 11:48:01 · 1830 阅读 · 0 评论 -
队列的应用
队列(queue)是一种先进先出的数据结构,总是从队尾加入元素,而从队首移除元素。使用一个队首指针front来指向队首元素的前一个位置,而使用一个队尾指针rear来指向队尾元素,当使用数组来实现队列时,队首元素front和队尾元素rear为int型变量(数组下标从零开始);而当使用链表来实现队列时,则为int* 型变量的指针。接下来介绍队列的常用操作,包括清空( clear)、获取队列内元素的...原创 2019-09-17 13:43:32 · 1489 阅读 · 0 评论 -
栈的应用
栈(stack)是一种先进后出的数据结构(又称为后进先出的线性表),每次只能使用栈顶指针对栈顶元素进行操作(进栈出栈),栈顶指针是始终指向栈的最上方的元素的一个标记。当使用数组实现栈时,栈顶指针是一个int型的变量(数组下标从0 开始),通常即为TOP; 而当使用链表实现栈时,则为int*型的指针。当栈中没有元素时令TOP为-1,即空栈。使用stack需要在头文件中加入“#include<...原创 2019-09-17 10:50:57 · 1473 阅读 · 0 评论 -
algorithm头文件下的常用函数之lower_bound()和upper_bound()
使用algorithm头文件,需要在头文件下加一行“using namespace std”,lower_ bound()和upper_ bound()需要用在一个有序数组或容器中。lower_ bound(first,last,val)用来寻找在数组或容器的[first,last)范围内第一个值大于等于val的元素的位置,如果是数组,则返回该位置的指针;如果是容器,则返回该位置的迭代器。...原创 2019-09-17 09:50:49 · 3532 阅读 · 2 评论 -
algorithm头文件下的常用函数之sort()
顾名思义,sort()就是用来排序的函数,它根据具体情形使用不同的排序方法,效率较高。一般来说,不推荐使用C语言中的qsort()函数,原因是qsort()用起来比烦琐,涉及很多指针的操作。而且sort()在实现中规避了经典快速排序中可能出现的会导致实际复杂度退化到O(n^2)的极端情况。希望读者能通过这篇介绍来轻松愉快地使用sort函数。1.如何使用sort()排序sort()函数的使用...原创 2019-09-05 17:25:09 · 3852 阅读 · 2 评论 -
algorithm头文件下的常用函数之max()、min()、abs()、swap()、reverse()、next_permutation()、fill()
使用algorithm头文件,需要在头文件下加一行“using namespace std”。1.max()、min()、abs()max(x,y)和min(x,y)分别返回x和y中的最大值和最小值,且参数必须是两个(可以是浮点数)。如果想要返回三个数x、y、z的最大值,可以使用max(x,max(y,z)的写法。abs(x)返回x的绝对值。注意:x必须是整数,浮点型的绝对值请用mat...原创 2019-09-04 22:56:30 · 6139 阅读 · 1 评论 -
pair的常见用法详解
pair是一个很实用的“小玩意”,当想要将两个元素绑在一起作为一个合成元素、又不想要因此定义结构体时,使用pir可以很方便地作为一个代替品。要使用pair,应先添加头文件#include <utility>,并在头文件下面加上“ using namespace std;"。注意:由于map的内部实现中涉及pair,因此添加map头文件时会自动添加utiliy头文件,此时如果...原创 2019-09-04 21:38:20 · 8532 阅读 · 0 评论 -
stack的常见用法详解
stack翻译为栈,是STL中实现的一个后进先出的容器。要使用 stack,应先添加头文件include<stack>,并在头文件下面加上“ using namespacestd;"1. stack的定义其定义的写法和其他STL容器相同, typename可以任意基本数据类型或容器:stack<typename> name;2. stack容器内元素的访问...原创 2019-09-04 20:50:53 · 45041 阅读 · 7 评论 -
priority_queue的常见用法详解
priority_queue又称为优先队列,其底层是用堆来进行实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。例如在队列有如下元素,且定义好了优先级:桃子(优先级3)梨子(优先级4)苹果(优先级1)那么出队的顺序为梨子(4)→桃子(3)→苹果(1)。当然,可以在任何时候往优先队列里面加入(push)元素,而优先队列底层的数据结构堆(heap)会随时调整结...原创 2019-09-04 19:47:21 · 21695 阅读 · 4 评论 -
queue的常见用法详解
queue翻译为队列,是一个先进先出的容器,要使用queue,应先添加头文件#include<queue>,并在头文件下面加上“using namespace std;”。1.queue的定义:定义的写法和其他STL容器相同, typename可以是任意基本数据类型或容器:queue<typename> name;2. queue容器内元素的访问:...原创 2019-09-04 13:41:44 · 25174 阅读 · 2 评论 -
map的常用用法详解
map翻译为映射,也是常用的STL容器。众所周知,在定义数组时(如 int array[l00])其实是定义了一个从int型到int型的映射,比如array[0]=25、array[4]=36就分别是将0映射到25、将4映射到36。一个 double型数组则是将int型映射到double型,例如db[0]=3.14,db[1]=0.01。但是,无论是什么类型,它总是将int型映射到其他类型。这...原创 2019-09-04 10:45:16 · 3224 阅读 · 0 评论 -
string的常用用法详解
一般我们在C语言中用到字符串,都是使用字符数组来存放,但是操作字符数组有时候会不便,所以为了方便操作,在C++的STL中加入了字符串类型(string)。想要复习以往字符数组的相关知识点请点击以下链接:字符串的输入输出字符串操作字符串的sscanf和sprintf(选看)如果要使用 string,需要添加 string头文件,即# include<string> ...原创 2019-09-03 15:31:33 · 15861 阅读 · 2 评论