数据结构和算法
文章平均质量分 59
将数据结构和算法的知识放在一个专栏了,包括排序算法,查找算法,递归分治、动态回归,以及树、图的问题。
孟小胖_H
本人博客大部分都是在复习中整理的,主要用于记录笔记,其中也记录了一些个人观点和思考,欢迎指点,一起共同学习进步。
展开
-
回溯法——旅行售货员问题
旅行售货员问题即给几个地点,相互之间有路径,有每个路径对应的消耗的费用。我们将起点设为1,其他地点设为2,3,4…n。我们起初将所有路径费用都设置成∞,然后再输入 相通路径的费用,再更新费用值。我们以下图为例。如下图:注意BackTrack主要是一个求排列组合函数(排列组合函数思路见“递归分治——排列组合”博客),加上了路径和费用回溯。代码如下(适用于旅行售货员问题无向图)主要代码端在BackTraack处://回溯法//旅行售货员问题class TraveLing{private: int原创 2021-09-24 13:21:53 · 4890 阅读 · 0 评论 -
回溯法——打印子集树
打印子集树。比如说有三个元素,用0和1表示子集有或者没有这个元素,向左分支走为1,向右分支走为0,那么如下图所有路径都可以用0和1表示出来,可以用0和1完整表示子集。0就不打印对应的元素,1就打印对应的元素。比如三个元素是{1,2,3}路径为{0,1,0}的话就打印{2};路径为{1,1,0}的话就打印{1,2}。递归代码:void fun(int* ar, int* br, int i, int n){ if (i == n)//如果一条路径走完了就打印 { for (int j = 0;原创 2021-09-23 20:22:50 · 223 阅读 · 0 评论 -
回溯法——N皇后问题
N皇后问题即给一个n*n方格,将n个皇后放进去,要求皇后不能出现在同一列,同一行,也不能出现在对角线上,换句话说,只有折线才可能连接两个皇后。我们拿四皇后来举例:如图1,第三行的皇后放第一个各格子的话,和第一行的皇后处于同一列,所以不可以;放在第二个格子或者第四个的话,和第二行的皇后处于同一个对角线,所以不可以 ;放在第三个格子的话,和第二行的皇后处于同一列 ,所以不可以。图2和图3是正确的放法。在这里用一维数组就可以表示 皇后位置,比如n皇后,我们申请一个n+1空间大小的数组(申请n+1空间大小是原创 2021-09-24 10:01:23 · 373 阅读 · 0 评论 -
并查集(加权规则、折叠规则)
1.基本知识概念等价类与并查集。如果用符号“=”表示集合上的等价关系,那么对于该集合中的任意对象x,y,z,下列性质成立 :自反性:x=x(即等于自身)对称性:若x=y,则y=x传递性:若x=y且y=z,则x=z一个集合S中的所有对象可以通过等价关系划分为若干个互不相交的子集S1,S2,S3,…,它们的并集就是S,这些子集即为等价类。确定等价类的方法:第一步:读入并储存所有的等价对(i,j);第二步:标记和输出所有的等价类。2.解决步骤分析:假定给定集合:S={0, 1, 2, 3, 4原创 2021-09-23 14:40:19 · 603 阅读 · 0 评论 -
递归与分治——整数划分求划分的式子个数
问题描述:将正整数n表示为一系列正整数之和,n=n1+n2+n3+n4+…+nk (其中,n1>=n2>=n3>=n4…>=nk>0,k>=1)正整数n的这种表示成为正整数n的划分。正整数n的不同划分个数成为正整数n的划分数,记作p(n)。例如,正整数6有如下11种划分:65+1;4+2,4+1+1;3+3,3+2+1,3+1+1+1;2+2+2,2+2+1+1,2+1+1+1+1;1+1+1+1+1+1;思路:将最大加数不大于m的划分数记为q原创 2021-10-01 22:51:53 · 653 阅读 · 0 评论 -
递归与分治——斐波那契数列非递归,递归,与优化后的递归算法
斐波那契数列:1、1、2、3、5、8、13、21、……简单说,就是前两项的和是第三项的值。1、求第N个斐波那契数的值(非递归)//斐波那契数列int fun(int n){ int a = 1, b = 1, c = 1; for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; } return c;}//O(n) S(1)2、求第N个斐波那契数的值(递归)很简单,求第N个斐波那契数,我们需要求第n原创 2021-04-19 07:33:07 · 234 阅读 · 0 评论 -
递归与分治——全排列问题
递归函数:以层次来想函数递归,以深度来想递归出口。问题:给出一个集合,输入这个集合所有的排列集合。例如:输入:{1,2,3}输出:{1,2,3}{1,3,2}{2,1,3}{2,3,1}{3,1,2}{3,2,1}思路:全排列,就是不断交换两个元素,打印。将所有可以交换的两个元素都交换一遍,都打印一遍,加上本来的排列,打印出来的就是我们的全排列。所以我们焦点就放在了交换上面,怎么交换两个元素会使逻辑清晰。我们自己手写全排列的时候,一般都是先以第一个元素为首的全排列写完,再以第原创 2021-10-01 21:22:56 · 350 阅读 · 0 评论 -
递归与分治——子集问题
问题描述:给一个集合,集合里面元素都不重复,输出这个集合的子集。例如:输入:{1,2,3}打印:{1,2,3}{1,2}{1,3}{2,3}{1}{2}{3}{}//代表空集思路:我们用一个等大小的集合,集合中对应的0代表不打印这个元素,集合中对应的1代表打印这个元素。建立两颗深度为3(集合个数)的满二叉树。可以看作哈夫曼编码,每一个码代表打印的一个子集。这样所有的码都代表一个子集。比如:{1,1,1}代表{1,2,3}{1,1,0}代表{1,2}{1,0,1}代表{1原创 2021-10-01 20:13:21 · 214 阅读 · 1 评论 -
递归与分治——二分查找算法(折半查找算法)
二分搜索主要解决的问题是确定排序后的数组中是否包含目标元素val。二分搜索通过持续跟踪数组中包含元素val的范围。分为两个过程,第一就是找到了,第二个就是没找到;一开始,这个范围是整个数组,然后通过将val与数组中的中间项进行比较并抛弃一半的范围来缩小范围。该过程持续进行。1、非递归算法//12 12 12 13 13 13 45 45 56 56 56 78 89 90 100,查询val,且返回最左边(最右边)的val的下标int BinSearch(const vector<int&g原创 2021-04-19 08:25:01 · 443 阅读 · 0 评论 -
递归算法——汉诺塔问题
一年前,老师用斐波那契数列引领我走进递归的殿堂,同一天,用汉诺塔扼杀了我对递归的好感。。。。。。今天翻开算法书本来想看看有哪些经典问题被遗漏时,看到了这个汉诺塔,我才想起来当初老师讲这个问题的时候我没听懂,问题被搁置了,今天看见我才想起来,对于现在的我肯定没什么挑战,但是当初刚学递归的时候,真被它弄傻了。看一下题目吧:总的来说就是最终将所有圆盘都移动到c上面。移动过程中大圆盘不能在小圆盘上面,每次只能移动一个圆盘。思考一下,将所有圆盘都移动到c上面,那么肯定需要将[1,n-1]圆盘都移动到b上面,原创 2021-11-05 16:24:36 · 274 阅读 · 0 评论 -
找到数组中第k小的值(利用快排的划分函数)
在数组中找到第K小的值,我们利用划分函数,可以做到和快排异曲同工的效果。话不多说,先上在快排中的划分函数代码:int Parition(vector<int>& ar, int left, int right){ assert(!ar.empty()); int tmp = ar[left]; while (left < right) { while (left < right && ar[right] > tmp) --right;原创 2021-05-04 11:05:51 · 409 阅读 · 0 评论 -
排序算法——拓扑排序(卡恩算法(广度优先)、dfs+深度搜索算法)
文章目录前言一、拓扑排序规则二、卡恩算法实现1.卡恩算法思想2.代码实现三、dfs+深度优先1.算法思想2.代码实现总结前言本篇博客主要记录拓扑排序的实现。包括卡恩算法实现和dfs+深度搜索算法实现。其实这两个算法本质分别是广度优先搜索和深度优先搜索。一、拓扑排序规则首先知道入度和出度的概念,箭头指向本顶点,则本顶点的入度就+1,箭头指出。则出度+1。拓扑排序即把出度为0的结点一个一个找出来,看下例子就知道了:上图中1入度为0,所以1排在前面,此时拓扑排序为{1},将1指出去的箭头都擦去,.原创 2021-11-05 13:44:03 · 5253 阅读 · 1 评论 -
排序算法——快速排序算法
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,一部分记录的关键字比基准值小,一部分记录的关键字比基准值大,然后再对这两部分进行同样操作。1、快速排序递归算法//快速排序,递归int Parition(int* ar, int left, int right){ assert(ar != nullptr); int tmp = ar[left]; while (left < right) { while (left < right && ar原创 2021-04-20 23:57:38 · 353 阅读 · 0 评论 -
排序算法——堆排
1.堆的概念堆通常是一个可以被看做一棵完全二叉树。(1)大根堆, 就是说这个完全二叉树中每一棵子树的根节点都大于他的左右孩子(如果有的话)。(2)小根堆,就是说这个完全二叉树中每一棵子树的根节点都小于他的左右孩子(如果有的话)。在数组中:如果父节点的下标为i,则左孩子的下标为 2 * i + 1,右孩子下标为2*i+2。如果孩子的下标为j, 则父节点的下标是:( j - 1 ) / 2。2.堆的建立过程将数组中的数据调整成堆时,从最后一棵子树开始,向根节点一颗颗调整,每棵子树的调整都是从其原创 2021-09-30 20:18:16 · 612 阅读 · 0 评论 -
排序算法——归并排序
1.思想一次排序过程,将已经各自有序的两个段的数据合并一个段,并且合并后依旧有序。第一次我们认为单个数据是有序的,一个数据就是一个段,一次排序后,两个数据就是一个有序数据段,这样下一次每个有序数据段就是两个数据。最后将其合并成一个完成有序段,则整个数据就已经排序好了。第一次数据:单个元素为一个有序数据,进行俩俩合并,合并后的每段数据是有序的。第一次归并后的数据:第二次归并:上图两个数据为一个有序数据段,俩俩有序数据段合并,合并后的数据段:第三次归并:上图四个数据为一个有序数据段,俩俩有序数据原创 2021-09-29 15:35:05 · 372 阅读 · 0 评论 -
排序算法——希尔排序(缩小增量排序)
思想:现将数据分成d个组, 在每个分组内使用直接插入排序算法排序 ,目的就是使得整个数据序列越来越有序。接着将数据继续分组(分组数会越来越小),然后排序, 最后一次分组肯定为1,每个分组数一般是互质的。在这里涉及间隔取法优秀博客希尔排序详解点击此处void Shell(int* arr, int len, int group){ for (int i = group; i < len; ++i) // i负责遍历整个数据段, 控制本次直接插入排序处理那一组的数据 { int tm原创 2021-09-28 14:57:29 · 5359 阅读 · 2 评论 -
排序算法——单链表快速排序(划分函数从一边划分)
我们知道了普通的快速排序利用的划分函数是从两边向中间划分,但是对于单链表,这种划分函数可就显得不这么适用了。原因在于单链表每个节点只存在存放后一个结点的指针域,找到后一个节点容易,但是想要找到前一个结点的话,可就显得力不从心了。 所以,我们可以借用普通划分函数的思想,来写一个都从前向后划分的划分函数。 首先我们的i和j肯定都指向前面,我们仍然以第一个值为划分标准 ListNode* LinkParition(ListNode* left, ListNode* right){ ListNode*.原创 2021-05-04 10:23:52 · 293 阅读 · 0 评论 -
排序算法——冒泡排序、选择排序、直接插入排序
1.冒泡排序一趟排序的过程,将相邻的两个元素进行比较,如果前一个比后一个大,则将两个元素交换——将最大的元素交换到整个数据的最后。排序的趟数 = 数据元素的个数 = len - 1#include<iostream>using namespace std;/*时间复杂度:O(n^2)空间复杂度:O(1)稳定性: 稳定的*/void BubbleSort(int* arr, int len){ for (int i = 0; i < len; i++) { b原创 2021-09-28 14:31:31 · 284 阅读 · 1 评论 -
串匹配算法——BF算法
1.思路相当于枚举,用主串S的每一个字符作为起始位置,和子串P进行比对。如果在S串中找到了和P串相等的,则返回本次查找的起始位置。当S[i] = P[j],i和j都向后走,即i++,j++,再判断你S[i] 和P[j]是否匹配,如果匹配继续i++,j++,如果不匹配,i回到第一个和j匹配的字符的后一个字符的位置 。即如下图,此时S[i] == P[j],i和j都向后走,i一直走到下一个a,j走到d,发现S[i] != P[j],所以i回到a的字符后面,即b位置(i-j+1),j回到下标0位置。2.代码原创 2021-09-30 18:07:07 · 311 阅读 · 0 评论 -
Hash——哈希法概念、哈希函数构造方法、哈希冲突解决办法(重点讨论链地址法)
声明:本篇博客根据回顾老师上课知识和书籍《数据结构——用C语言描述》(耿国华)整理得出,仅作知识回顾学习用。1.哈希法哈希法又称散列法、杂凑法、关键字地址计算法。相对应的表称为哈希表、散列表、或杂凑表等。哈希方法思想:首先在元素的关键字k和元素的存储位置p之间建立一个对应关系H,使得 p = H(k),H成为哈希函数。创建哈希表时,把关键字为k的元素直接存入地址为H(k)的单元,以后查找关键字为k的元素时,再利用哈希函数计算该元素的存储位置。再按关键字存取元素。Hash中存储的key值都是唯一的。原创 2021-10-01 11:02:21 · 2913 阅读 · 0 评论 -
单链表——单链表逆置(不带头结点)
本博客主要记录两种解决方法(1)三指针(3)双指针(三指针优化)(2)双指针之头插法思想一、三指针思想:p1主要指向前面的一个结点p2指向中间的结点p2->next = p1;p3指向后面的一个结点,p3主要作用是当p2指向改变之后,p2结点的后面的结点如果不用一个指针指向的话,就找不到了。代码:void Reserve(ListNode*& head){ if (head == NULL || head->next == NULL) return; p1 =原创 2021-10-24 18:51:07 · 3366 阅读 · 5 评论 -
单链表——判断两个单链表(无头节点)是否相交,如果相交,返回单链表的第一个结点
本博客主要记录两个解法:1.求两个单链表的节点个数,消除结点个数不同带来的影响,两个指针一起走,相遇即相交点。2.数学方式求解。一、求结点个数,消除结点个数不同带来的影响,俩指针同步走思路:两个单链表相从相交的第一个结点开始,后面的结点都共享,所以两个单链表的节点个数之差就是相交的结点之前,两个单链表的结点个数之差,我们只需要将两个单链表结点个数之差算出来,然后消除结点个数不同带来的影响,让p1和p2同时向后走,那么相遇的时候,即相交的第一个结点,如下图:p1和p2分别从两个单链表的如下位置同时原创 2021-10-24 18:21:35 · 1892 阅读 · 0 评论 -
数组——找重复元素
给定一个数组,数组长度为n,元素值为1~n-1,也就是说这个数组里面至少有一个元素是重复的。找出一个重复的元素,并且把这个元素值返回。例如:arr[5] = {1,3,4,2,2};返回2本篇博客主要记录以下三种方法:1.元素对号入座(空间换时间)2.在原数组上不断操作,将元素换到对应的下标中3.快慢指针一、元素对号入座(空间换时间)思路:由于数组大小为n,元素值为1~n-1,我们直接申请一个n大小的空间,将元素值赋值到申请的空间下标与其值相等的单元格中,如果没有重复,正好一个单元格放一个元素原创 2021-10-22 15:54:27 · 4596 阅读 · 0 评论 -
数组——旋转数组
题目:给一个数组,和长度,还有一个k值,将数组后移k个位置。注意:超过最后的位置的数组,会循环到第一个位置。例如:arr[7] = {1,2,3,4,5,6,7}; len = 7; k = 3;旋转过后数组为:arr[7] = {5,6,7,1,2,3,4};首先k = k%len;k和k = k%len效果一样。比如上面例子,移7位和移0位一样,优化一下,k = %len。本篇博客记录以下几种方法:1.将最后一个元素拿出来保存到tmp中,其他元素都后移k个,将tmp赋值给第一个元素。重复操作k原创 2021-10-22 13:48:50 · 225 阅读 · 0 评论 -
数组——两个有序数组的合并
题目:有元素按照递增有序排列的两个数组arr,和brr,将brr的元素合并到arr中,且arr中的元素依然有序。arr的大小足够存放arr的有效元素和brr的有效元素。例如:arr[10] = {1,2,3};m=3//m为arr的有效数据个数brr[7] = {1,2,3,4,5,6,7};n = 3//n为brr的数组长度(元素都有效)合并后:arr[10] = {1,1,2,2,3,3,4,5,6,7};主要讲述三种方法:1.空间换时间(O(n),S(n))2.在arr原数组操作,从前向原创 2021-10-22 12:11:06 · 1786 阅读 · 0 评论