树和图总结(大二下写,初学者)

这段时间,我学到了数据结构当中的栈和队列、字符串和多维数组、树和二叉树以及图。这里重点讲述我在第五章树和二叉树、第六章图中学到的知识以及解决的问题。关于上述所学的的知识,我总结为三方面:课本上学到的知识、上机实现课本上的例子的过程所学到的知识和力扣做题学到的知识和技巧。

第五章:树和二叉树

在这一章中,我重点学习到的并且可操作的对象是二叉树,原因是二叉树的结构较为简单且统一,具有实际的操作价值。在学习二叉树之前,我必须知道关于树的相关知识,以便于解决后续的问题。我学到了树中结点的度的定义,注意树的结点度是其子树的个数,这一点要切记,不要混淆。我知道了什么是树的度、叶子节点、分支结点、孩子节点、双亲结点以及兄弟节点。其次,我还学到了什么是路径、路径长度、结点的层数、树的深度(即所有结点的最大层数)、树的宽度。最后,我学到的关于树的基本知识就是遍历操作,其分为前、中、后序遍历,顾名思义就是根据访问根结点的次序所划分的三种遍历方法,还学到了层次遍历,其现过程会在后面构造二叉链表中说明,这里就不过多赘述。

学会了上述的基本知识后,我进入到了二叉树的学习,在解决关于二叉树的相关问题前,我需要掌握一些二叉树的基本概念和性质。首先,我学会了什么是二叉树,并知道了几种特殊的二叉树(斜树、满二叉树、完全二叉树),其中完全二叉树是在满二叉树中,从最后一个结点开始,连续去点任意个结点所得到的。然后我学到了5个二叉树的基本性质以及我对这5个性质的理解:

性质一:二叉树的第i层上最多有2i-1个结点。由于是“最多”,我就以满二叉树为例来思考,发现各层的结点数满足公比为2的等比数列,则第i层结点数有2i-1。

性质二:一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。由于还是求最多,我还是以满二叉树为例,由性质一可知结点数满足公比为2的等比数列,且深度为k,根据等比数列求和公式,很容易知道树的结点总数为2k-1。

性质三:在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。首先我将结点度数为0、1、2的个数各设为n0、n1、n2,所以树的结点总数n= n0+n1+n2,其次,分支数n-1= n1+2n2(度数为2的会产生两个分支,度数为一的会产生1个分支,为0的叶子节点不产生分支。),由上述两式可得n0=n2+1。

性质四:具有n个结点的完全二叉树的深度为 [log2n]+1。由性质二,我可以列出关于结点数的不等式,2k-1 ≤ n < 2k,两边取对数得k-1≤log2nk,即log2nk≤log2n+1。由于深度为整数则最终我得到了[log2n]+1。

性质五:通过观察二叉树的层序编号我发现了结点i的双亲结点为i/2,结点i的左孩子为2i,结点i的右孩子为2i+1。该性质显而易见,就不再过多陈述。但该性质反映了结点之间的逻辑关系。

然后我学到了关于二叉树的遍历操作,给我一个二叉树的图,我可以轻而易举的写出前序遍历、中序遍历、后序遍历以及层次遍历的结果。同时我还可以已知一棵二叉树的前序序列和后序序列,唯一确定这棵二叉树。例如:已知一棵二叉树的前序遍历序列和中序遍历序列分别为ABCDEFGHI 和BCAEDGHFI。首先,我们要清楚一点,我们通过前序确认根节点,中序确认左右子树。前序第一个为A,则A为根节点,接着在中序当中找到A,则A的左边BC为左子树的点,A的右边EDGHFI为右子树的点,然后再看前序A之后为B,则B为A左子树的根节点,接着看中序BC可知,B没有左子树只有右子树C(这里有一个技巧,当前序中发现与中序相同的片段BC时,则以B为根节点的树没有左子树只有右子树C)。然后继续看前序遍历到D,则D为右子树根节点,前面得出EDGHFI,则E为左子树的点,GHFI为右子树的点,再看前序遍历到E之后为F,则F为右子树根节点,然后再分,再看前序。最终得到唯一确定的二叉树。

