![](https://img-blog.csdnimg.cn/20201014180756925.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
C/C++
文章平均质量分 51
与C/C++相关的代码
thdwx
这个作者很懒,什么都没留下…
展开
-
二叉搜索树(BST)
中的所有元素值都大于它。那么基于这个性质,对于二叉搜索树的插入删除或是查找等操作的逻辑就非常清楚了。二叉搜索树是一种二叉树,但它对树中元素的顺序作了限制。在二叉搜索树中,对于任意一个结点,它的。但这都是建立在输入数据随机的情况下,如果输入数据比较有序,那么建立的树高就不会是。时需要找到插入位置,也就是最底层为NULL的结点,一次插入的时间复杂度就为。等操作都需要在树中找到相应的结点,它们的时间复杂度也就为。如果插入的数据分布比较随机,那么二叉树的树高的量级就为。(如果有)中的所有元素值都小于它,它的。原创 2023-04-27 17:05:58 · 103 阅读 · 0 评论 -
由二叉树的遍历序列确定二叉树
我们都知道,二叉树的先序和后序遍历序列可以确定二叉树的根,那么如果只通过先序或者后序序列是不能确定一个唯一的二叉树的,这是因为我们没有办法确定左右子树。而只根据二叉树的中序遍历序列,我们甚至连根都确定不了。但是通过后序与中序或是先序与中序,我们就能唯一的确定一个二叉树。这是因为,虽然先序或后序序列不能确定左右子树,但是我们可以通过得到的根在中序序列中将二叉树的左右子树确定。原创 2023-04-26 14:48:06 · 645 阅读 · 0 评论 -
线索二叉树
个指针都是空指针,这就浪费了大量的空间。我们可以使用这些空着的指针域来存储该结点的前驱或是后继结点的地址,这就是二叉树的线索化。如果使用二叉链表来实现二叉树,每个结点就有两个指针域,分别指向其左孩子和右孩子。个指针域,但是二叉树中除过根节点外,每一个结点只需要一个指针来链接,也就是说,对于。个结点的二叉树,我们只使用了其中。个结点的二叉树来说,总共就有。原创 2023-04-26 20:32:46 · 70 阅读 · 0 评论 -
图—拓扑排序
当节点数量过多时,这个算法的性能就会越来越差,并且在查找未访问入度为0元素时,已被访问过的元素实际上不用再进行考察了,新的入度为零的元素也一定只会在更新邻接顶点入度时产生(最开始图中入度为0的顶点只有一个,因为如果有两个入度为零的顶点,那么不论从它们之中的哪个出发,都遍历不到另一个,所以即使有两个或以上入度为零的顶点,它们的拓扑排序也是独立的),所以实际上只需要在很小的范围内找入度为0的顶点,而这个算法执行了大量没有意义的操作,所以性能也会变差。显然,如果图含有圈,那么拓扑排序是不可能的,因为如果存在。原创 2022-10-19 21:16:20 · 936 阅读 · 0 评论 -
贪婪算法(Huffman编码)
如果一个算法分阶段的工作,并且在每一个阶段都认为所做的决定是最好的,而不考虑将来的后果,这样的算法就叫做贪婪算法。贪婪算法只考虑当前局部的最优解,而不去考虑全局的情况,如果最终得到的结果是全局最优的,那么算法就是正确的,否则贪婪算法就只能得到一个次优解。如果不要求得到绝对的答案,那么使用贪婪算法得到次优解也是可以的,否则就需要使用更复杂的算法来得到准确结果。原创 2022-10-26 19:05:46 · 1470 阅读 · 0 评论 -
分治算法(选择问题等)
将问题分解为较小的问题,并递归解决小问题(基本情况除外)。从所有子问题的解中构建原问题的解。一般认为,在程序中至少含有两个递归的算法就是分治算法,如果只有一个递归函数(例如,快速幂等),那么通常是将原问题转化为更简单的问题来解决,例如求阶乘的递归写法:只是将n的阶乘转化成了n乘(n-1)的阶乘,并没有将原问题进行分解。在分治算法中,子问题通常是不相交的,如果子问题相交,例如斐波那契数列的递归写法,实际上并没有将问题真正分解(因为,原创 2022-10-27 21:40:42 · 1334 阅读 · 0 评论 -
动态规划(Dynamic Programming)
我们清楚的知道使用分治算法来求解决斐波那契数列的效率惊人的低,其中的原因是,斐波那契数列分解成的两个子问题并不是独立的,它们之间有着非常多的交集,而在递归中,这些交集会被计算成百上千次,从而降低了算法的效率(这也是为什么分治算法要求子问题尽可能的不要相交)。如果观察斐波那契数列的通项公式,我们会发现数列的第n项只与它之前的两项有关,那么知道这两项也就得到了,这种从子问题出发,逐步得到原问题解的思想就是动态规划。原创 2022-10-28 17:27:25 · 2316 阅读 · 0 评论 -
欧拉路径(欧拉环游、欧拉回路)
我们能想到的第一个特性是,如果一个无向图要具有欧拉回路,那么图必须是连通的并且图中的每一个顶点的入度都必须是偶数。如果刚好有两个顶点的入度为奇数,那么就可能存在一条从一个顶点出发,最终回到另一个顶点的欧拉环游,但是必须只有两个顶点的入度为奇数,如果多于两个顶点,那么欧拉环游也是不存在的。解决这个问题的办法是,将访问过的边删掉之后,从剩余图中尚未访问的边的路径上的第一个节点开始再进行深度优先搜索,直到所有边都被遍历一次。事实上,一个无向图存在欧拉回路的充分必要条件就是:图是连通的并且所有顶点的度都为偶数。原创 2022-10-25 15:57:35 · 3093 阅读 · 0 评论 -
单源赋权最短路径
单源赋权最短路径指给定一个赋权图和一个输入顶点,从出发到图中其他顶点的最短路径就称为单源赋权最短路径。解决单源赋权最短路径问题的一般方法叫作Djkstra算法(迪杰斯特拉算法)。迪杰斯特拉算法是一种贪婪算法,它是分阶段进行的,在每个阶段都认为选择是最好的。迪杰斯特拉算法只能解决非负权值的最短路径问题。带有负值边的赋权最短路径原创 2022-10-21 14:06:56 · 741 阅读 · 0 评论 -
回溯算法(BackTracking)
回溯算法的一个具体例子是在新房子里摆放家具,开始什么也不摆放,然后每件家具被摆放在房间的某个位置,如果所有的家具都被摆放得令户主满意,那么算法终止;如果摆到某一步,该步之后的所有摆放方法都不能满意,那么就需要撤销这一步,并尝试其他的摆放方法,如果发现撤销了所有可能的第一步,就不存在令人满意的摆放方法。当然,若给定该问题的一个解,则可以通过对所有的点加上一个偏移量而构建无穷多其他的解。中的最大元素(最大的距离对应的点的位置只有两处,且点的位置只需要参考两个端点),显然。中最大的元素8,由于。原创 2023-02-27 19:09:59 · 470 阅读 · 0 评论 -
最小生成树—Prim算法
我们要讨论的问题是如何在一个中找到它的最小生成树,虽然这个问题对有向图也有意义,但是处理起来更麻烦。一个无向图 G 的最小生成树就是连接 G 上所有顶点的边构成的树,且这些边的总权值最低。当且仅当图是才有最小生成树。在无向图的最小生成树中,边的条数为。最小生成树是一棵树,因为它无圈;边的总权值最低,所以它最小;包含所有顶点,所以是生成树。显然,最小生成树是包含所有顶点的最小的树。如果我们需要给一所房子里安装电路,那么这就是一个最小生成树问题。对于任意生成树,如果向树中增加一条不属于树的边。原创 2022-10-22 11:31:19 · 1691 阅读 · 0 评论 -
图的若干定义及表示
但最终我们还是需要输出顶点的名字,所以还需要完成数字到名称的转换,可以使用数组来保存名称,但如果名称过长,需要的空间就很大,另一种方法是可以使散列表中存放结构体,结构体中既有保存编号的变量,也有保存对应名称的变量。机场可以看为顶点,航线(如果存在)可以看为边,图中的边是有权的,权可以是油耗、时间等费用,显然图是有向的,因为两个机场之间可能只能单向通行。但在大部分应用中,图都是稀疏的,例如上面的例子,矩阵用大量的空间来存放并没有用的0值,只有很少一部分是有效值,所以邻接矩阵法是一个代价很大的并不实用的方法。原创 2022-10-19 18:39:06 · 444 阅读 · 0 评论 -
深度优先搜索
首先,标记A为已知,接着选择对B进行深度优先搜索;在退回到C这一层时,对D进行深度优先搜索之后,退出所有递归程序。的一个邻接顶点,对其进行深度优先搜索,这样就递归的遍历了图的所有顶点。当图中有圈时,需要注意在选择邻接顶点时,要选择未知的邻接顶点(通过只对尚未访问的顶点进行深度优先搜索,能够避免进入死循环)。如果图不连通,就需要调用多次深度优先搜索,每次都生成一棵树,最终会形成深度优先搜索生成森林。当且仅当从任意节点开始的深度优先搜索访问到图中的每一个顶点,无向图是连通的。深度优先搜索是对先序遍历的一般化。原创 2022-10-25 15:25:44 · 1128 阅读 · 0 评论 -
单源无权最短路径
由于在循环过程中,不断地考察了一些已知的顶点和一些已经处理过的未知顶点,所以算法的效率较低。我们可以参考拓扑排序的改进来对这个算法进行改进,我们希望在处理当前定点后处理那些未知的并且距离已经赋值的顶点,而不对已知的或是未被处理过的顶点进行考察,所以可以使用一个队列来存放将要被处理的顶点。到此就得到了所有顶点的最短无权路径,这种搜索一个图的方法称为广度优先搜索,这种方法按照层处理顶点,首先处理输入顶点的邻接顶点,再处理邻接顶点的邻接顶点,很像树的层序遍历。距离为1的顶点,这些顶点可以通过考察。原创 2022-10-20 14:42:55 · 663 阅读 · 0 评论 -
图—双连通性
但实际上,不存在一个遍历一定是后序或是先序遍历,如果在后序遍历之前对当前顶点进行操作,那么这个遍历也就既是后序也是先序,所以可以使用一次遍历就找到图中的割点。然后,我们从A顶点出发,A,B,C,D顶点所能到达的最低顶点都是A(1),因为它们都能通过D的一条背向边到达A,所以它们的。如果一个无向图不是双连通的,那么删除后使得图不再连通的顶点就叫做割点,这些节点在许多应用中都是很重要的。根据上述说明,要得到一个无向连通图中的割点,首先要进行一次深度优先搜索来得到所有的。使图连通,所以“仅当”也是成立的。原创 2022-10-24 18:39:33 · 749 阅读 · 1 评论 -
随机化算法
在随机化算法中,至少使用了一次随机数。该算法的运行时间不只依赖于特定的输入,还取决于生成的随机数。一个随机化算法的最坏运行时间几乎总是和非随机化算法的最坏运行时间相同。重要的区别在于,好的随机化算法没有不好的输入,而只有坏的随机数。例如,在快速排序中,如果使用第一个元素作为枢纽元,那么对于基本上已排好序的输入来说,运行时间可能会达到最坏的。但如果随机数选择的好,就不会达到这个最坏时间界。原创 2022-11-04 16:40:25 · 750 阅读 · 0 评论 -
优先队列(堆)
在一些用户环境中,需要优先处理队列中的一些元素。例如,在一堆文件中有一项文件非常重要,需要对它进行优先处理,如果使用队列的话,则必须逐个出队直到该文件,这是没有办法对该文件进行优先处理的,所以就提出了一中特殊的队列(优先队列)来解决这个问题。优先队列至少允许下面两种操作:Insert(插入)、DeleteMin(删除最小者)。使用一个带有表头的链表,插入在表头以O(1)时间复杂度实现,删除则需要遍历该链表,时间复杂度为O(N)。原创 2022-10-09 20:48:08 · 631 阅读 · 0 评论 -
再散列和可扩散列
如果使用之前的方式来实现散列,假设一次find需要探测散列中的三个元素,那么当数据放入外存中时,一次find就需要访问3次硬盘,但访问硬盘是一个开销很大的操作,这时候为了提高程序的性能,借鉴了B树的结构,定义了可扩散列。当使用开放定址法时,所有的关键字都存在数组中,但是数组的大小是固定的,而数据量可能会逐渐增加,当数组中存放的关键字大于一半时,即λ>0.5时,对其进行插入、查找等操作的耗时就会增加,这会降低程序的性能。当再散列应用在交互中时,新空间的开辟以及原数据的转移可能会使用户感到明显的延迟。原创 2022-09-26 18:34:02 · 361 阅读 · 0 评论 -
AB11 合并两个排序的链表
开始时,没有注意到题目要求的O(N)时间复杂度,采用了先将两个链表合并,再进行排序的方法,采用冒泡排序时间复杂度为O(N^2),使用别的排序方法应该可以优化(目前我只会冒泡排序)。输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。数据范围:0≤n≤1000,−1000≤节点值≤1000。要求:空间复杂度 O(1),时间复杂度 O(n)原创 2022-09-21 23:00:47 · 149 阅读 · 0 评论 -
散列(哈希)
那么关键字就可以在10007的空间内均匀分布,但不幸的是,单词的字母并不是随机分布的,所以单词的前三个字符的组合并没有想象的那么多,这也就造成了这个散列函数并不能将单词均匀的分配到表中,因为有许多单词的前三个字母是相同的。以上例子说明散列函数的选择是非常重要的,在设计散列函数时一定要记住对散列函数的要求,要尽可能的使关键字分布均匀,并且函数要尽可能地简洁,而对于无法避免的冲突,则有两种解决冲突的方法:分离链接法、开放定址法。理想的散列表是一个大小固定的数组,数组中的关键字可以是整型、字符串或是结构体等。原创 2022-09-24 19:27:27 · 158 阅读 · 0 评论 -
排序—希尔排序
但由于最后一次排序之前,数组已经变得大体上有序,所以这次插入排序花费的时间并没有这么多。使用希尔增量的希尔排序性能并不是很好,使用Sedgewick增量的希尔排序的性能是更好的,并且在实践中使用Sedgewick增量的希尔排序要快于堆排序。最后一次插入排序之后,数组就变得有序,虽然最后一次排序是一个标准的插入排序,而插入排序的时间复杂度为。希尔排序由多次插入排序组成,只是每次插入排序的增量不同。希尔排序使用一个增量序列。选择为数组元素个数的一半,每执行完一次插入排序,就将增量缩小为它自身的一半,直到。原创 2022-10-11 17:55:27 · 335 阅读 · 0 评论 -
分离链接法
解决散列冲突问题的一种常用方法是分离链接法。其做法是将散列到同一值的元素都保存在一个表中。为方便起见,这些表都有表头,如果不需要进行删除的话,那么也可以不使用表头。原创 2022-09-24 19:49:28 · 407 阅读 · 0 评论 -
排序—桶排序
插入排序时间复杂度为,不需要额外的空间,一般输入比较少或是已经基本排序的情况下可以使用插入排序希尔排序也称缩小增量排序,时间复杂度为,不需要额外的空间,使用sedgewick增量序列的希尔排序时间复杂度可达到,在实践中性能好于堆排序堆排序使用二叉堆实现的排序算法,时间复杂度为,不需要额外的空间,在实践中很少使用归并排序时间复杂度为,采用递归,所以需要额外的栈空间,一般使用于外部排序快速排序时间复杂度为,采用递归,所以需要额外的栈空间,是目前最快的排序算法桶排序时间复杂度为。原创 2022-10-18 19:25:42 · 179 阅读 · 0 评论 -
树的定义、存储结构及森林的有关概念
树可以用几种不同的方式定义,一种自然的定义树的方法是递归。一棵树是一些节点的集合,这个集合可以是空集,当非空时,则一棵树由根节点root以及0个或多个非空的子树组成,这些子树中的每一棵的根都由来自根root的一条边连接。每一棵子树的根叫做root的儿子,根root是每一棵子树根的父亲。root没有父亲节点。T1到T5是root的子节点,root是它们的父节点。下面看一棵更复杂的树:没有儿子的节点称为树叶(叶节点):A、B、E、D、T4、T5。原创 2022-09-22 18:32:33 · 95 阅读 · 0 评论 -
开放定址法
分离链接法由于需要实现两个数据结构,并且需要使用指针操作(分配内存非常耗时),这就使得分离链接法的速度稍慢。开放定址法是另一种解决冲突问题的方法,它不需要任何指针操作,所以性能也就稍微好一点。开放定址法是指,在产生冲突时,就尝试向后找到一个空的单元来存放关键字。开放定址法的算法为其中,为产生冲突时重新计算的索引值,为哈希函数,为为解决冲突引入的跟i有关的函数。使用开放定址法时,所有的关键字都要放在散列表中,所以需要的散列表要比分离链接法大(分离链接法关键字都存在链表中)。原创 2022-09-24 22:32:10 · 5004 阅读 · 1 评论 -
排序—归并排序
由于归并排序是由递归来实现的,所以理清基本的排序实现原理就可以完成对函数的编写,而不必深究递归的具体过程(这通常是复杂的,实际上递归最后会在只有两个元素的子数组中进行,然后将这个子数组分为两部分,并将这两个部分合并,其实也就是通过合并操作来使数字在正确的位置上)。的时间复杂度完成排序,其采用分治和递归的方法,将要排序数组分为两部分并分别进行排序,最后将排好序的两部分进行合并就完成了整个排序操作。虽然均使用了递归的方法,但归并排序在内存中的排序性能却不如快速排序好,更多的是用于外部排序。原创 2022-10-16 22:21:22 · 141 阅读 · 0 评论 -
排序—快速排序
在排序时,可能会出现数组中有等于枢纽元的元素,为了解决这种情况,只需要在遇到等于枢纽元的元素时停止,并进行交换即可,如果不对等于枢纽元的元素进行交换,而是跳过他,那么当输入全部为枢纽元时,就会产生数组越界的错误。随机选择数组中的三个数,并使用它们的中值作为枢纽元,但是随机数的产生开销巨大,一般的做法是,选择数组最左边、最右边以及中间的元素,选择它们三个中间的中位数来作为枢纽元,这样虽然选择的数字两边可能分布不均匀,但是这种不均匀一般是轻微的。快速排序是目前实践中最快的算法,它的平均运行时间是。原创 2022-10-17 23:59:15 · 214 阅读 · 0 评论 -
排序—插入排序
对于 P=1 趟到 P=N-1趟,每一趟排序开始前都能保证从 0 到 P-1 位置上的元素是已经排好序的。在第 P 趟中,将位置 P上的元素向左移动到正确地位置上,当所有趟数执行完成后,整个数组就是有序的。插入排序最坏的时间复杂度是O(N^2),当输入序列是正序时,如果要将其逆序排列,就满足了插入排序的最坏时间界。对于如上数组,数组下标从0开始,对于第 P 趟排序,将位置 P 上的元素与之前的所有元素进行比较,当发现有元素比 P 位置上的元素小时,将 P 位置的元素插入到该元素之后,就完成了一趟排序。原创 2022-10-11 00:13:10 · 149 阅读 · 0 评论 -
平衡二叉树 (AVL树)
我们希望对于二叉查找树,除MakeEmpty外的操作都花费O(logN)时间,但是,由于我们在进行删除操作时,总是用右子树中的一个节点来代替被删除元素,这就会导致在一系列删除之后,左子树的深度要明显的深于右子树,在25万次随机插入/删除之后,图中的树看起来明显的不平衡。当树不再平衡后,那么每次操作的时间就可能不再是O(logN),这对于性能而言是有害的。一个解决方法就是:要有一个称为平衡的附加结构条件,任何节点的深度不能过深。AVL树是一种典型的平衡二叉树。AVL树是带有平衡条件的二叉查找树。原创 2022-09-23 18:17:35 · 184 阅读 · 0 评论 -
数据结构—二叉树
二叉树是一棵树,其每个节点的儿子数不能多于2.下图是一般的二叉树:二叉树的平均深度要比N(数据量)小得多,其平均深度是O(根号下N)。而对于二叉查找树来说,其平均深度为O(logN).而当二叉树退化成链表时(即每个父亲节点只有一个儿子),它的深度可以大到N-1.二叉查找树是有序的,其任意节点的左子树的节点都小于它,右子树的节点都大于它。原创 2022-09-22 19:18:29 · 642 阅读 · 0 评论 -
排序—堆排序
对于N个元素的排序,首先找到下标为N/2的元素mid(由二叉堆的结构特性可知,mid后的元素一定没有孩子节点,所以它们已经满足堆序性),mid(包括mid)之前的元素都可能有孩子节点,那么借鉴于deletemin,将该位置的节点值保存在临时变量中,并且将该位置视为空穴,则通过下沉操作,可以使以该节点为根节点的子堆满足堆序性,当完成下标为0元素的下沉后,整个数组就变成了一个二叉堆。之后的操作都是相似的,当堆的大小为2时,就是最后一趟排序,因为在这次排序中,就决定了。的大小,整个数组的排序也就完成了。原创 2022-10-12 15:06:00 · 440 阅读 · 0 评论 -
Jam的计数法
Jam的计数法在Jam数字中,每个字母互不相同,而且从左到右是严格递增的。每次,Jam还指定使用字母的范围,例如,从2到10,表示只能使用{b,c,d,e,f,g,h,i,j}这些字母。如果再规定位数为5,那么,紧接在Jam数字“bdfij”之后的数字应该是“bdghi”。(如果我们用U、V依次表示Jam数字“bdfij”与“bdghi”,则U,且不存在Jam数字P,使U原创 2022-09-13 20:40:46 · 250 阅读 · 0 评论 -
BC160 小q的数列
BC160 小q的数列小q最近迷上了各种好玩的数列,这天,他发现了一个有趣的数列,其递推公式如下:f[0]=0 f[1]=1;f[i]=f[i/2]+f[i%2];(i>=2)现在,他想考考你,问:给你一个n,代表数列的第n项,你能不能马上说出f[n]的值是多少,以及f[n]所代表的值第一次出现在数列的哪一项中?(这里的意思是:可以发现这个数列里某几项的值是可能相等的,则存在这样一个关系f[n'] = f[n] = f[x/2]+f[x%2] = f[x]...(n'原创 2022-09-12 20:12:44 · 180 阅读 · 0 评论 -
程序的环境和预处理
程序的环境和预处理原创 2022-09-06 22:20:47 · 135 阅读 · 0 评论 -
最大子序列的和问题的解
【代码】最大子序列的和问题的解。原创 2022-09-12 15:17:57 · 54 阅读 · 0 评论 -
队列的链表实现
【代码】队列的链表实现。原创 2022-09-17 15:28:02 · 138 阅读 · 0 评论 -
队列的循环数组实现
队列是一种特殊的表,它限制插入只能在队尾,删除只能从队头删除。队列ADT的基本操作有enqueue(入队,从队尾),dequeue(出队,从队头)。原创 2022-09-21 14:50:30 · 137 阅读 · 0 评论 -
BC119 小乐乐与字符串
小乐乐与字符串在庆祝祖国母亲70华诞之际,老师给小乐乐出了一个问题。大家都知道China的英文缩写是CHN,那么给你一个字符串s,你需要做的是统计s中子序列“CHN”的个数。子序列的定义:存在任意下标a < b < c,那么“s[a]s[b]s[c]”就构成s的一个子序列。如“ABC”的子序列有“A”、“B”、“C”、“AB”、“AC”、“BC”、“ABC”。原创 2022-09-12 19:12:31 · 79 阅读 · 0 评论 -
表ADT(抽象数据类型)
表ADT(抽象数据类型)抽象数据类型是指一系列操作的集合。抽象数据类型是数学上的抽象,在ADT的定义中并没有指出这些操作的具体实现,只给出了操作的功能。当需要使用ADT时,由程序员根据所需要的操作来进行编写,需要进行ADT中的操作时只需要调用已经写好的函数即可。一般处理的是形如 A1,A2...An 的表,这个表的大小为n,大小为0的表称为空表。对除空表外的表,称 Ai+1 为 Ai 的后继,称 Ai-1 为 Ai 的前驱,不定义 A1 的前驱,也不定义 An 的后继。表ADT中有:PrintList(原创 2022-09-18 23:17:17 · 527 阅读 · 0 评论 -
atoi函数实现
【代码】atoi函数实现。原创 2022-09-11 23:41:39 · 127 阅读 · 0 评论