- 博客(159)
- 收藏
- 关注
原创 第八章——最短路径
最短路径问题相关概念的算法操作详见这篇文章。这里只对Dijkstra算法和Floyd算法的代码进行介绍。Dijkstra算法BFS可以求某点到另一点的最短路径长度,Dijkstra算法主要是求某点到其余所有点的最短路径长度(你用BFS慢慢搞也行,时间复杂度会很大)。关于Dijkstra算法会不会出现已经出队的某个确定路径的点不是最短路径的情况呢?其实大家用反证法想一下,如果出现这样的情况,这个点又怎么会率先出队呢?这样就能够明白了。至于很多博客,书上面说的关于路径中出现环的问题,在边权为非负值
2021-12-29 21:00:37
457
原创 第八章——生成树和最小生成树
生成树和最小生成树相关概念和算法思路详见这篇文章。这里只对Prim算法和Kruskal算法的代码进行介绍。Prim算法U为当前的连通子图所包含的顶点集合,Prim算法的本质就是每次在V-U的顶点与U的顶点的连边中选出一条加入连通子图,U同时相应地改变,直到U使得图中的所有点连通。//v为最小生成树的起始点,closest[i]为将编号为i的结点加入到集合U中,加入到连通子图的边的另一个端点//lowcost[i]为当前U中的顶点能够与编号为i的结点相连的边的最小花费,如果无法加入或者已经加入花
2021-12-29 16:24:04
225
原创 第八章——图
图的基本概念从离散数学的角度来看,图由两个集合V和E组成,其中V是图中顶点的有限集合,记作V(G),E是图中两个顶点(注意这两个顶点不一定是完全不同的)连接成的边的有限集合,记作E(G)。如果G中存在两个顶点连接成的边是笛卡尔积,即存在某一个点到另一个点的方向,那么称G为有向图,否则为无向图。在有向图中,<i,j>和<j,i>是两条不同的边。图的基本术语这里主要从数据结构来看,就不从离散的角度来那么详细地解释这些术语了。端点和邻接点对于无向图中的某条边<i,j&g
2021-12-29 01:29:32
1696
原创 第七章——二叉树
二叉树的基本概念二叉树类似于2次树,但和2次树有一些不同:1.度为2的树至少有一个结点的度为2,而二叉树没有这种要求。简而言之二叉树可以退化成一条链。2.度为2的树可以不区分左右子树,二叉树中左右子树(结点)的次序严格区分排列。有关满二叉树等基本术语和树那里一样,逻辑表达法和树那里也是一样的。除此之外的有:若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面一层的叶子的结点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树。完全二叉树还可以定义为:一棵深度为k,n个结点的二叉
2021-12-25 01:44:36
3282
原创 第六章——广义表
广义表的基本概念广义表是线性表的推广,和线性表一样按照线性结构存储n个元素。和线性表的区别就在于,广义表中的元素可以是一个原子类型(就是不能往下进行分解的,例如整型类,字符类),也可以是一个广义表,这个表示广义表的元素就成为广义表的子表,这些元素之间是满足相对次序的。广义表的长度定义为最外层包含元素的个数,即子表相对于父表(我这里就成为父表了)的长度为1。广义表的深度定义为所含括号的重数,其中原子的深度为0,空表的深度为1。例如对于下面则会个广义表:GL=((),(e),(a,(b,c,d))),G
2021-12-10 15:16:45
1988
原创 第六章——稀疏矩阵
稀疏矩阵的基本概念稀疏矩阵也是一种比较特殊的矩阵类型,但比起上一节提到的特殊矩阵类型,它特殊的地方不在于元素的分布而是在于稀疏矩阵中非0元素的个数s相对于矩阵元素的总个数t非常小。例如一个100*100的矩阵,若其中只有100个非0元素,就可称其为稀疏矩阵。稀疏矩阵中元素的位置分布一般是随机的。稀疏矩阵的三元组表示三元组就是指用三种属性来表示某个节点。由于稀疏矩阵的元素分布一般没有规律,就是说不能用数学公式来减少属性的个数,所以在存储非0元素时必须同时存储该非0元素对应的行下标,列下标和元素值。
2021-12-10 00:30:55
6900
4
原创 第六章——数组
数组的基本概念一维数组就是n个相同类型数据元素构成的有限序列。二维数组很多人都比做矩阵,我个人理解上可以看作是以一维数组作为元素的一维数组。推广到d(d>=3)维数组,就是说d维数组是以d-1维数组作为元素的一维数组。d维数组的抽象数据描述基本运算:initarray(&A):初始化数组,为数组A分配空间Destroyarray(&A):销毁数组,释放数组A的存储空间Value(A,index1,index2···,indexd):A是已存在的d维数组,index1,
2021-12-08 17:01:57
596
原创 7.5 数位DP
7.5 数位DP所谓数位DP,就是某些问题如果以数作为状态的属性进行转移,数值过大会导致时间和空间的复杂度过高。然而问题中n位数的情况可以由i位数(1<=i<n)的情况直接得到,于是我们可以以数的长度和数某位的值作为状态的属性进行转移。下面以一道题为例。hdu 2089 “不要62”一个数字,如果包含4或者62,它是不吉利的。给定m和n,统计m~n中吉利数的个数。分析:如果直接用dp[i]表示1-i,当n的值过大时显然复杂度太高。考虑用数位dp,对于长度为i的符合条件的数,在它的首位
2021-12-07 13:39:49
127
原创 7.4 树形DP
7.4 树形DP树形DP在紫书上9.4那章节提到过一部分,但介绍的非常简单(也可能是当时水平有限),现在在黑书上进行更专项的学习。树形DP是指在“树”这种数据结构上进行的DP:给出一棵树,要求以某种最优的方式(最少的代价或者最大收益)完成给定的操作。由于树的结构本身具有一定递归的性质(树和子树的关系),比起线性DP,树形DP的状态转移方程更加直观。树的最大独立集(类题)我们在紫书上是学习过树的最大独立集问题的,那为什么这里要加个类题呢?首先这是原问题:树的最大独立集对于一棵n个结点的无根树,
2021-12-04 22:48:08
781
原创 7.3 区间DP
7.3 区间DP紫书中的最佳矩阵链乘问题和最佳三角剖分问题就是区间DP问题,区间DP和之前基础DP的区别就在于(我个人理解):基础DP状态与状态之间的关系往往是线性的,第i+1个状态可以由第i个状态得到。二维线性DP中,第i行第j列的状态往往由第i行第j-1列的状态,或第i-1行第j列的状态,或第i-1行第j列的状态与第i行第j-1列的状态共同得到。区间DP的状态(i,j)往往不是指一个结点,而是指一段端点为i和j的区间。在解题中,先解决小区间的问题,再合并小区间得到更大区间问题的结果,直到解决最后的
2021-12-01 11:09:41
370
原创 7.2 递推与记忆化搜索
7.2 递推与记忆化搜索记忆化搜索就是DP过程中的一种小手段,往往能够减少非常多的重复计算。poj 1163 “The Triangle”给定一个n层的三角形数塔,从顶部第一个数往下走,每层经过一个数字,直到最底层。注意,只能走斜下方的左边一个数或者右边一个数。问所有可能走到的路径,最大的数字和是多少?分析:这个问题就是紫书上dp部分作为序章的数字三角形。如果像下面这样常规的递归写法:int solve(int i,int j){ return a[i][j]+(i==n?0:max(solv
2021-11-29 12:56:15
274
原创 7.1 基础DP
7.1 基础DPDP在紫书那里也有一个单章介绍这个专项,不过当时并没有正式的介绍DP思想而是从例题直接入手的,黑书这里从基础开始一步一步讲解。首先DP和贪心,分治法一样,DP并不指一个特定的算法,而是一种解决问题的算法思想,简单解释来说是将一个复杂的问题分割成相对简单的子问题,再一个个解决,最后得到复杂问题的最优解。与分治法不同的是,分治法的子问题之间互相独立,每个子问题能够独立解决。DP的子问题,之间互相联系,最终解即是子问题的其中之一。7.1.1 硬币问题有n种硬币,面值分别为v1,v2··
2021-11-28 22:37:56
511
原创 6.2 分治法
6.2 分治法紫书那里也学习到了分治法,不过只是对分治法的运用进行了讲解,没有一个系统性的介绍,黑书就补上了这一点。首先什么是分治法?分治法和贪心一样我个人理解来就是一种解决问题的思维角度,贪心法是考虑局部最优整合成整体最优,分治法是将难以直接解决的大问题划分成一些规模比较小的子问题各个击破,如果这k个子问题还不够小,将它划分成规模更小的子问题,直到子问题的求解能够很容易求出即可。能够使用分治法的题目需要符合以下两个特征:1.平衡子问题:子问题的规模大致相同,能把问题划分成大小几乎相等的k个子问题,
2021-11-19 22:24:03
483
原创 6.1 贪心法
6.1 贪心法贪心法在紫书那里就学过了,就是将一个问题分割成若干个部分,然后在某些部分中采取对于当前部分或者进行到当前部分的整体最优的策略,知道所有的部分处理完全;在每一步的进行中都不考虑对后续步骤的影响,也不往回更改之前的策略。如果只使用贪心法,贪心法的适用性并没有那么高,但如果将贪心法结合其他的暴力算法,往往能带来意想不到的结果(例如A*算法,最小生成树算法,Dijkstra算法)。6.1.1 常见问题黑书上是以最少硬币问题为例的,但事实上最少硬币问题,如果稍微改一下参数,就不一定能够通过贪心
2021-11-19 02:23:14
985
原创 5.4 树状数组
5.4 树状数组树状数组我个人理解上就是能够解决线段树的一部分问题的一个简化版本的线段树,通过二进制的一些运算进行查询和更新。树状数组的概念假设对于长度为n的数列{a1,a2······an},有如下两种操作:1.修改元素add(k,x):2.求和sum(x):求a1到ax的和(ai到aj的和可以用两个sum值相减)。树状数组的代码如下:...
2021-11-17 19:29:21
341
原创 5.3 线段树
5.3 线段树如雷贯耳的一种数据结构。主要用于优化反复对序列某段区间进行运算求值和修改区间内点的值的问题的时间复杂度。5.3.1 线段树的概念线段树是用于处理区间问题的数据结构,其中每个结点代表一段区间[L,R] (一条线段),下图即是线段[1,10]的线段树结构。对于每个结点[L,R]:1.L=R,说明这个区间内只有一个点,它就是叶结点。2.L<R,说明这个区间内至少有两个点,同时这个区间可以被对折分成两个子节点,左子节点对应的区间为[L,M],右子节点对应的区间为[M+1,R],其
2021-11-15 22:13:06
588
原创 5.2 二叉树
5.2 二叉树比起紫书上二叉树的基础知识,黑书拓展了一些特殊的树形结构。5.2.1 二叉树的存储二叉树的基本概念就不再多做介绍,对一些用语不太明白的详见紫书6.3 树和二叉树。关于二叉树的存储结构,一般用指针来实现:struct node{int value; node *l,*r;}l和r分别指向当前结点的左孩子和右孩子,当新建一个node时用new来申请内存,使用完毕时,用delete进行删除。二叉树的遍历主要就是两种遍历方式,宽度优先遍历和深度优先遍历。宽度优先遍历紫书中的遍
2021-11-03 17:18:44
290
原创 5.1 并查集
5.1 并查集并查集是一种精巧且实用的数据结构,主要用于处理一些不相交集合的合并问题。如果还记得紫书十一章内容的同学,这个概念肯定不是第一次见了,在讲解Kruskal算法时就已经简单介绍过这种数据结构:其中图上的结点分割成一个一个不相交的点集合,并查集将一个集合中的点用一种类似于链表(递归)的方法存储着,集合中的点不存在次序关系,只存在是否属于某个“帮派”的属性,优势就在于两个集合的合并就像链表的插入一样非常之快。当时也提到了并查集可能遇到的特殊情况:树退化成一条长链子从而降低查找速度。解决的方法是在遍
2021-10-25 19:11:05
322
原创 4.4 DFS
4.4 DFS黑书的这一章节和上一章节一样汇总了DFS部分的知识和内容。4.4.1 DFS和递归上一章已经介绍过了BFS和DFS两种算法思想,我们这里就直接看hdu1312使用DFS的解法。void DFS(int dx,int dy){room[dx][dy]='#'; num++;//标记已经搜索过的区域防止重复遍历 for (int i=0;i<4;i++){int newx=dx+dir[i][0],newy=dy+dir[i][0];//向四个方向进行深搜 if (CH
2021-10-20 21:40:24
215
原创 第五章——递归
递归的定义当定义一个过程(往往是函数)时出现调用本过程(函数)的成分称为递归。拿紫书的一个例子来说:递归:参见“递归”。递归:如果还是没有理解,参加“递归”。当然这两个递归都是一个死循环,实际使用时需要避免这样的递归。这种直接调用自身的递归,称为直接递归。如果过程或函数p调用过程或函数q,而q又调用p的递归称为间接递归。任何间接递归算法都可以转换为直接递归。如果一个递归过程或递归函数中的递归调用语句是最后一条执行语句,则称这种递归调用为尾递归。直接看下面这个比较简单的例子。例 5.1设计一个
2021-10-20 11:32:25
507
原创 第四章——串的模式匹配
串的模式匹配首先什么叫串的模式匹配?设有两个串s和t,要在串s中找到与t相等的子串。通常将s称为目标串,t称为模式串,这种串的定位查找也称为模式匹配。对于这个问题,常见的两种算法是BF算法和KMP算法。Brute-Force 算法Brute-Force(暴力)算法简称BF算法,也称简单匹配算法。基本思路是采用穷举的方法,一个一个字符进行匹配,如果以第i个字符开头的子串与t不相等,那么枚举第i+1字符开头的子串。BF算法过程模拟:i和j分别从第一位开始匹配:直到匹配某一位失败(如果没有匹配
2021-10-16 15:12:19
2469
原创 第四章——串
串的定义字符串简称为串,是由零个或多个字符组成的有限序列。一个串中任意连续字符组成的序列称为该串的子串。串的抽象数据类型描述串的基本操作比较多。StrAssign(&s,cstr):将字符串常量cstr赋给串s,即生成其值等于cstr的串sDestroyStr(&s):销毁串,释放串s分配的存储空间StrCopy(&s,t):串复制,将串t赋给串sStrLength(s):求串长,返回串s中字符的个数Concat(s,t):串连接,返回由两个串s和t连接在一起形成的
2021-10-15 13:44:25
777
原创 第三章——队列
队列的定义队列简称队,也是一种操作受限的线性表,其限制为仅允许在表的一端进行插入,在表的另一端进行删除操作,把进行插入的一端称为队尾(rear),向队列插入新元素称为进队或入队(enqueue),新元素进队之后成为新的队尾元素;从队列删除元素称为出队或离队(dequeue),元素出队后,其直接后继元素就成为新的队首元素。由于队列元素进出的规则,队列又称为先进先出(FIFO)表。需要注意的是,以上的规则仅对普通队列成立,双端队列两端都可以进行插入和删除。队列的抽象数据类型描述了解基本运算:Ini
2021-10-11 20:39:09
968
原创 4.3 BFS
4.3 BFS紫书那里我们当时是先学习的二叉树的BFS,再学的简单图的BFS,最后往更深入的学习回溯,路劲寻找问题和A*算法,IDA*算法,黑书这里将这些汇总了起来放在一章节进行介绍(只是希望不要像上上章那么坑就好)。4.3.1 BFS和队列首先要知道BFS(广度优先搜索)和DFS(深度优先搜索)都是最基本的两大暴力搜索技术,常用于解决最简单的图和树的遍历问题。那么如何区分和理解两种算法呢?我们以老鼠走迷宫为例,有两种不同的方法:1.一只老鼠走迷宫。它在每个路口都选择先走某个固定的方向,直到碰壁无
2021-10-11 13:13:37
175
原创 4.2 子集生成和组合问题
4.2 子集生成和组合问题简单叙述一下,就是求n个元素的集合的所有子集。紫书这里提供了三种不同的写法:增量构造法,位向量法和二进制法(我清晰地记得这篇是520那天写的)。其中常用的是增量构造法和二进制法,增量构造法写起来和理解起来比较苦难,但是时间效率高。#include<stdio.h>//根据前面的经验我们显然可以知道这里的算法是通过递归实现的 void print_subset(int n,int* A,int cur){//说实话这里的cur我个人觉得写作len记作当前子集长
2021-10-07 21:28:08
109
原创 第三章——栈
栈的定义事实上关于栈,我在紫书学习那里已经简单地介绍过有关应用的一部分了,但是为了更深入地学习(为了过数据结构这门课),我们还是对书上的知识进行更深入地学习。栈是一种只能在一端进行插入和删除操作的线性表。表中进行操作地一端称为栈顶,表的另一端称为栈底。当栈中没有数据元素时称为空栈,栈的插入操作通常称为进栈或入栈,删除操作通常称为出栈或退栈。栈得主要特点是“后进先出”(LIFO),即后进栈的元素先出栈,每次进栈的数据元素都放在原来栈顶元素之前成为新的栈顶元素,每次出栈的数据元素都是当前栈顶元素。栈
2021-10-07 09:10:17
741
原创 4.1 递归和排列
4.1 递归和排列递归emmm,老生常谈了,不过还要重申一个非常重要的问题即是:常规的递归实际上没有降低算法的复杂度,只是简化了代码而已。接下来看看黑书提出的3个问题。问题 4.1打印n个数的全排列。分析:用stl里面的next_permutation遍历一遍的方法就不再提了,上一章节已经用过一道例题进行说明了,至于递归的方法emmm,紫书上面提供过一种做法:#include<cstdio>#include<algorithm> using namespace st
2021-10-06 23:52:34
120
原创 3.3 next_permutation()
3.3 next_permutation()这个函数在紫书全排列那章节提过几句,但提的不多。这个函数的主要作用就是根据给定的排列求出字典序中它的下一个排列。需要注意的是,和书上说的不同,如果是最后一个排列访问它的下一个排列,我们得到的是整个全排列的第一个排列。具体的实操应用直接看书上的例题吧。hdu 1027 “Ignatius and the Princess Ⅱ”给定1-n,要求输出字典序第m小的序列。分析:1-n的全排列字典序最小的即是1,2,3·····n,即从这个排列一直向下next_p
2021-10-06 21:09:04
115
原创 3.2 sort()
3.2 sort()其实就是对sort()这个工具进行了介绍,有关紫书sort的部分在第四章函数那里,这里带大家简单地回顾一下。首先基本格式:sort(指针(迭代器)1,指针(迭代器)2,cmp函数)就是对两个指向之间的元素进行排序,具体的区间为:[first,last)。cmp函数表示比较函数,即排序后的序列元素与元素之间应当遵循的规则,如果不加,就默认按照从小到大的顺序进行排序。cmp函数的写法和优先队列那里的·····,不大一样(至少我习惯的写法不太一样),优先队列那里是像下面一样写一个结
2021-10-06 07:23:22
93
原创 3.1 容器
3.1 容器前两章都是一些介绍过的概念,我们直接从第三章开始进行学习。这里的容器指的主要就是STL容器,STL容器主要分为顺序式容器和关联式容器。顺序式容器包括vector,list,deque,queue,prioritiy_queue,stack等:vector:动态数组,即不定长数组,能从末尾进行快速的插入和删除(也仅能从末尾),同时满足数组的特性——根据索引直接访问元素。list:双链表queue:队列,FIFOdeque:双端队列,比起传统队列,可以从前面或后面快速插入与删除,并且直
2021-10-06 01:01:56
111
原创 黑书学习日志
学习目标:对ACM算法竞赛进阶的篇章进行学习学习内容:学习时间:须知:学习内容:主要就是围绕算法竞赛入门到进阶展开学习的(标出来的内容都是我感觉比较重要的或者说必须要了解的)黑书(看过我紫书专栏的都知道这里就相当于一个目录):学习时间:一些空余时间须知:1.首先这个博客是我自己写的用来娱乐和复习,所以描述语言用的都不是非常专业,有些地方甚至会有问题,欢迎大家前来沟通交流。2.由于是学习算法,所以我用的是 Dev-C++ 5.11,CV的时候请务必注意自己使用的编译器和环境哦3.因为是自己
2021-10-02 15:12:45
150
原创 11.4 网络流初步
11.4 网络流初步网络流是一个适用范围相当广的模型,相关的算法也非常多。这里我们开始初步(注意是初步)了解这类问题。百度百科的介绍如下hhhh:网络流(network-flows)是一种类比水流的解决问题方法,与线性规划密切相关。网络流的理论和应用在不断发展,出现了具有增益的流、多终端流、多商品流以及网络流的分解与合成等新课题。网络流的应用已遍及通讯、运输、电力、工程规划、任务分派、设备更新以及计算机辅助设计等众多领域。还是直接看问题吧hhhh。11.4.1 最大流问题假设需要把一些物体从结
2021-10-02 05:43:11
268
原创 第二章——有序表
有序表有序表,其实就是指所有元素以某种顺序排列的一个线性表。为了简单叙述,我们这里假设有序表中的元素递增排序。(循环表就不单独拿出来说了,之前ji)
2021-09-26 11:33:56
846
原创 第二章——双链表和循环双链表
线性表——双链表和循环双链表双链表的定义在链表那里就介绍过了,这里就不重复介绍了。双链表和循环双链表同样地,我们用结构体来声明链表中的一个结点:struct DLinkNode{ElemType data; DLinkNode *prior; DLinkNode *next;};//定义双链表结点类型,prior指向前一个结点,next指向后一个结点 和单链表基本是一样的。双链表和循环双链表基本运算的实现这里会提供双链表和循环双链表基本运算的实现及代码。建立双链表——头插法和单链表
2021-09-22 11:26:31
558
原创 11.3 最短路问题
11.3 最短路问题第九章我们介绍过无权和带权DAG上的最短路最长路问题。这里给图上加上环。11.3.1 Dijkstra 算法Dijkstra算法适用于边权为正的情况,它可用于计算正权图上的单源最短路(就是从单个源点出发,到所有节点的最短路),该算法同时适用于有向图和无向图://初始化所有的结点为未标记,设d[0]=0,其他d[i]=INF//循环n次{// 在所有未标号的结点,选出d值最小的结点x// 给结点x标记,对从x除法的边(x,y),更新d[y]=min{d[y],d[x]+w(
2021-09-20 05:40:31
304
原创 11.2 最小生成树
11.2 最小生成树主要就是学习求最小生成树的两个算法和做例题,算法我在另外一篇博客已经全部介绍过了,直接看例题吧。11.2.2 竞赛题目选解11-2 苗条的生成树 (UVA 1395)给出一个n结点的图,求苗条度(最大边减最小边)尽量小的生成树。分析:这里是讨论边的关系,于是考虑用类似Kruskal(后面简称k法)的方法。k法不仅可以求最小生成树,还可以判断若干条边是否能够在图中构成一个生成树。将边按权值从小到大排序,对于一个连续的边集[E1,E2],如果这些边能够构成一个生成树(用k法判断
2021-09-18 21:11:34
126
原创 Kruskal算法和Prim算法
Kruskal算法和Prim算法在无向图中,连通且不含圈的图称为树。给定一个无向图G=(V,E),连通G中所有点,且边集使E的子集的树称为G的生成树,其中权值最小的生成树称为最小生成树(MST)。构造MST的算法有很多,最常见的即是Kruskal算法和Prim算法。Kruskal算法...
2021-09-18 18:16:12
1078
原创 第二章——单链表和循环单链表
线性表——链表顺序表需要事先占用一整块实现分配大小的存储空间,但是对于某些问题:很多空间只使用一次(甚至根本用不到),使用顺序表存储空间的利用率往往很低。于是需要一种能够动态管理存储空间的存储结构——链表。线性表的链式存储结构——链表线性表的链式存储结构我们简称为链表。其中每个存储结点不仅包含元素本身的信息(称为数据域),而且包含表示元素之间逻辑关系的信息,在C和C++中用指针实现,称为指针域。一种最常见,最基本的方法是在每个结点除包有数据域以外只设置一个指针域,用于指向其后继结点,这样构成的链表
2021-09-17 01:25:51
2955
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