接下来我学到了二叉树的代码实现,要实现必须要清楚其存储结构是什么,首先学到的是简单的顺序存储,即先将二叉树按照完全二叉树编码,编码作数组下标进行存储。这种存储方式的缺点也很明显,浪费空间,只有存储完全二叉树时才能充分的利用空间。然后学到的就是链式存储结构,通过构造二叉链表来实现二叉树。定义结点结构体有三部分,即数据部分、指向左孩子的指针、指向有孩子的指针。由二叉树的结构及其定义我可以知道,实现二叉树及其遍历,最简单的操作就是使用递归。例如前序遍历,在函数中我先设置结束条件,即当前节点为空就结束,由于是先序,我输出当前的根结点,之后再分别递归的调用其左子树和右子树。若是中序或后续,调整输出顺序即可。至于层次遍历,则与前三种遍历不同,在输出第一层根节点的时候要保留第二层结点的信息,所以我用队列来存储结点,先将根节点入队,在队列非空的条件下运行while循环,循环中,出队一个元素,输出结点值,分别让其左右子树入队,循环结束即层次遍历结束。

我觉得最为闪光的便是构造函数,我在构造函数中调用树结点指针类型的函数,让私有成员中的root等于creat函数的返回值。并且规定输入#时表明是空树,输入不是#时,动态申请一个空间,给结点赋值,之后依次递归建立它的左子树和右子树。请注意!该函数不需要另设结束条件,当我们输入的树结构其叶子结点的输入值都为“#”时,则函数不会继续调用递归,回依次返回为空,最终creat函数顺序执行结束,这一点递归的调用我觉得很妙。

还有一个和线性表中一样会遇到的问题,就是找一个结点的后继(孩子结点)很容易,但找他的前驱(双亲结点)又该怎么实现呢?首先我要知道要查找的树的根节点以及要查的结点,查找时分为如下几种情况,当根节点为空,不必多说直接返回空,当根节点的左子树或右子树正好为要查结点时,返回根结点即可,当上两种情况都没找到时,就需要递归的调用函数分别找左子树、右子树,如果在左子树中找到则返回左子树递归调用的结果,否则返回右子树的结果。由于构造要找的结点比较困难且我在力扣上没有找到相关的题目,所以我在写代码的时候通过指针直接让其指向树的某一结点来当作要查的结点。

下面是我构造的二叉链表的输出情况:

学会并实现完二叉链表之后,我又学到了线索链表,顾名思义,就是要有线索,即指向前驱和后继结点的指针,既然有前驱和后继的顺序,那必然离不开二叉树的遍历顺序,根据二叉树的四种遍历方式,线索二叉树又分为四种,这里以中序线索二叉树为例来讲述。首先,关于结构体的构造,我在二叉链表的基础上加了两个标志位,分别用于判断左右子树的指针指向左孩子或右孩子结点还是指向前驱或后继。建立带标志的二叉链表与二叉链表基本一致,不再赘述,主要是创建将链表线索化的函数,这一步也不难,因为是创建中序线索链表,则通过中序遍历的方式来构造各结点前驱和后继的关系。函数有两个参数,当前结点root,刚刚访问的结点pre,知道这两个参数则在实现过程中,找结点的前驱很容易:如果root的左孩子为空则左标志位置1,并将左指针指向pre。但找其后继就比较复杂:先查看当前点的右子树是否为空,为空则右标志位置1,然后再判断pre所指的右标志位是否为1,若是则将当前结点root作为上一次遍历的结点pre的后继。找后继总结来说就是先变右标志位,再在下次遍历中设置后继线索,非常的巧妙!

还记得我构造线索链表的初衷吗,即保存二叉树的某种遍历序列,所以线索不是白白构造出来就行的,还要实现根据线索的遍历操作才行。在遍历函数之前,我先定义了一个next函数,用于找某一结点的后继结点,查找时会出现两种结果,第一:结点的右标志为1,说明右指针指向结点后继,此时直接得到了后继结点。第二:右标志为0时,说明结点有右孩子,此时根据中序遍历的特点,右子树中最左边的结点就是当前结点的下一个遍历的结点,则我找到这个最左结点就找到了后继。清楚以上两点很容易就能实现next函数,Next函数实现之后就是遍历的函数,在找到第一个遍历的结点后,循环调用next函数,知道结点没有后继为止。

