自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(39)
  • 收藏
  • 关注

原创 剑指Offer题解 | #把数字翻译成字符串# 动态规划

剑指Offer「把数字翻译成字符串」的动态规划解法

2022-10-04 21:03:42 694 1

原创 剑指Offer题解 | #礼物的最大价值# 动态规划(图解)

剑指Offer题目「礼物的最大价值」动态规划解法

2022-10-04 20:55:59 395

原创 C#中的接口(interface)

C#-接口接口的命名规范I+名词接口与抽象类的区别接口是由抽象类演变而来的。抽象类是未完全实现逻辑的类,其内部可以有抽象成员,也可以有非抽象成员;且子类在覆写抽象成员时,需要修饰符override。而接口是完全未实现逻辑的,其内部只允许存在抽象成员而不能包含非抽象成员,即不能包含数据成员和静态成员;且成员是隐式默认public和absolute的,不允许显式地写出来;子类在实现接口的方法时,不需要修饰符override。接口声明只能包含如下类型的非静态成员函数的声明:方法、属性、事件、索引器

2021-10-28 11:44:59 1395

原创 C#中的屏蔽、覆写和抽象

屏蔽基类成员虽然派生类不能删除其继承的任何成员,但可以声明一个与基类成员签名相同的成员来屏蔽之。(注意方法的签名由名称和参数列表组成,不包括返回值类型)要让编译器知道你在故意屏蔽继承的成员,可使用new操作符,否则会出现警告。namespace TestConsole{ class Human { public void Action() { Console.WriteLine("labour..."); }

2021-10-27 16:20:18 821 1

原创 C#中的扩展方法

当我们想为一个类增加一个方法,但该类是系统提供的类,无法修改其源代码;且该类是一个密封类,无法继承它;或者是其他设计原因,我们无法通过修改或继承来为该类添加方法时,我们就可以使用扩展方法。在如今的开发中,扩展方法是一种特别有用的工具,事实上,几乎整个LINQ库都是通过扩展方法来实现的。下面我随便举一个例子,比如我现在要给Form类添加一个方法://首先要声明一个类,该类必须声明为“static”,命名一般为“Extend+目标类名”public static class ExtendForm{

2021-10-17 17:17:11 382

原创 深拷贝与浅拷贝(C++)

浅拷贝:简单的赋值拷贝操作举一个简单的例子:class Person{public: int age = 0; //无参构造函数 Person() { cout << "调用无参构造函数" << endl; } //有参构造函数 Person(int a) { age = ; cout << "调用有参构造函数" << endl; }

2021-10-13 12:20:18 74

原创 C#中的事件(event)

事件模型(event model)事件模型的5个组成部分事件拥有者(event source)(类对象)(有些书将其称为事件发布者)事件成员(event)(事件拥有者的成员)(事件成员就是事件本身,事件不会主动发生,其只会在事件拥有者的内部逻辑的触发下发生。)事件响应者(event subscriber)(类对象)(有些书将其称为事件订阅者)事件处理器(event handler)(事件的响应者的成员)(根据拿到的事件参数/信息对事件进行处理)事件订阅(委托类型)举个栗子:“裁判员开枪,运

2021-10-13 12:08:25 9801 3

原创 C#中的委托类型

C#的委托,类似C++里的函数指针,相当于是给函数取了一个别名,其实质上是一种引用类型,使用前需要先创建一个委托对象;一个委托对象可以持有一个或多个方法的引用,持有的方法可以是实例方法,也可以是静态方法。系统提供的委托类型ActionAction是一种至少0个参数、无返回值的泛型委托://格式:Action act = new Action(对象名.方法名); //实例化对象act.Invoke(); //调用(Invoke可省略,即写成:act();)//举个例子,假设此时存在一个类Tes

2021-10-05 14:45:42 665 1

原创 访问容器元素时下标操作与at函数的区别(C++)

对于C++的STL内的顺序容器,可以使用下标或at函数对容器内的元素进行访问,如://假设存在一个容器vector<int> v,现在要访问其第i个成员,下面两种方式是等价的v[i]v.at(i)二者的区别在于,如果下标越界(编译器并不检查这种错误),在运行时at函数会抛出一个out_of_range的异常,而下标操作不进行任何输出或直接运行奔溃:void test1(){ vector<int> v; //定义一个空容器 cout << v

2021-04-28 14:57:50 1101

原创 建议使用构造函数初始值列表而非赋值(C++)

首先看一下两者的区别://初始值列表Student(string n1,int n2,int a):name(n1),num(n2),age(a){};//赋值Student(string n1,int n2,int a){ name=n1; num=n2; age=a;}以上代码中,前者与后者的效果是一样的,有时我们可以忽略构造函数初始值列表和赋值之间的差异,但并非总能这样。如果成员是const或者引用的话,就必须使用初始值列表,若用赋值的方式将产生编译错误:cl

2021-04-25 10:58:37 149

原创 委托构造函数(C++)

所谓委托构造函数即是使用它所属的类的其他构造函数执行它自己的初始化过程,或者说它将自己初始化的工作委托给了其他构造函数。其形式是将构造函数初始化时的初始值列表替换为调用其委托的构造函数。例如:class Student{public: //普通的构造函数 Student(string n1,int n2,int a):name(n1),num(n2),age(a) { cout << "调用普通构造函数" << endl; };

2021-04-25 10:49:46 1654

原创 含有可变形参的函数(initializer_list)(C++)

功能:有时我们无法预知应该向函数传递几个实参,为了编写能够处理不同数量实参的函数,C++11提供了两种主要方法:如果所有实参类型相同,可以传递一个名为initialzer_list的标准库类型;如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。还有一种是C语言的省略符形参varargs。今天主要介绍一下initialzer_list。initializer_list是一种模板类型,定义在同名的头文件中,其提供的操作有:initializer_list<T> lst

2021-04-17 09:06:46 243

原创 哈希表(C/C++)

特点:哈希表也称散列表,即在记录的存储地址(散列地址)和它的关键码(散列表)之间建立一个确定的对应关系(散列函数),如此,不经过比较,一次读取就能得到所查元素的查找方法。散列表一般不适用于多个记录有同样关键码的情况;也不适用于范围的查找,如不能找到最大值或最小值。散列函数的常用设计方法:1、直接定址法:散列函数是关键码的线性函数,即 H(key)=a*key+b适用于:事先知道关键码,关键码集合不大且连续性较好。2、除留余数法:确定一个合适的整数p,H(key)=key mod p (mod:取

2020-12-28 17:01:40 258

原创 平衡二叉树(C/C++)

描述:平衡二叉树是二叉搜索树的改进,本文基于你已了解二叉搜索树为前提,不了解的朋友请看这里:https://blog.csdn.net/Ivan_47/article/details/111831396当我们使用二叉搜索树时,若我们按递增或递减的顺序插入结点,那么每一次的新结点都会插入在左子树或右子树,这样一来二叉树便退化为了链表,那么搜索的效率就会降低。为提升二叉排序树的平均效率,可将其升级为平衡二叉树,其特点是:所有根结点的左子树和右子树深度差不超过1。其中左右子树的深度差也称为平衡因子。每插入

2020-12-28 11:06:22 358

原创 二叉搜索树(C/C++)

描述:二叉搜索树又称二叉查找树或二叉排序树,其特点是:树中所有的结点,若存在左子树,其左子树结点的值一定小于其值;若存在右子树,其右子树结点的值一定大于其值;且树中所有结点的值都是唯一的(或者说关键字是唯一的),不允许存在重复的结点值(或关键字)。//定义结点typedef struct BSTree{ int data=0; BSTree *ltree=nullptr,*rtree=nullptr,*parent=nullptr;//三个指针,分别指向左右子树及父结点}BSTre

2020-12-28 10:09:33 123

原创 图的最短路径问题(Floyd算法)(C/C++)

问题描述:在给定的带权有向图G=(V,E)中,对于任意的顶点vi、vj,求vi到vj的最短路径。思路:假设图中有n个顶点,对于从顶点vi到vj的路径,进行n次循环:第一次迭代加入顶点v0作为中转点,考虑路径vi→v0→vj是否存在,如果存在,则比较vi→vj与vi→v0→vj的长度,取较短者作为vi到vj的最短路径;第二次迭代再添加一个顶点v1作为中转点,比较并选择较短的路径,以此类推。经过n次迭代后,便求得了vi到vj的最短路径。辅助数组:为了不对邻接矩阵的值造成破坏,定义一个二维数组dist,

2020-12-27 20:23:03 715

原创 归并排序(C++)

思路:将序列对半分为两个子序列,两个子系列排好序后,再合并为一个序列,且合并后任然有序。对于子序列,也可以再分为两个更小的子序列,重复上述过程。很明显是一个递归的过程,将序列不断平分,直到不可再分。那么含有n个元素的序列最后便会被分为n个子序列,然后子序列进行两两有序归并,再对归并后的n/2个有序子序列再进行有序归并,如此重复,直到所有子序列归并为一个序列。代码://有序归并:用于把左右两个排序好的子序列合并起来void Merge(vector<int> &v,int b

2020-12-27 15:38:16 117

原创 快速排序(C++)

思路:一趟排序:在给定的序列中选定一个基准数,将序列中值比基准数小的元素都移动到其左侧,值比基准数大的都移动到其右侧;然后返回基准数的下标。快速排序:对序列进行一趟排序,然后按照返回值将序列分为左右两个子序列(递归)。两个子序列会重复上述过程,然后分为更小的序列,以此类推,直到序列不可再分。具体实现看以下代码,注释很详细。代码://一趟排序:将序列分为两部分int myPartition(vector<int> &v,int beg,int end){ int l

2020-12-27 15:13:15 233

原创 插入排序(直接、二分、希尔)(C++)

插入排序(直接、二分、希尔)直接插入排序:将一组无序的序列看作是已排序和未排序的两个部分,设置一个循环,每次都从未排序部分中取一个元素出来插入到已排序部分中,且要保证插入后已排序部分仍然有序。要将一个元素插入到一个序列中的某一个位置(该元素在该序列的尾端),其实就是将该元素不断与前一个元素交换,直到达到目标位置停止。可以设置一个伪指针指向未排序部分的首元素,让该元素向前移动,直到其前一个元素值比其小才停止,然后伪指针后移一位(缩小未排序部分范围)。void InsertSort(vector&lt

2020-12-27 12:47:46 155

原创 堆排序(C++)

堆排序是简单选择排序的升级,两者思想相同,都是将一组无序的序列看作已排序部分和未排序部分,然后从未排序部分选择一个元素加入已排序部分,直到所有元素都完成排序。不了解简单选择排序的朋友可以看一下这里:https://blog.csdn.net/Ivan_47/article/details/111772569思路:构造:首先将序列中待排序的部分构造成一个堆。堆即是一棵完全二叉树,此处用数组存储二叉树,根据二叉树的性质可知,结点 i 的双亲结点为 i / 2 、左子树为 2i、右子树为 2i+1(假

2020-12-27 10:40:22 215

原创 简单选择排序(C++)

思路:可以将一个无序的序列看作两部分,已排序的部分和未排序的部分,设置一个循环,每次都从未排序的部分中选择一个值最小的元素,与未排序部分的首元素交换位置,然后此时的首元素就加入了已排序部分,未排序部分缩小范围,重复上述过程。具体做法:设置一个伪指针负责指向未排序部分的首元素,此时整个序列都未排序,所以指向序列的首元素。接着从未处理的部分中选择一个最小的元素与该指针所指的元素交换。然后伪指针右移一位,继续处理未排序的元素,直到伪指针到达序列终点结束算法。代码:void SelectSort(ve

2020-12-27 09:11:33 360 2

原创 图的最短路径问题(Dijskra算法)(C/C++)

问题描述:在给定的带权有向图G=(V,E)中,求V集合的中源点v0到其余各点的最短路径。思路:设置一个集合S存放已经找到最短路径的顶点(初始时先将源点v0存入),对于其余顶点,每次都会找一个从源点出发路径最短的顶点,就将其放入集合中,最新放入集合的顶点会成为源点v0通往其他顶点的辅助顶点,即源点v0可以借助集合中的其他顶点到达集合外的某一顶点,但不可以借助集合外的任意顶点到达其他任意顶点。重复上述过程,直到所有顶点都放入集合中。辅助变量:定义一个数组dist,其下标对应顶点数组下标,表示

2020-12-24 19:53:17 876 2

原创 图的拓扑排序问题(C/C++)

描述:在有向无环图,顶点之间存在先后关系,遍历某个顶点前必须前遍历其前驱顶点。拓扑排序一般应用于计划的安排:完成一项任务后才可进行下一项。思路:输出图中入度为0的顶点,并记录其下标,之后删除该顶点所连接的弧,重复这个过程直到图中找不到入度为0的顶点为止。如果执行完算法后图中还有顶点没有输出,则说明图中存在环,输出错误信息。代码://邻接矩阵的定义typedef struct AMGraph{ int vex[ARRAYSIZE]={0};//顶点数组 int arc[ARR

2020-12-23 15:11:19 188

原创 图的最小生成树的两种算法(Prim & Kruskal)(C/C++)

Prim算法:假设有两个集合V和U,V存放图中已处理(已加入最小生成树)的顶点,U存放图中未处理的顶点。每次从集合U中选取一个与 集合V中的顶点 之间权值最小的顶点。例如初始时选定某一顶点a加入集合V,然后从集合U中选取一个与集合V中顶点之间权值最小的顶点,此时V中只有a,所以就是选择U中与顶点a之间权值最小的顶点,假设选中了顶点b,将其也加入集合V,此时最小生成树有a和b两个结点,即集合V中有两个顶点a和b,所以要从U中寻找与顶点a或顶点b之间权值最小的顶点,重复这个过程,直到集合U中不再有

2020-12-22 20:49:47 840

原创 图的深度与广度优先遍历(DFS & BFS)(C/C++)

先介绍一下两种图的结构邻接矩阵:由一个一维数组存储图中的顶点,由一个二维数组表示元素之间的关系,用行和列以及其中的值表示两个顶点之间是否有边(弧),如第2行第3列的值为1,则表示第2个元素与第3个元素之间存在边。typedef struct AMGraph{ int vex[ARRAYSIZE]={0}; int arc[ARRAYSIZE][ARRAYSIZE]; int vexnum=0,arcnum=0;//顶点个数、弧个数 int type=0;//1表示无向

2020-12-21 20:15:30 267

原创 汉诺塔问题的思路与实现(C/C++)

问题描述:一共n个盘子,从上往下依次按从小到大摆放,有a、b、c三个柱子,需要把n个盘子从a全部搬运至c,要求小盘子不允许摆放在大盘子之上。思路:可以先将a上的n-1个盘子搬运到b,a剩余的一个盘子再搬运到c,最后b上的n-1个盘子再搬运到c。而b上的这n-1个盘子也依然可以这么做,先把n-2个搬运到a,剩余一个搬运到c,a上的n-2再搬运到c。如此反复,直到要搬运的盘子只剩一个,即n-(n-1),此时直接搬运到c即可。很明显这是一个递归的过程。在递归函数中,4个参数分别代表:此次要搬运的盘

2020-12-18 12:29:42 373 1

原创 哈夫曼树的实现(C/C++)

建立:为了使得树的带权路径最短,需要将权值较大的结点放在较底层,较小的放在较上层。实现这个过程需要借助队列结构。用队列存储结点地址,每次选取权值最小的两个结点并出队(可以事先排好次序),申请一个新结点作为它们的父结点,并将父结点入队。重复这个过程直到队列只剩一个结点,即根结点。//定义结点typedef struct HuffmanTree{ int weight=0;//权值 HuffmanTree *lchild=nullptr;//左子树 HuffmanTree *

2020-12-17 23:33:16 258 1

原创 树的孩子兄弟表示法(C/C++)

创建:和二叉树一样结点内部拥有两个指针,不过不是指向左右子树,而是指向孩子结点和兄弟结点。兄弟结点之间互相连接,在同一层,孩子结点在下一层,结构像广义表,45度观看又像二叉树。创建方式类似二叉树的层序建立,也需要借助队列结构。先处理根结点,获取第一个输入字符,保存至根结点中,将该结点入队,然后进入循环:数据优先存放到某结点的子树中,然后再存放到其兄弟结点中。获取队头结点,并让其出队。获取输入字符,将其保存至当前结点的长子结点(第一个孩子结点,即直接与父结点相连的子结点)中,然后长子结点入队,接下来在

2020-12-17 22:48:25 3850 2

原创 二叉树的括号法建立与遍历(C/C++)

括号法表示二叉树,如下例:A(B(#,D),C(E(#,F),#))字母代表结点,字母后面的括号为该结点的子树,逗号左边为左子树,逗号右边为右子树。建立:观察括号法的表达式可知,处理完一个结点后,紧接着是继续处理其子结点,处理完其子结点,又要继续处理其子结点的子结点,即后来的先处理,是一种后进先出的思想,所以这里将借助栈结构完成此算法。观察括号法的表达式易知,输入的字符有以下几类:①正常字符(如字母):用户要保存到结点中的值;②左括号:其右边跟着的是其左边那个结点的子树;③逗号:其右边是其左边那个

2020-12-17 22:31:30 3519 1

原创 二叉树的层序遍历与建立(C/C++)

层序遍历:除根结点外,每一层结点的父结点正好是上一层的所有结点。所以需要借助队列结构,存放结点地址。先将根结点入队,然后进入循环:获取队头结点,判断其是否有子结点,有则入队,然后输出当前结点的值(或其他遍历方式),最后将该结点出队,重复这个过程,直到队列为空。void LevelOrderTravers(BiTree *&T){ queue<BiTree*>q; if(T!=nullptr) q.push(T); BiTree *p=nullptr;//用

2020-12-17 22:19:08 3023

原创 KMP算法的实现(C/C++)

这里就不对KMP算法进行介绍了,直接给上我的理解、思路和代码。设定:i:主串的伪指针j:模式串的伪指针前缀:模式串中靠前的某一段后缀:伪指针 i 左侧部分靠后的某一段同步测试:主串第 i 位字符与模式串第 j 位字符进行比较并判断是否相等同步成功:同步测试结果相等失去同步:同步测试结果不相等两个串进行同步测试,当失去同步时,让 j 前移直至模式串前缀与主串后缀重合,而 i 不动。移动位数 = 模式串已匹配的字符数 - 失去同步前最长前缀匹配字符数如此移动可以保证忽略尽量

2020-12-16 22:36:21 129

原创 尽量使用递增递减运算符的前置版本(C++)

为什么推荐使用前置?在复合表达式中,如果使用递增(递减)运算符的前置版本,会先将运算对象加1(减1),然后再对改变后的对象进行操作;而后置版本会先对该对象进行其他操作,再将其增1(减1)。由此可见,相比后置版本,前置版本避免了不必要的工作,它把值加1(减1)后直接返回改变后的运算对象,而后置版本需要先将对象的原始值存储下来以便于返回这个未修改的内容,如果我们不需要用到修改前的值,那么后置版本的操作就是一种浪费。对于整数和指针类型来说,编译器可能会对这种额外的工作进行一定的优化;但是对于相对复杂的迭代器

2020-11-08 22:33:03 181

原创 auto和decltype的基本用法与用途(C++)

autoC++11标准引入的auto类型说明符,能让编译器通过初始值来推算变量的类型,显然,auto定义的变量必须有初始值。如:auto i=47,*p=&i;//i是int变量,p是指向int的指针需要注意的是,auto一般会忽略掉顶层const,而底层const则会保留。(顶层const表示指针本身是个常量,底层const表示指针所指对象是个常量)const int ci=47,&cr=ci;//ci是const限定的int变量,cr是ci的引用,ci与cr都是顶层const

2020-11-01 22:33:40 472

原创 不要以多态方式处理数组(C++)

继承的重要性质之一就是:可以使用指向基类对象的指针或引用来操作派生类对象,对于这样的指针或引用我们称其行为是多态的,就好像它们有多重类型似的。所以C++允许我们通过基类指针或引用来操作派生类的数组,这是合法的,但建议不要这么做。我们知道,数组通过下标访问元素的行为,比如array[i],本质是一个“指针算术表达式”的简写,它的原型是:*(array+i)。数组名array本质是一个指针,当其访问连续的两个元素时,是通过位移一个元素内存大小的距离来实现的,如array和array+i之间的距离为:i×si

2020-10-31 22:56:52 218

原创 C++转型操作符

在C语言中旧式转型的语法结构是由一对小括号加上对象名称(标识符)组成,而小括号和对象名称在C++的任何地方都有可能被使用,使得转型操作不那么明显。为了解决C旧式转型的缺点,C++增加了4个新型转型操作符:static_cast,const_cast,dynam_cast,reinterpret_cast,提高了转型操作的严谨意义和易辨识度。static_cast这一操作符的功能几乎和C旧式转型等价,不同的地方主要在于书写方式:在C旧式转型中会这样写:(type)expression现在应该这样写:

2020-10-31 22:32:55 157

原创 常量引用或指向常量的指针,其所指对象可以是非const对象(C++)

常量引用:常量引用即为对const的引用,其仅对引用可参与的操作做出了限定,但对于引用的对象是否为const常量并未做限定。若引用对象是const常量,那么无论通过何种方式都不允许改变对象的值;若引用对象是非常量,则允许通过其他途径改变其值。如:int i=47;int &r1=i;const int &r2=i;此时不允许对r2进行改变其值的操作,因为r2位常量引用,但可以通过改变r1或直接改变i来达到改变值的目的。i=0;//行!r1=0;//行!r2=0;//不行!

2020-10-31 21:59:18 1026

原创 仿函数及其应用案例(C++)

概念:仿函数即是对 “()” 的重载,因为其调用方式很像函数,因此被称为仿函数。举例:class myAdd{ public: //定义仿函数,表示要对两个数进行相加 int operator()(int v1,int v2) { return v1+v2; }}int main(){ myAdd add; int result=add(10,10);//调用仿函数 cout << result &lt

2020-10-11 10:23:48 307

原创 引用做函数返回值的注意事项(C++)

1、不要返回局部变量的引用#include <iostream>using namespace std;//返回值类型“ int & ”即返回的是一个int类型的变量的别名int& test(){ int a=10;//a为局部变量,存放在四区中的栈区 return a;}int main(){ //返回a的别名存放在变量ref中 int &ref=test(); //输出ref cout <&lt

2020-09-20 10:10:32 587 1

原创 虚析构与纯虚析构解决内存泄漏问题(C++)

虚析构与纯虚析构解决内存泄漏问题当我们new一个子类的对象,并用父类的指针指向它时,若父类中使用一般的析构函数,则父类指针在析构的时候,不会调用子类中的析构函数,导致子类在堆区开辟的数据则无法释放,造成内存泄漏。使用虚析构与纯虚析构函数可以解决这一问题。1、虚析构父类中使用一般的析构函数时:#include <iostream>using namespace std;class A//父类A{public: A() { cout <&lt

2020-09-12 11:19:36 290

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除