解法:递归。根据后序遍历的性质,最后一个数字是根节点。首先需要找到左右字数的分界点。遍历数组,找到第一个大于root的位置即为左右子树分界点,从分界点开始继续遍历,如果出现小于root的值,表示右子树不满足二叉搜索树定义,直接返回false。否则继续递归判断左子树和右子树是否是二叉搜索树。
边界条件:空树
解法:题目要求输出路径。因此考虑用一个vector来保存走过的节点,push_back方法用来存入值,pop_back方法用来删除值,这样就可以形成一个后进先出的结构。按照前序遍历的顺序,遍历到一个节点的时候,首先判断该节点是否是叶子结点,如果是叶子结点且sum == target,则找到一条路径,输出该路径。如果是叶子结点但sum != target,则说明该路径不是要找的目标路径。找到叶子节点,说明寻找完毕,从vector弹出叶子节点。如果不是叶子结点,则递归进行左右子树的遍历。
边界条件:空树
例:a、b、c的所有字符串排列为abc、acb、bac、bca、cab、cba
解法:首先确定第一个位置的元素,然后一次确定每一个位置,每个位置确定时,把所有情况罗列完全即可,注意有重复字符时,跳过
例:a、b、c的所有字符串组合为a、b、c、ab、ac、bc、abc
解法:可以把求n个字符组成长度为m的组合问题分解成两个子问题,分别求n-1个字符中长度m-1的组合,以及求n-1个字符中长度为m的组合
解法一:类比快排原理,当一个数字出现次数超过一半时,排序数组中的中间位置一定是该数字。利用partition函数,任取一个枢轴(如区间最后一个数),将小于枢轴的值移到左边,大于枢轴的值移到右边,每次partition都可以将一个枢轴元素置于正确的位置。循环该过程,直到partition函数返回的枢轴位置等于 length/2 。时间复杂度O(n)。注意:这种方法只有在原数组可以修改的情况下使用
解法二:你们全上都打不过我。利用数组保存两个值,数字本身和数字出现的次数n。遍历数组,遇到相同的数字时,n++;遇到不同的数字时,n--,直到n ==0 时,更新数字。循环以上过程,最后留下的数字就是出现次数超过一半的数字。
边界条件:数组中只有一个数字
解法一:类比39题,利用partition函数,任取一个枢轴(如区间最后一个数),将小于枢轴的值移到左边,大于枢轴的值移到右边,每次partition都可以将一个枢轴元素置于正确的位置。循环该过程,直到枢轴位于第k个数所在的位置(k-1)。时间复杂度O(n)。注意:这种方法只有在原数组可以修改的情况下使用
解法二:最大堆。维护一个大小为k的最大堆。通过删除堆顶元素来保持堆的大小为k,当所有数据遍历一边之后,堆中的最大值即为第k大的元素。时间复杂度:插入删除元素O(logk),共有n个元素,因此总的时间复杂度为O(nlogk)。该方法适合处理海量数据
边界条件:k = 1,k等于数组的长度
解法:将数据分成两部分,左边部分的所有数字都小于右边部分。用最大堆处理左边数据,最小堆处理右边数据,注意保证两个原则:
1、左右数据数目之差不超过1
2、最大堆的所有数据都要小于最小堆的数据
当新数据插入且以上两个原则不能满足时,删除最小堆的堆顶数据,并将其插入最大堆中,同时将新数据插入最小堆
解法:考虑动态规划,设dp[i]为以i结尾的连续子数组的最大和,则dp[i] = max { dp[i-1] + nums[i] , nums[i] },含义:在前面已经得到的子数组后加上nums[i]或者自成一派,自己最大。
(注意:题目中出现“连续子数组”,dp[i]都要考虑以第i为结尾,这样才能保证连续)
边界条件:数组为空
例:数组{3,32,321},能排成的最小数字时321323
解法:利用sort函数,自定义compare方法。为了解决大数问题,将数字转为字符串。将两个字符串连接,由于两种连接方式生成的字符串位数相同,可以直接使用字符串比较函数strcmp,返回较小的那个组合。
(0翻译成a,...,11翻译成l,...,25翻译成z),求出一串数字有几种翻译方法
例:12259,一共存在5种翻译方法,分别是1|2|2|5|8,12|2|5|8,12|25|8,1|22|5|8,1|2|25|8
解法:考虑倒序。用一个数组dp[i]保存i到末位的翻译数。如果i与i+1无法组合,则dp[i] = dp[i+1];如果i与i+1能够组合,则dp[i] = dp[i+1] + dp[i+2]
边界条件:取用dp[i+2]时,注意判断是否越界,如果越界,则dp[i] = dp[i+1] + 1
(棋盘格中有数字,从左上到右下,每次向左或向下移动,求路径和)
解法一:暴力。一个长度为n的字符串包含O(n^2)个子串,同时每个子串需要O(n)的时间判断是否包含重复字符,因此总的时间复杂度为O(n^3)
(返回最长子串长度)
解法一:暴力。一个长度为n的字符串包含O(n^2)个子串,同时每个子串需要O(n)的时间判断是否包含重复字符,因此总的时间复杂度为O(n^3)
解法二:函数f(i)表示【以第i个字符结尾】的不包含重复字符的子字符串的最长长度。分为3种情况:
1、str[i]没有出现过时,f(i) = f(i - 1) + 1
2、str[i]出现过且两次出现的间隔d大于f(i-1),表示str[i]上次出现的位置在当前不重复子串之外,没有影响,f(i) = f(i - 1) + 1
3、str[i]出现过且两次出现的间隔d小于或等于f(i-1),表示str[i]上次出现的位置在当前不重复子串之内,需要更新当前不重复子串,f(i) = d
边界条件:字符串为空,
(能被1,2,3,5联合整除的数,求第1500个丑数)
解法一:暴力。3个循环,把2除尽,把3除尽,把5除尽,如果为1,则为丑数。缺点:每个数字都要除尽之后才能判断是不是丑数,因此在非丑数上会消耗大量时间
解法二:由于丑数肯定是由其他丑数乘以2,3,5构成,因此对于按顺序递增的丑数而言,新的丑数可以通过对前面已经求得的丑数进行乘法操作得到,3种乘数得到的新丑数中的最小值,就是顺序递增的丑数序列的最新值。还可以继续优化,用T2, T3, T5来表示丑数的【下界】,所谓下界,就是乘以2,3,5后,得出的值不会是之前记录过的(是新值)。T2, T3, T5需要在每轮计算后进行更新,可以避免多余的运算
边界条件:index = 1, index < 1的无效输入
解法一:暴力。时间复杂度O(n^2)
解法二:利用哈希表的思想。构建定长数组,将字符作为下标,存储该字符出现的次数。两次扫描,第一次扫描用于统计每个字符出现的次数,第二次扫描用于找到第一个出现次数为1的字符。时间复杂度O(n)+O(n) = O(n)
边界条件:字符串为空
例:在数组{7,5,6,4}中,一共存在5个逆序对,分别是{7,6},{7,5},{7,4},{6,4},{5,4}
解法一:暴力。时间复杂度O(n^2)
解法二:归并思想。将数组按两个一组切分,记录包含两个元素数组的逆序对数,同时进行排序,避免后期重复计算。类比归并操作,进行4个一组的逆序对数量统计。两个指针分别指向两个数组的高位,判断指针所指元素的大小,如果是逆序(左边元素大于右边元素),累加逆序数,注意此时的逆序数等于右边起始位置到指针位置所有的元素个数(因为上一步已经在子数组中按递增排好序)。注意如果某个数组已经排完,剩下的元素可以直接放入(类比归并操作)。
边界条件:数组为空,数组只包含一个数字,数组只包含两个数字
解法一:暴力,从一个链表出发,每到一个节点,就遍历另一个链表,判断是否是公共节点。时间复杂度O(mn),m、n为链表长度
解法二:两条链表同时出发。当其中一个链表到终点时,记录两个链表的步数差n。让长链表先走n步,再同时移动两条链表,相遇的点即为公共节点。时间复杂度O(m + n)
边界条件:二叉树只有左/右节点、输入的二叉树根节点为nulptr(空二叉树)
解法一:排序数组,首先想到二分。通过二分找到其中一个k的位置,从该位置向左右扫描,直到扫描到边界。时间复杂度O(n)
解法二(优):解法一的时间复杂度可以继续优化。考虑通过二分查找,直接找到左右边界。以左边界为例,二分查找,直到满足(值==k 且 为左边界) 时,跳出循环,右边界同理。时间复杂度O(logn)
边界条件:k不存在,返回-1。 index-1可能越界,所以index == 0时,需要跳出循环
(总共n个,实际只有n-1个,求缺失的那个数)
解法一:数学解法。先求出0~n-1的累加和 n * (n-1) / 2 ,再求出数组实际的累加和,相减,即为缺失的那个数。没有利用到排序的性质,时间复杂度O(n)
解法二(优):排序数组,首先想到二分。完整的数组中,数组下标对应的值,与下标是相等,以此作为判断条件,比较下标与对应值的大小关系,index < nums[index] && index-1 < nums[index],则继续找左边;index < nums[index] && index-1 == nums[index - 1], index -1即为所要找的值;index = nums[index],继续找右边。时间复杂度O(logn)
边界条件:数字合法性判断,n < 0,数组不是递增序列,输入数字不在 0~n-1范围内
解法一:暴力扫一遍。时间复杂度O(n)
解法二(优):排序数组,首先想到二分。当index < nums[index]时,找左边;index > nums[index],找右边;直到 index = nums[index]。时间复杂度O(logn)
边界条件:数组不是递增序列,数组中不存在这样的数字
解法:中序遍历,按左子树,根节点,右子树的顺序查找,每次查找 k--,直到 k == 1时,下一个要找的就是第K大节点 (此题编码不容易)
边界条件:k可能等与 0 或者 1, 二叉树只有左/右节点 ,输入的二叉树根节点为nulptr(空二叉树)
解法:递归,先从左子树开始找,再从右子树开始找,返回左右子树中的最大深度 + 1
边界条件:二叉树只有左/右节点、输入的二叉树根节点为nulptr(空二叉树)
解法一:仿照55.1,从根节点开始,递归计算每棵子树的深度,直到所有子树都满足平衡二叉树的定义
解法二(优):解法一让许多树节点的深度进行了重复计算,因此考虑后序遍历,每次都记录节点的深度,这样可以一边遍历一边判断每个节点是不是平衡点,避免重复计算
边界条件:二叉树只有左/右节点、输入的二叉树根节点为nulptr(空二叉树)