接着我学到了树转二叉树,这个知识在离散数学就有接触,这里简单说一下,方法步骤是加线、去线、层次调整。记住一句口诀:兄弟结点变右儿子。还有一个结论就是树的后序遍历等价于二叉树的中序遍历,两者前序遍历相同。还学到了森林转二叉树,方法和树转二叉树类似,把森林中的每棵树都转为二叉树,然后把每棵树的根节点看作互相的兄弟结点,还是那句话,兄弟结点变右儿子,最终就可以转化完成。上述过程是可逆的,即可以把二叉树转为森林或树,只要把口诀变为右儿子变兄弟结点就可实现。

最后学到的课本上的知识就是哈夫曼树,即带权路径最小的二叉树。该树的实现构成我在离散数学当中也学到过,简单来说就是,讲初始结点各自看成一棵二叉树,每次选出根结点最小的两棵树形成一颗新的二叉树,新二叉树的根节点权值接着参与比较,直到所有二叉树合成一个二叉树。在知道原理后,关键就是代码实现。我通过数组来保存哈夫曼树的结点信息,数组结构有四个元素:权值、左孩子下标、右孩子下标以及双亲结点下标。每次进行合并操作后,就把新子树的根节点顺序存放到数组的后面,经过不断的更新数组,最终的得到的数组就是哈夫曼树各结点的信息。在实现代码之前,首先要明白几个细节,第一:由于是n个结点,则每次两两合一,最终要进行n-1次合并。第二:由于n个结点,则数组初始就有n个(下标依次为0~n-1),则合并操作直接从n开始到2n-1进行储存,为了方便,于是循环也就从n开始到2n-1。第三:在选最小的两个根节点时,一定是从双亲结点都为初始值-1当中选的。为了方便观看,我写了一个输出函数,输出数组的所有信息和输出每个叶子到根结点的路径。

关键代码及运行结果如下:

学完上述二叉树课本上的知识后,我就要开始解决实际的问题,解答二叉树算法方面的问题,由于这些算法题都需要二叉树根节点指针作为参数,于是我为了方便实现及运行出结果,我再上面写的二叉链表的基础上进行函数的编写。每道题我都会给出关键代码及运行结果。

设计算法求二叉树的结点个数:该题只需通过遍历操作,在遍历的过程中进行计数即可。

统计叶子节点的数目:当当前结点的左孩子为空且右孩子也为空时,叶子节点的数目加一,否则的话递归的调用当前点的左子树、右子树。

104. 二叉树的最大深度 - 力扣(LeetCode)这道题求二叉树的最大深度,即计算树的高度,该题可以用深度优先搜索的方法,通过递归的调用,分别求出左子树和右子树的最大深度,然后选出两者最大的然后+1,就得到了二叉树的最大深度。

计算二叉树的宽度:求二叉树的宽度我首先要知道每一层的结点数,返回最大的就是宽度,我用的是广度优先搜索的方法来实现的,函数中要记录层数、每层的结点数、宽度。同时还要记录每一层的最右边结点在队列中的位置,用这个来判别某一层是否遍历完,首先我们知道我们在遍历某个结点的时候,就将该结点的左右孩子入队到队尾,当我们遍历到该层的最后一点个结点时,下一层的所有结点正好全部入队,此时队尾指针所指的就是下一层的最右边结点,更新宽度和层数,并将新层数的结点数赋值为0。知道遍历完所有节点,就能比较出最大宽度。

左右子树的交换:首先从根节点出发,如果为空则返回,否则的话定义一个二叉树类型的指针temp,让指针指向根节点的右子树,再让根节点的右指针指向根节点的左子树,最后再让根节点左指针指向temp所指的内容,递归的调用根节点的左右子树即可。

100. 相同的树 - 力扣(LeetCode)判断两颗二叉树树是否相同,两者必须要结构相同且对应结点值也相同,首先,两者都为空则肯定相同,只要有一个为空另一个不为空肯定不同,接着判断两个二叉树对应节点值是否不相同,最后上述都不满足,则递归的调用各自的左孩子以及右孩子。

101. 对称二叉树 - 力扣(LeetCode)该题是判断二叉树是否轴对称,写这道题的时候我马上就想到了上一题的判断两颗二叉树是否相同,我把根节点左右子树作为参数传入上一题的函数中,不过上一题的函数还需小小的修改,递归调用时把同为左指针、右指针改为一左一右。

