给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
厦大计算机系机试第一题(只拿了一半好像,回过头来想到是ascii-‘0’)出现的负数造成的 ,哎菜的难受(如果ascii的值比’0’小就异常了)。
本题可采用滑动窗口的方法,重点是2个指针,i和j,分别表示最长子串的首端和末端。
9.9更新: 第一种方法效率更多,可以不适用map,也较好理解,其实双指针对于此题,我认为重点就是如何理解子串开头的慢指针,我的逻辑思维不好,因此举了一个稍微特殊的例子理解了慢指针的妙用。
(结合代码)双指针演示:
i(慢指针)
j(快指针)
a b c d b e (uniquestr = a)
i j
a b c d b e(uniquestr = ab)
i j
a b c d b e(uniquestr = abc)
i j
a b c d b e(uniquestr = abcd)
i j
a b c d b e(此时不执行if的第一个分支,i移动)
i j
a b c d b e(此时不执行if的第一个分支,i移动)
i j(此时,j才继续移动,所以i是会移动到不重复的位置上,(uniquestr = cdb))
a b c d b e
// 无论i怎么移动,判断子串是否变大的依据是看j,而不是看num[s[i]]
// 如果s[j] 出现了重复,其实并不需要找到s[j]相同的字符,只需要将i依次
//向前移动,并还原num[s[i]]的值,切忌不要直接去想怎么马上移动到第一个重复
//的字符,因为只要这样子移动,而判断ans的变化与否还是看的是j,只要没移动
//到重复的字符,那么j的位置就不变改变,就不用担心出现错误。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// i j 双指针
int len = s.size();
int num[256] = {0};
int i = 0, j = 0;
int ans = 0;
while(j < len){
if(num[s[j]] == 0){
ans = max(ans, j - i + 1);
num[s[j]]++;
j++;
}else{
num[s[i]] = 0;
i++;
}
}
return ans;
}
};
给定一个字符 串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
(1)中心扩散法
需要两次判断,用来检测以x为中心的回文是奇数还是偶数的情况。巩固了C语言欠缺的知识(太差了,一脸泪,字符指针的返回值无法使用局部变量 ,必须动态分配内存给字符指针malloc函数,或者使用static作为全局变量,并且在使用malloc的时候,先必须申请的内存空间为返回的字符串的长度len+1,切一定要加上 ans[len] = '\0’结束符,否则无法编译通过。)
char * longestPalindrome(char * s){
int maxlen = 0;
int start = 0;
int len = strlen(s);
if(len < 2){
return s;
}
for(int h = 0; h < len; h++){
int i = h;
int j = i;
while(i >= 0 && j <= len && s[i] == s[j] ){
i--;
j++;
}
if(maxlen < j - i -1){
maxlen = j - i - 1;
start = i + 1;
}
i = h;
j = h + 1;
while(i >= 0 && j <= len && s[i] == s[j] ){
i--;
j++;
}
if(maxlen < j - i -1){
maxlen = j - i - 1;
start = i + 1;
}
}
char * ans =(char *) malloc(sizeof(char) * (maxlen + 1 ));
strncpy(ans, s + start, maxlen);
ans[maxlen] = '\0';
// strcpy(s,ans);
return ans;
}```
//动态规划
//C语言版本,不过这个leetcode通过不了,“cbbd” 本地这个这个回文串是“bb”,在服务器上却是“cbb”,
//无语了,不过对比了底下的C++版本可以通过,我觉得思路应该是没问题的,恳请批评指正
//dp[i][j] 表示 从尾i 到 头j组成的字符串是否为回文字符串,分别用1与0表示。
char * longestPalindrome(char * s){
int dp[1001][1001];
int len = strlen(s);
int maxlen = 1;
int start = 0;
int end = 0;
//首先,初始化,自己本身就是个长度为1的回文串。
for(int i = 0; i < len; i++){
dp[i][i] = 1;
}
//两层for循环,首先i从头遍历,j是一个重点,她是从i的位置向前遍历,
//重点判断dp[i - 1][j + 1],而不是dp[i + 1][j - 1]]。
for(int i = 0; i < len; i++){
for(int j = i; j >= 0; j--){
if((s[i] == s[j]) && (i - j < 2)){//这种是特殊情况,表示i与j相邻的情况。
dp[i][j] = 1;
}
else if((s[i] == s[j]) && (dp[i - 1][j + 1] == 1)){//核心
dp[i][j] = 1;
}
if((dp[i][j] == 1) && (i - j + 1 > maxlen)){//判断长度是否超过最大
maxlen = i - j + 1;
start = j ;
end = i;
}
}
}
char * ans =(char *) malloc(sizeof(char) * (maxlen + 1 ));
strncpy(ans, s + start, maxlen);
ans[maxlen] = '\0';
return ans;
}
//C++ 版本
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if (len == 0)
return s;
bool dp[len][len];
int start = 0, end = 0;
for (int i = 0; i <len; i++)
dp[i][i] = true;
for (int r = 0; r < len; r++)
for (int l = 0; l < r; l++)
if (s[r]==s[l] && (r-l==1 || dp[l+1][r-1])) {
dp[l][r] = true;
if (r-l > end-start) {
start = l;
end = r;
}
continue;
}else{
dp[l][r] = false;
}
string ans = s.substr(start, end-start+1);
return ans;
}
};
思路:借鉴leetcode大佬的评论:@袁雷洋 今天提交完答案之后,又在网上看了几个解答的方法,还是感觉这个递归的方法逻辑最清晰,但是思路很容易就迷糊了.在这里稍微梳理一下。
首先建立了一个全部为False的二维矩阵保存递归的结果,行数是(字符串长度+1),列数是(p的长度+1).这里为什么要多建一行一列呢?因为需要考虑到p和s为空的情况.
其中dp[i][j]表明字符串s[:i]与p[:j]的匹配结果.(其中s[:i]最后一个元素为s[i-1],p[:j]最后一个元素为p[j-1],搞懂这一点才看得懂代码的)
首先处理i为0(s==’’)或者j为0(p==’’)的情况.当p==’‘时,只有s==’‘时二者才能匹配,所以首列中,只有首个元素为0,即dp[0][0]=0. 当s==’‘时,只有p==’‘或者p==‘x*y*’(或类似形式)时,二者才能匹配. 所以有
dp[0][j]= =( j >= 2) and (p[j-1] = =’*’) and (dp[0][j-2]).
有必要对上面这个式子解释: 首先j>=2,因为j=1时,p不为空,也不存在首个元素就为*的情况,所以此时p与s肯定无法匹配.dp[0][j]对应p的子串,最后一个元素为p[j-1],只有p[j-1]= =’*'时才有可能匹配成功.同时需要dp[0][j-2]同样为True时,dp[0][j]才能为True.(这个地方好好理解一下,对下面的总代码理解很重要)
下面就使用双重循环开始递归了.dp[i][j]对应s的子串,最后一个元素为s[i-1],对应p的子串,最后一个元素为p[j-1].
(1)首先判断p[j-1]是否为’*’.
如果p[j-1] == '’,那么’‘前面肯定是有一个字母的(比如说b吧),那么需要同时考虑’b*’,此时分两种情况:
①’b*'是无用的:
比如s=‘aaaa’,p=‘a*b*’.此时s与p的配对结果与s与’a*'配对的结果是一样的.所以有 dp[i][j] = dp[i][j-2].
②’b*'是有用的:
比如s=‘aabb’,p=‘a*b*’.之前s=‘aab’,p='a*b’时二者已经配对过了,所以s=‘aab’,p=‘a*b*‘时,二者同样能配对,同样需要判断s[i-1]是否与p[j-2]相同,或者p[j-2]==’.’(即可以代替任何字符)。
所以有 dp[i][j] = dp[i-1][j] and (p[j-2] = = s[i-1] or p[j-2] = = ‘.’)
1与2两种情况有一种成立就行,即取或.
if p[j-1] == ‘*’: dp[i][j] = dp[i][j-2] or (dp[i-1][j] and (p[j-2] = = s[i-1] or p[j-2] == ‘.’))
(2)如果p[j-1]不为’*’.
判断起来就相对简单了.首先要考虑之前的子串是否匹配成功了,即dp[i-1][j-1]的值,同时要考虑dp[i][j]对应的s的子串最后一位s[i-1],p的子串p[j-1]是否相等, p[j-1] == '.‘时同样满足情况,毕竟’.'是万能匹配符. 所以就有 else: dp[i][j] = dp[i-1][j-1] and(p[j-1] == s[i-1] or p[j-1] == ‘.’)
希望能对大家有所帮助。
#include
bool isMatch(char * s, char * p) {
int sl = strlen(s);
int pl = strlen(p);
//首先建立了一个全部为False的二维矩阵保存递归的结果,行数是(字符串长度+1),列数是(p的长度+1).这里为什么要多建一行一列呢?因为需要考虑到p和s为空的情况. 其中dp[i][j]表明字符串s[:i]与p[:j]的匹配结果.(其中s[:i]最后一个元素为s[i-1],p[:j]最后一个元素为p[j-1],搞懂这一点才看得懂代码的)
bool dp[sl + 1][pl + 1];
memset(dp, false, sizeof(dp));
//初始化 三步
dp[0][0] = true;
for(int i = 1; i < strlen(s) + 1; i++){
dp[i][0] = false;
}
for(int j = 1; j < strlen(p) + 1; j++){
dp[0][j] = j > 1 && dp[0][j - 2] && p[j - 1] == '*';
}
// 有必要对上面这个式子解释: 首先j>=2,因为j=1时,p不为空,也不存在首个元素就为*的情况,所以此时p与s肯定无法匹配.dp[0][j]对应p的子串,最后一个元素为p[j-1],只有p[j-1]=='*'时才有可能匹配成功.同时需要dp[0][j-2]同样为True时,dp[0][j]才能为True.(这个地方好好理解一下,对下面的总代码理解很重要)
for(int i = 1; i < strlen(s) + 1; i++){
for(int j = 1; j < strlen(p) + 1; j++){
if(p[j - 1] == '*'){
dp[i][j] = dp[i][j - 2] ||( (p[j - 2] == s[i - 1] || p[j - 2] =='.') && dp[i - 1][j]);
}else{
dp[i][j] = dp[i - 1][j - 1] &&( (s[i - 1] == p[j - 1]) || p[j - 1] == '.');
}
}
}
return dp[strlen(s)][strlen(p)];
}
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
//双指针 i, j
class Solution {
public:
int maxArea(vector<int>& height) {
if(height.size() <= 1) return -1;
int i = 0, j = height.size() - 1, res = 0;
while(i < j){
int h = min(height[i], height[j]);
res = max(res, h * (j - i));
if(height[i] < height[j]) ++i;
else --j;
}
return res;
}
};
双指针的运用,减少时间复杂度。我之前的方法不够好,多次运用vector和set去重,下方为大神写的,简洁易懂。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int len = nums.size();
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
set<vector<int>> s;
if(len < 3){
return ans;
}
int i, j, k;
for(i = 0; i < len; i++){
if(nums[i] > 0)
break;
vector<int> res = nums;
res.erase(res.begin() + i);
j = 0;
k = res.size() - 1;
while(j < k &&(i == 0 || nums[i] > nums[i - 1])){
if(res[j] + res[k] + nums[i] == 0){
vector<int> fs;
fs = {nums[i],res[j],res[k]};
sort(fs.begin(),fs.end());
if(!s.count(fs)){
ans.push_back(fs);
s.insert(fs);
}
j++;
k--;
}
else if(res[j] + res[k] + nums[i] < 0){
j++;
}
else if(res[j] + res[k] + nums[i] > 0){
k--;
}
}
}
return ans;
}
};
//大神写法 非常的好理解,我的直接太复杂了TT
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int target;
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; //去重
target = num[i];
if (target > 0) break; //排序好后如果target>0那么双指针l 与 r都至少是取他排序后面的数,也就是都比他大,因此可以直接跳过,不可能等于0
int l = i + 1, r = nums.size() - 1;
while (l < r) {
if (nums[l] + nums[r] + target < 0) ++l;
else if (nums[l] + nums[r] + target > 0) --r;
else {
ans.push_back({target, nums[l], nums[r]});
++l, --r;
while (l < r && nums[l] == nums[l - 1]) ++l;
while (l < r && nums[r] == nums[r + 1]) --r;
}
}
}
return ans;
}
};
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
参考leetcode本题题解中的大佬@YouLookDeliciousC的代码,利用map
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
map<string, vector<string>> m;
string temp;
for(int i = 0; i <strs.size(); i++){
temp = strs[i];
sort(temp.begin(), temp.end());
m[temp].push_back(strs[i]);//这一步很精妙
}
int len = m.size();
vector<vector<string>> ans;
ans.resize(len);
int cnt = 0;
//map> ::iterator it;
//注意auto的用法,可以省略上一步。
for(auto it = m.begin(); it != m.end(); it++){
ans[cnt++] = (*it).second;
// ans[cnt++] = it->second;
}
return ans;
}
};
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
/** 转至@SEU.FidGet评论大佬的清晰注释解答,非常的直观,发现自己的竖式乘法白学了...
num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
例: 123 * 45, 123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
index: 0 1 2 3 4
1 2 3
* 4 5
---------
1 5
1 0
0 5
1 2
0 8
0 4
---------
0 5 5 3 5
这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中 ,可见,这里有4中组合,分别是15;’10 和 12;05 和08 ;04,此处最高进位是0,但还是要注意如果为1的情况,因此要在数组里留给这进位的位置,即res[0]。
**/
class Solution {
public:
string multiply(string num1, string num2) {
vector<int> res(num1.size() + num2.size());
int m = num1.size(), n = num2.size();
if (num2[0] == '0' || num1[0] == '0') return "0"; // 先排除是否为0的结果。
/* res从左到右存储高位到低位两两相乘的结果 */
for(int i = num1.size() - 1;i >= 0; i--)
for(int j = num2.size() - 1; j >= 0; j--)
res[i + j + 1] += (num1[i] - '0') * (num2[j] - '0'); // 难点:因为是最低位存储最高位,所以要反过来放。
/* 左侧高位,右侧低位,因此结果最开始从右往左依次的进位,容易理解 */
for(int k = res.size() - 1; k > 0; k--){
if(res[k] >= 10){
res[k - 1] += res[k] / 10;
res[k] %= 10;
}
}
string res_str = "";
for(int i=0;i<res.size();i++){
if(i == 0 && res[0] == 0) continue; //最高位未进位则不处理 例如 2 * 3 = 6 而不是 06.
res_str =res_str + char(res[i] + '0');
}
return res_str;
}
};
仿造上一题的思路,只不过这里比如11+ 1, 要考虑各自的长度,不能直接计算。
class Solution {
public:
string addBinary(string a, string b) {
int la = a.size();
int lb = b.size();
int len = 0,max = 0; // max和len初始值都是a和b中最大的长度,但是len会变化,max不会变化。
int min_len = 0;
string temp; //temp用来存储 位数较大的那个字符串,例如“11” 与“1”中的"11"。
if(la > lb){
len = la;
temp = a;
max = la;
min_len = lb;
}
else{
max = lb;
len = lb;
temp = b;
min_len = la;
}
vector<int> res(len + 1, 0);
la = la - 1;
lb = lb - 1;
// for(int i = len - 1; i >= 0; i--){ //这个for循环只能试用于相同位数的字符串加法。
// res[i + 1] = (a[i] - '0') + (b[i] - '0');
// }
for(int i = 0; i < min_len; i++){
res[len--] = (a[la--] - '0') + (b[lb--] - '0');//
}
for(int i = max - min_len - 1; i >= 0; i--){ // 这里使用max。
res[len--] = (temp[i] - '0');
}
for(int i = max; i >= 1; i--){ // 同理这里也是max。
if(res[i] >= 2){
res[i - 1] = res[i - 1] + 1;
res[i] = res[i] - 2;
}
}
string res_str ="";
for(int i = 0; i < max + 1; i++){
if(i == 0 && res[i] == 0) continue;
res_str += (res[i] + '0');
}
return res_str;
}
};
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
(时间复杂度和内存使用都还太多,看来还是直接从string 上下手比较快0.0)
知识点:完整的括号匹配算法流程如下:
首先从前向后扫描字符串:
①遇到左括号 x,就把 x 压栈;
②遇到右括号 y:
如果发现栈顶元素x和该括号y匹配,则栈顶元素出栈,继续判断下一个字符;
如果栈顶元素x和该括号y不匹配,字符串不匹配;
如果栈为空,字符串不匹配;
③扫描完成后,如果栈恰好为空,则字符串匹配,否则,字符串不匹配。
class Solution {
public:
bool isValid(string s) {
int len = s.size();
stack<char> ans;
for(int i = 0; i < len; i++){
if(s[i] == '(' || s[i] == '{' || s[i] == '[' ){ //①遇到左括号 x,就把 x 压栈;
ans.push(s[i]);
}
else{ //遇到右括号
bool flag = isMatch(s[i], ans);
if(!flag)
return false;
ans.pop();
}
}
if(ans.empty())//③扫描完成后,如果栈恰好为空,则字符串匹配,否则,字符串不匹配。
return true;
return false;
}
bool isMatch(char s, stack<char> ans){
if(ans.empty())
return false;
if(s == ')'){
return ans.top() == '(';
}else if (s == ']'){
return ans.top() == '[';
}else{
return ans.top() == '{' ;
}
return true;
}
};
https://leetcode-cn.com/problems/simplify-path/solution/cti-jie-zhan-by-happyblacky/
示例1:
输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:"/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
示例 3:
输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:"/a/./b/../../c/"
输出:"/c"
示例 5:
输入:"/a/../../b/../c//.//"
输出:"/c"
示例 6:
输入:"/a//b////c/d//././/.."
输出:"/a/b/c"
//都可以 stack会慢一点,因为要逆序输出
class Solution{
public:
string simplifyPath(string path)
{
//vector dir = {};
stack<string> dir;
int loc = 0;
while(loc < path.size())
{
string name = "";
while(loc<path.size() && path[loc] != '/')
{
name += path[loc];
loc++;
}
if(name != "" && name != ".." && name != ".")
dir.push(name);
if(name == ".." && !dir.empty())
dir.pop();
loc++;
}
if(dir.empty())
return "/";
string dirpath = "";
vector<string> ans;
while(!dir.empty())//注意,如果目录是/home/foo,由于栈先进后出,会变成/foo/home,因此需要逆序
{
ans.push_back(dir.top());
dir.pop();
}
/* vector就可以直接循环
for(int i=0;i
for(int i = ans.size() - 1; i >= 0; i--){
dirpath +="/" + ans[i] ;
}
return dirpath;
}
};
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
//因为是排序数组,故可以用双指针的方法来做
class Solution {
public:
int removeDuplicates(vector& nums) {
int l, r;
if(nums.size() <= 1)
return nums.size();
l = 0, r = 1;
for(; r < nums.size(); r++){
if(nums[l] != nums[r]){
nums[++l] = nums[r];
}
}
return l + 1;
}
};
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if(words.size() == 0 || s.size() == 0)
return {};
int m = words[0].size();
int n = words.size();
if(s.size() < n * m)
return {};
//建立map tmp 和 mp 进行关联
map<string, int> mp, tmp;
for(int i = 0; i < words.size(); i++){
mp[words[i]]++; //考虑到words会有相同的单词,因此选用计数方法较简便
}
int j = 0;
for(int i = 0; i + n * m - 1 < s.size(); i++){
string str = "";
for(j = i; j < i + n * m; j = j + m){
str = s.substr(j, m);
map<string, int> :: iterator iter;
iter = mp.find(str);
if(iter != mp.end())
tmp[str]++;
else
break;
}
if(j == i + n * m && tmp == mp)
res.push_back(i);
tmp.clear();
}
return res;
}
};
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
//本来想通过 ans = ans * 10 + digit[i] 这种无脑的方法来查看进位变化, 结果leetcode早有准备,给了一个好几个元素组成的数组,直接越界。
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
int len = digits.size();
// 这个循环参考leetcode题解大佬@YHHZW 的思路,非常巧妙,从最后循环,如果除余不为0,则可以继续循环加一。
// 124 --》 125
// 129 --》 130
// 999 --》 000
for(int i = len - 1; i >= 0; i--){
digits[i] ++;
if(digits[i] % 10 != 0)
return digits;
else
digits[i] = 0;
}
//针对 999这种情况
if(digits[0] == 0){
vector<int> ans;
ans.push_back(1);
for(int i = 0; i < len; i++)
ans.push_back(digits[i]);
return ans;
}
return digits;
}
};
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
//双指针, 这种方法更快一些。底下注释为自己写法,时间太慢了。
class Solution {
public:
bool isPalindrome(string s) {
int left = 0, right = s.size() - 1;
while(left < right){
while(left < right && !isalnum(s[left]))
left ++;
while(left < right && !isalnum(s[right]))
right --;
if(tolower(s[left]) == tolower(s[right])){
left ++;
right --;
}
else{
return false;
}
}
return true;
}
};
// class Solution {
// public:
// bool isPalindrome(string s) {
// vector ans;
// for(int i = 0; i < s.size(); i++){
// if(isalnum(s[i])){
// ans.push_back(tolower(s[i]));
// }
// }
// int left = 0, right = ans.size() - 1;
// while(left < right){
// if(ans[left] == ans[right]){
// left ++;
// right --;
// }
// else{
// return false;
// }
// }
// return true;
// }
// };
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
class MyQueue {
public:
stack<int> ans;
stack<int> helper;
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
helper.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
while(!helper.empty()){
int temp = helper.top();
ans.push(temp);
helper.pop();
}
int tmp = ans.top();
ans.pop();
while(!ans.empty()){
int temp = ans.top();
helper.push(temp);
ans.pop();
}
return tmp;
}
/** Get the front element. */
int peek() {
while(!helper.empty()){
int temp = helper.top();
ans.push(temp);
helper.pop();
}
int res = ans.top();
while(!ans.empty()){
helper.push(ans.top());
ans.pop();
}
return res;
}
/** Returns whether the queue is empty. */
bool empty() {
return ans.empty() && helper.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
// 辅助栈的方法
class MinStack {
public:
/** initialize your data structure here. */
stack<int> ans;
stack<int> helper;
MinStack() {
}
void push(int x) {
ans.push(x);
if(helper.empty() || helper.top() >= x){
helper.push(x);
}
}
void pop() {
int temp = ans.top();
ans.pop();
if(temp == helper.top())
helper.pop();
}
int top() {
return ans.top();
}
int getMin() {
return helper.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
https://blog.csdn.net/qq_21815981/article/details/79833976
知识点: 快慢指针。
可以定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动;从第K步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当第一个指针到达链表的尾节点时候,第二个指针正好是倒数第K个节点,代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head == NULL ||(n == 1 && head -> next == NULL))
return NULL;
ListNode *fast = head, *slow = head;
for(int i = 0; i < n; i++){
fast = fast -> next;
}
if(!fast) //如果删除的是第一个结点(倒数第N个结点),直接返回后续的
return head -> next;
while(fast -> next != NULL){
fast = fast -> next;
slow = slow -> next;
}
slow -> next = slow ->next ->next;
return head;
}
};
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
知识点: C++ 中结构体的构造函数。
如这里的ListNode(int x) : val(x). next(NULL){ }
这句可以用 ListNode *ans = new ListNode(0) 创建了一个数据值为0,指针域为null的结点。这样子的话,在创建一个 ListNode *result = ans, 最后只要排除了初始化的0这个值就可以了,因此return result -> next 就好。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *p = l1, *q = l2;
ListNode *ans = new ListNode(0); //重点
ListNode *result = ans;
while(p != NULL && q != NULL){
if(p -> val > q -> val){
ans -> next = q;
q = q -> next;
ans = ans -> next;
}else{
ans -> next = p;
p = p -> next;
ans = ans ->next;
}
}
if(q != NULL)
ans -> next = q;
else if(p != NULL)
ans -> next = p;
return result -> next; //重点
}
};
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: `1->1->2->3->4->4->5->6`
知识点:分治法。
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
这题可以建立在上一个链表题目的基础之上,将多个链表不断的折半缩小,直到变成2个链表以内,在带入mergeKLists函数中进行合并,思维巧妙,掌握原理后不难理解。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()){
return NULL;
}
if(lists.size() == 1){
return lists[0];
}
if(lists.size() == 2){
return mergeTwoLists(lists[0],lists[1]);
}
int mid = lists.size()/2;
vector<ListNode*> sub_list1;
vector<ListNode*> sub_list2;
for(int i = 0;i<mid;i++){
sub_list1.push_back(lists[i]);
}
for(int i = mid;i<lists.size();i++){
sub_list2.push_back(lists[i]);
}
ListNode* l1 = mergeKLists(sub_list1);
ListNode* l2 = mergeKLists(sub_list2);
return mergeTwoLists(l1,l2);
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *l = new ListNode(0);
ListNode* ptr = l;
while(l1 != NULL && l2 != NULL){
if(l1->val <= l2->val){
ptr->next= l1;
l1 = l1->next;
ptr =ptr->next;
}else{
ptr->next = l2;
l2 = l2->next;
ptr =ptr->next;
}
}
if(l1 != NULL){
ptr->next = l1;
}
if(l2 != NULL){
ptr->next = l2;
}
return l -> next;
}
};
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
递归。这是这道题目的一个大佬评论给出的思路,即使我在前面做了一个题目,有了递归的感觉,但还是没法写出来TT 太差了。。 最大的缺陷我觉得应该是把它想成一个3个结点的链表,而不是我之前想的22分成,
大佬的博客,关于一篇如何处理递归的问题http://lylblog.cn/blog/4,非常不错。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == NULL || head -> next == NULL)
return head;
ListNode * next = head -> next;
head -> next = swapPairs(next -> next);
next ->next =head;
return next;
}
};
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
// dfs遍历, 需要邻接矩阵M[][], 标记数组是否被访问flag[], 统计个数count
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
int len = M[0].size();
bool flag[200] ={false};
int num = 0;
//dfs
for(int i = 0; i < len; i++){
if(flag[i] == false){//如果该点未被访问,即之前D[i][j] == 1 &&
dfs(i, M, flag);
num++;
}
}
return num;
}
void dfs(int i, vector<vector<int>>& M, bool flag[]){
flag[i] = true; // 表示该学生已经被访问
for(int j = 0; j < M.size(); j++){
if(flag[j] == false && M[i][j] == 1){// 如果2个人是朋友,并且是未被访问false(避免重复标记)
dfs(j, M, flag); //继续遍历该点,直到flag = false 或 M[i][j] == 0
}
}
}
};
//并查集的基本使用, 如果是朋友 ==> 则组合他们。 如果父节点不是本身,朋友圈加1
class Solution {
public:
//具有路径压缩的寻找父节点函数findFather,非递归写法
int findFather(vector<int>& father, int x){
while(father[x] != x){
x = father[x];
}
while(father[temp] != x){
i = father[temp];
father[temp] = x;
temp = i;
}
return x;
}
/*路径压缩的递归写法
int findFather(vector& father,int x)
{
if (x != father[x])
{
father[x] = findFather(father, father[x]);
}
return father[x];
}
*/
//合并2个结点Union
void Union(int i, int j, vector<int>& father){
int fa = findFather(father, i);
int fb = findFather(father, j);
if(fa != fb){
father[fb] = fa;
}
}
int findCircleNum(vector<vector<int>>& M) {
vector<int> father(M.size());
//初始化 init, 任意结点的父节点是自己本身
for(int i = 0; i < M.size(); i++){
father[i] = i;
}
for(int i = 0; i < M.size(); i++){
for(int j = i + 1; j < M.size(); j++){
if(M[i][j] == 1){ //如果是朋友,则合并,类比为在一条线上
Union(i, j, father);
}
}
}
int num = 0;
for(int i = 0; i < M.size(); i++){
if(father[i] == i){ // 如果父节点就是自己本身,则说明一定存在一个朋友圈(合并后 就可以去重了)
num ++;
}
}
return num;
}
};
给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
// DFS 线性扫描整个二维网格,如果一个结点包含 1,则以其为根结点启动深度优先搜索。在深度优先搜索过程中,每个访问过的结点被标记为 0。计数启动深度优先搜索的根结点的数量,即为岛屿的数量。
// 因此不需要增设bool flag数组。 grid数组可以代表标记数组。
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if(grid.size() == 0)
return 0;
int nc = grid[0].size();
int res = 0;
for(int i = 0; i < nr; i++){
for(int j = 0; j < nc; j++){
if(grid[i][j] == '1'){ // 注意是字符元素
res++;
dfs(i, j, grid);
}
}
}
return res;
}
// dfs 函数,此题就是要注意边界的选取,这是一个易错点。
void dfs (int i, int j, vector<vector<char>>& grid){
grid[i][j] = '0';
if(i - 1 >= 0 && grid[i - 1][j] == '1' ) dfs(i - 1, j, grid);
if(i + 1 < grid.size() && grid[i + 1][j] == '1' ) dfs(i + 1, j, grid);
if(j - 1 >= 0 && grid[i][j - 1] == '1' ) dfs(i, j - 1, grid);
if(j + 1 < grid[0].size() && grid[i][j + 1] == '1' ) dfs(i, j + 1, grid);
}
};
//BFS
// pair的用法 ,借鉴官方解析,可以不适用结构体 queue> m 并且pair有2个函数,可以直接使用m.first和m.second
// map 的first和second 必须借助迭代器才能使用,而不能单独使用
// for(map ::iterator t = m.begin(); t != m.end() m++) {
// int t1 = m->first;
// int t2 = m->second;
class Solution {
public:
struct land{
int r;
int c;
};
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if(grid.size() == 0)
return 0;
int nc = grid[0].size();
int res = 0;
queue<land> q;
for (int i = 0; i < nr; i++) {
for (int j = 0; j < nc; j++) {
if(grid[i][j] == '1'){
q.push({i,j});
grid[i][j] == '0';
res ++;
while(!q.empty()){
int t1, t2;
land temp = q.front();
t1 = temp.r;
t2 = temp.c;
q.pop();
if(t1 - 1 >= 0 && grid[t1-1][t2] == '1') {
q.push({t1-1, t2});
grid[t1-1][t2] = '0';
}
if(t1 + 1 < nr && grid[t1+1][t2] == '1') {
q.push({t1+1, t2});
grid[t1+1][t2] = '0';
}
if(t2 - 1 >= 0 && grid[t1][t2-1] == '1') {
q.push({t1, t2-1}); grid[t1][t2-1] = '0';
}
if(t2 + 1 < nc && grid[t1][t2+1] == '1') {
q.push({t1, t2+1}); grid[t1][t2+1] = '0';
}
}
}
}
}
return res;
}
};
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
说明:
输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
class Solution {
public:
bool Topologica_sort(int numCourses, vector<vector<int>> &ans, vector<int> &in_degree){
bool flag = true;
for(int i = 0; i < numCourses; i++){
int j = 0;
while(j < numCourses && in_degree[j] != 0){
j++;
}
if(j == numCourses)
return false;
in_degree[j] = -1;
for(int k = 0; k < ans[j].size(); k++){
in_degree[ans[j][k]]--;
}
}
return flag;
}
/* 这个函数借助了队列 ,并且使用了DFS算法。
bool Topologica_sort(int numCourses, vector> &ans, vector &in_degree){
queue q;
int cnt = 0;// 用来检验入列的元素,是否与总边数相等
int j = 0;
while(j < numCourses ){
if(in_degree[j] == 0){
q.push(j);
}
j++;
}
while(!q.empty()){
for(int i = 0; i < ans[q.front()].size(); i++){
in_degree[ans[q.front()][i]] --;
if(in_degree[ans[q.front()][i]] == 0){
q.push(ans[q.front()][i]);
}
}
q.pop();
cnt++;
}
// cout << cnt;
if(cnt == numCourses)
return true;
else
return false;
}
*/
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> ans(numCourses);
for(int i = 0; i < prerequisites.size(); i++){
ans[prerequisites[i][0]].push_back(prerequisites[i][1]) ;
}
vector<int> in_degree(numCourses);
for(int i = 0; i < numCourses; i++){
for(int j = 0; j < ans[i].size(); j++){
in_degree[ans[i][j]]++;
}
}
return Topologica_sort(numCourses, ans, in_degree);
}
};
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
void函数有return的作用:退出函数体,如果在函数体结尾处不加也可自动退出,如果在中途需要 退出的话就用return(如递归函数中。)
vector
知识点:回朔法(盲区)
需要再刷好几道才会写,默默学大佬的,对于递归我的理解一直很差,这次自己学到了一个小方法,稍微看个代码和题目自己画画图,例如这题可以画出2个分支,分别是‘(’和 ‘)’二叉树,类似一种DFS的思维。可能这就是数学思维吧,算我一个很致命的弱势,逻辑转的忒慢了,用时间题目 换速度(原来我也是个DP = =)。
/*
例如N = 3;
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]*/
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;
func(ans, "", 0, 0, n);
return ans;
}
void func(vector<string> &ans, string str, int left, int right, int n){
if(left > n || right > n || right > left) //right > left 表示括号匹配不合法。
return;
if(left == n && right == n){
ans.push_back(str); //利用ans 存储, 并回溯给上层。
return;
}
func(ans, str + '(', left + 1, right, n);
func(ans, str + ')', left, right + 1, n);
}
};
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> results;
vector<int> res;
vector<bool> flag(nums.size(),false);//标记元素是否被访问过
HDFS(results,res,flag,nums,0);
return results;
}
void HDFS(vector<vector<int>> &results,vector<int> &res,vector<bool> &flag,vector<int> nums,int dep){
if(dep==nums.size()){//达到树叶
results.push_back(res);
}
for(int i=0;i<nums.size();i++){//每层都对每个节点进行检测
if(!flag[i]){
flag[i]=true;//访问节点
res.push_back(nums[i]);
HDFS(results,res,flag,nums,dep+1); //进入下一层
res.pop_back();//往上一层回退
flag[i]=false;
}
}
return ;
}
};
作者:labuladong
链接:https://leetcode-cn.com/problems/same-tree/solution/xie-shu-suan-fa-de-tao-lu-kuang-jia-by-wei-lai-bu-/
来源:力扣(LeetCode)
给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入: 1 1
/ \ / \
2 3 2 3
[1,2,3], [1,2,3]
输入: 1 1
/ \
2 2
[1,2], [1,null,2]
输出: false
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
//判断二叉树是否为BST
//难点:root 需要做的不只是和左右子节点比较,而是要整个左子树和右子树所有节点比较。
boolean isValidBST(TreeNode root) {
return F(root, null, null);
}
boolean F(TreeNode root, TreeNode min, TreeNode max) {
if (root == null) return true;
if (min != null && root.val <= min.val) return false;
if (max != null && root.val >= max.val) return false;
return F(root.left, min, root)
&& F(root.right, root, max);
}
//在 BST 中查找一个数是否存在
boolean isInBST(TreeNode root, int target) {
if (root == null) return false;
if (root.val == target)
return true;
if (root.val < target)
return isInBST(root.right, target);
if (root.val > target)
return isInBST(root.left, target);
// root 该做的事做完了,顺带把框架也完成了,妙
}
//在 BST 中插入一个数,对数据结构的操作无非遍历 + 访问,遍历就是“找”,访问就是“改”。具体到这个问题,插入一个数,就是先找到插入位置,然后进行插入操作。
//上一个问题,我们总结了 BST 中的遍历框架,就是“找”的问题。直接套框架,加上“改”的操作即可。一旦涉及“改”,函数就要返回 TreeNode 类型,并且对递归调用的返回值进行接收。
TreeNode insertIntoBST(TreeNode root, int val) {
// 找到空位置插入新节点
if (root == null) return new TreeNode(val);
// if (root.val == val)
// BST 中一般不会插入已存在元素
if (root.val < val)
root.right = insertIntoBST(root.right, val);
if (root.val > val)
root.left = insertIntoBST(root.left, val);
return root;
}
参照上一题大佬的思路,需要增加一个协助函数F,这样才能符合判断“二叉排序树的子树也是一个二叉排序树”的性质,可以很快完成。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
return F(root, NULL, NULL);
}
bool F(TreeNode *root, TreeNode* min, TreeNode *max) {
if (root == NULL) //空树也是二叉排序树。
return true;
if (min != NULL && root->val <= min->val) //如果最小的数(左子树)大于根结点
return false;
if (max != NULL && root->val >= max->val)
return false;
return F(root->left, min, root)
&& F(root->right, root, max);
}
};
//利用中序遍历好看二叉树是否为有序的
class Solution {
double last = -Double.MAX_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
if (isValidBST(root.left)) {
if (last < root.val) {
last = root.val;
return isValidBST(root.right);
}
}
return false;
}
}
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
0916更新,写的更加具体,是自己更能理解。
//方法一:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
//第一种特殊情况:这里的root可以理解为是最初的情况,无根节点,因此h为0。
if(root == NULL)
return 0;
//第二种特殊情况:左右字数为空就是只有一个根节点,那么返回1.
else if(root -> left == NULL && root -> right == NULL)
return 1;
//第三种,右子树不为空,那就只递归右子树
else if(root -> left == NULL)
return 1 + maxDepth(root -> right);
else if(root ->left ==NULL)
return 1 + maxDepth(root -> left);
return max(maxDepth(root -> left), maxDepth(root -> right)) + 1;
}
};
//方法②DFS
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
int num = 0;
queue<TreeNode *> que; // 重点:建立队列。
que.push(root); //先将根节点入列。
while(!que.empty()){ // 终止条件,队列中无结点元素。
int n = que.size(); // 重点:统计队列内元素个数,表明他们位于同一层
for(int i = 0;i < n;i++){
TreeNode *cur = que.front();
if(cur->left != NULL)
que.push(cur->left);
if(cur->right != NULL)
que.push(cur->right);
que.pop(); // n次循环,每次检查后该结点的左右子树后,就弹出队列。
}
num++; // 深度 + 1
}
return num;
}
};
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
和105比较类似的,可以用递归去做,但是有一个细节卡了很久,就是[1,2]这个测试用例,一直想不清楚,后俩看了下最小深度,才恍然大悟,but太困了递归还是参考了题解的方法。
/*
这个测试样例比较特殊,应该输出的是2,而不是1.
1
/
2
*/
/*
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
if(root == NULL)
return 0;
else if(root -> left == NULL && root -> right == NULL)
return 1;
else if(root -> left == NULL)
return 1 + minDepth(root -> right);
else if(root -> right == NULL)
return 1 + minDepth(root -> left);
return min(minDepth(root -> left) , minDepth(root -> right)) + 1;
}
};