判断一棵树是否是完全二叉树:首先根据完全二叉树的特点我知道,我可以通过层次遍历来判断,当某结点只有右子树没有左子树时必定不是完全二叉树,当只有左子树没有右,子树时,这种情况下如果之前没有访问到叶子节点则将叶子结点标志符设为true(当第一次访问到叶子结点后,其后面的所有结点都必须为叶子结点),反之则返回false。第一次访问到叶子节点还有一种情况即直接访问到叶子结点,当左右子树都为空时将标志符设为true。如果以上情况都不满足且标志符为true则不是完全二叉树。经过以上判断,如果在层次遍历结束时仍没有返回false则是完全二叉树。

102. 二叉树的层序遍历 - 力扣(LeetCode)这个题实现二叉树的层序遍历,通过队列很容易就可以实现,写着个题的目的是,我在评论中看到了有用递归解决这个问题的,首先定义了vector<vector<int>>类型的对象ansans[i]为第i层的数据,类型为vector,记录每一层的元素数据,通过前序遍历的思想递归的调用,每递归一次层数加1

  

107. 二叉树的层序遍历 II - 力扣(LeetCode)该题的层次遍历是从最底层到最上层的从下到上的层次遍历,实现起来看着很难,但其实非常简单,只需将上一题层次遍历得到的结果,结果为vector类型, 使用reverse(ans.begin(),ans.end())将其翻转即可。

96. 不同的二叉搜索树 - 力扣(LeetCode)该题是给一个结点数n,求结点值从1到n互不相同的二叉搜索树的个数。我定义了两个数组,G(n)表示长度为 n 的序列能构成的不同二叉搜索树的个数,F(i,n)表示以 i 为根、序列长度为 n 的不同二叉搜索树个数。所求的结果就是,依次让所有结点作根的情况下F(i,n)的总和。考虑到n01的情况下,树的个数为1,又由F(i,n)为左子树构成的个数与右子树能构成个数的乘积。

第六章:图

在这一章中,我学到了关于图的基本知识,用邻接矩阵来实现图的存储,用邻接表来实现图的存储,在实现图的结构的基础上是实现了深度优先遍历以及广度优先遍历两种遍历算法。还学到了最小生成树的两种算法:Prim,Kruskal,以及最短路径的两种算法:Dijkstra,Floyd。最后就是AOV网和拓扑排序。

首先我学到了图的基本术语,这些我在离散数学中已经学过,我主要想说一下图中顶点的度,就以无向图为例顶点v的度就是指依附于该顶点的边数,这一点要和第五章树中结点的度区分开来,不要混淆。其次,与二叉树一样,我们要考虑图的遍历操作,我学到了深度优先遍历和广度优先遍历两种遍历方法。深度优先遍历基本思想:深度优先,顾名思义就是从一个顶点开始,一心寻找下一个点力求深度。具体过程为,先访问顶点,然后从顶点中未被访问过的邻接点中选出一个顶点,继续进行上述操作,知道图中所有结点都被访问过时结束。广度优先搜索的基本思想:首先访问顶点,然后依次访问顶点的未被访问过的邻接点,接着依次访问邻接点的未被访问过的邻接点,注意访问是有顺序的,先被访问顶点的邻接点要先于后被访问顶点的邻接点被访问,直到所有结点都被访问。

在明确了遍历方法后,我就要开始实现它,在实现之前,我需要先将图的结构存储起来,第一种方式使用数组来实现,由于邻接矩阵可以反映各顶点的关系,所以我用数组存储邻接矩阵的信息。首先要有存放图中顶点的数组,还要有存放边的数组即邻接矩阵,还要知道顶点数和边数。在构造函数中,给顶点的数组存入各点的信息,并初始化邻接矩阵让其开始都为0,依次输入每条边的两个顶点的编号,给邻接矩阵对应点赋值为1。为了判断某个顶点是否被访问过,我设置了一个visited数组,初始值为0,访问过就将数组对应点赋值为1表示该点已被访问。在调用两种遍历方法之前,都要将visited数组初始化为0,由于深度优先搜索我用的是递归的方法,则visited数组赋初值时需要在函数外,而广度优先搜索则是通过队列的方法,与树的层次遍历思想一致,所以visited数组赋初值时在函数里即可。邻接矩阵来表示图的方法我已经实现,代码运行结果如下。

在学完邻接矩阵的方法后,紧接着我学会了邻接表的使用,这是一种数组和链表相结合的方法,链表的各结点由两部分构成,存储了结点在数组中的下标以及指向下一个点的指针,而数组存储的是顶点的信息,每个顶点包含两部分,顶点数据以及指向链表的指针firstedge,数组中各顶点指向的链表都是该顶点邻接的点,该结构的设计可谓非常的巧妙。我觉得定义顶点表数组还充当了头节点的作用,可以迅速找到各顶点对应的链表并方便定义新的指针指向该顶点链表的第一个结点。在构造函数中,和邻接矩阵的方法类似,都是先初始化顶点数组,不过在这里初始化时要让数组指向链表的指针firstedge初始都为空。依次输入边的两顶点下标,动态申请内存空间,构造一个链表的结点,赋值(赋的是第二个下标),使用头插法,插入到第一个下标所在的数组中firstedge所指的链表中。深度优先遍历:同样是采用递归的方法,给定一个初始点的下标,输出该点的值,在这个点的链表中,找到未被访问过的点,得到下标,以这个下表为参数递归的调用该函数。广度优先遍历:得到初始点下标,先输出,然后将下标入队,进入循环,出队,让空指针指向下标所在点的边链表,然后在该边链表中,依次遍历,遍历时根据得到的下标输出,并将相应的visited(visited用法与邻接矩阵的一致)置1,同时将下标入队,直到该链表遍历完成。等到循环结束则遍历完成。邻接标还需要考虑图的销毁操作,根据顶点的个数,依次在数组中找到各自边链表并delete链表各点。邻接表来表示图的方法我也已经实现,代码运行结果如下。

最小生成树:Prim算法

基本思想:首先划分两个集合,一个是最小生成树的点集T,另一个是图中非最小生成树中的点的集合G,初始T中只有一个点(由于最小生成树必定包含图中所有的点,则初始点选谁都可),选出一条从T中所有的点到G中所有点包含的边中选出一条权值最小的边,将边的信息保存到一个集合TE当中,然后将该边上对应的点加入到T中,重复上述操作,直到所有的点都加入到集合T中。且该按算法完成之后就是一个树,不需要考虑有没有回路。

实现细节:算法的基本思想比较简单,但关键是要实现出来,这一步较为困难。由于算法需要知道任意两个顶点边的权值,所以我在构造的邻接矩阵的基础上进行算法的实现,且将原本的邻接矩阵改为带权的邻接矩阵且没有邻接边时,权值设为100。首先要定义两个数组,adjvex表示候选最短边的邻接点,lowcost表示候选最短边的权值,两个数组是相互对应的,adjvex下标i和值j组成的边的权值就是lowcost在下标i时的值。在函数中,我将lowcost对应下标的值置为0时看作将该下标对应的点加入到最小生成树的点集当中去。同时,由于初始就有一个点,还剩n-1个点,每操作一次就加入一个点,则最终只需n-1此操作即可完成。函数的开始,先初始化,然后循环n-1次,在每次循环中,找到lowcost中满足不为0且小于上面设定的权值100的条件下最小的权值对应的下标。输出对应的边和权值,然后将lowcost对应下标的值置为0,最后调整两个数组,让新加的那个点的邻接点权值与原有的比较,小则更新两数组。

关键代码及运行结果如下,我是以课本上的那个图为例来构造的,输出的是得到的最小生成树边的信息:

最小生成树:Kruskal算法

基本思想:Prim算法是逐个加点,而Kruskal算法则是逐个加边。按照边的权值从小到大进行考察,若被考察边的两点属于两个连通分量则连在一起,否则舍去该边。即按边的权值从小到大依次添加,当添加的边会产生回路时则舍去。

实现细节:显而易见,实现的关键在于判断是否会产生回路,为解决此问题,我们引入了并查集来判断两个点是否在一个联通分量中。思想是,每合并两个点所在的连通分量,判断其各自连通分量的根节点是否相同,若相同则说明两点属于一个连通分量,会产生回路,反之则不会。合并时约定俗成以第一个下标小的根作为合并后的根,为找根我加入了一个数组parent用来表示点的双亲。在解决回路问题后,该算法就比较好实现了,首先要建立边的数据结构,要有两点的下标以及边的权重,然后将边的数组按权重从小到大排序,接着定义找根的函数,然后按下标从小到大依次遍历每条边,并判断边的两点的根是否相同,不同时才会合并输出。

最短路径 :Dijkstra算法

基本思想:该算法求的是一个初始点到其余各点的最短路径,初始时最短路径顶点的集合只有一个点,求出初始点到其余各点的路径长,选出最小的,并将路径上的一个新顶点加入集合当中,计算初始点最后经过这个新顶点到达各顶点的路径长,若得到的路径长比原来的短,则更新。重复上述操作,知道所有顶点都加入到集合当中。

实现细节:该算法的实现过程与Prim算法的实现过程可谓非常的相似,但我在该算法的邻接矩阵中不可达的两点权值设为1000。还有我在该算法中设置了两个数组,一个(dist)为初始点到各点的权值,不可达则为1000,另一个是字符串类型的用于存放最短路径经过的顶点名称,还有就是dist=0意味着将点入集合相当于固定了,找到了初始点到该点的最短路径,以后就只考虑别的点。函数中,初始化,找最短,修改等操作都与Prim算法相似不再赘述。以课本为例,运行结果如下:

最短路径 :Floyd算法

Dijkstra算法一次只能算一个点到各点的最短路径,而要算任意两个点的最短路径则n个顶点要执行n次。而Floyd算法虽然时间复杂度和Dijkstra算法一样,但他可以直接得出任意两个点的最短路径,较为简单。思想是通过操作邻接矩阵并同时修改字符串类型的用于存放最短路径经过的顶点名称的数组,操作过程是,依次对各个点进行试探,分别找出到其余各点的路径,取最短的更新即可。

AOV网和拓扑排序

最后我学到了什么是AOV网,只要想到我们课程的前驱和后继的关系以及各课程的优先级就很容易理解AOV网了。我还学到了什么是拓扑排序,且AOV网一定不能存在回路,否则就没有其拓扑排序。还有一个AOV网的拓扑排序可能不是唯一的。

接下来就要考虑拓扑排序的数据结构了,其采用的是邻接表来储存,所以在实现时我在以前写的邻接表的基础上才操作,首先邻接表的数组结构要增加一个入度域表示当前点的入度,用于判断是否有前驱,为0时说明没有前驱可以输出。其次还要用一个栈来入度为0的点在数组的下标。在函数中,遍历顶点数组,将入度为0都压栈,然后当栈不为空时,进入循环:出栈、输出并记录次数,在出栈元素的单链表中遍历,遍历到的每个点入度减一,同时判断入读为0的点,满足则入栈。最终循环结束后,若记录的次数少于顶点数,则说明没有输出完,AOV网中存在回路。

表达式求值是一道非常有趣的题目,他很好的演示了栈的应用,后缀表达式求值最为简单,当读入操作数时压栈,当读入运算符时,从栈中弹出两个数,运算完之后将结果再压栈。知道输入完之后,栈顶的值就是表达式的值。中缀表达式求值,用到了两个栈,一个存放数一个存操作数,当输入操作数时,直接进栈,当是运算符时,若优先级高于栈顶,则出栈运算再入栈,直到运算符优先级低于输入的那个。若输入的优先级低于栈顶,则直接入栈。若为括号,则出栈运算直到消掉括号。我将中缀表达式求值,中缀转后缀,后缀表达式写在一起,一次输入,三个结果,相互验证。

3. 无重复字符的最长子串 - 力扣(LeetCode)该题寻找最长字串的长度,运用的是滑动窗口的思想,需要左右两个指针,枚举左指针的位置,在每一步的操作中,我们会将左指针向右移动一格,表示 我们开始枚举下一个字符作为起始位置,找右指针所能移动的最大值,要判断是否有重复的字符我用哈希集合就可以轻松的解决此问题。

32. 最长有效括号 - 力扣(LeetCode)该题要找出最长有效(格式正确且连续)括号子串的长度,我写这题的思想是,把所有括号不匹配的地方的下标记录下来,最左边为0最右为字符串的长度,中间为不匹配的下标,计算每段的差值,找出最大的就是最长有效括号的长度。

402. 移掉 K 位数字 - 力扣(LeetCode)该题要求移除这个数中的 k 位数字,使得剩下的数字最小。该数字,从左到右每位数字,当左边的大于右边的,就删除左边的,让该数的每位数处于从左到右的递增序列。满足上述要求时,但此时k0且剩下的数字还有位数时,每次删除最右边的位数。最终得到的数到检测开头是否为0由于要检测是否为0,则使用string模拟栈更简单方便,不用将战中的元素依次取出转为字符串,避免造成超时。

  • 56
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值