文章预览:
- 数组
- 链表
- 哈希表
- 字符串
- 栈与队列
- 二叉树
- 144. 二叉树的前序遍历
- 145. 二叉树的后序遍历
- 94. 二叉树的中序遍历
- 二叉树的统一迭代法
- 二叉树的层次遍历
- 226.翻转二叉树
- 101. 对称二叉树
- 100. 相同的树
- 572. 另一棵树的子树
- 559. N 叉树的最大深度
- 222.完全二叉树的节点个数
- 110. 平衡二叉树
- 257. 二叉树的所有路径
- 404. 左叶子之和
- 513.找树左下角的值
- 112. 路径总和
- 113. 路径总和 II
- 106. 从中序与后序遍历序列构造二叉树
- 105. 从前序与中序遍历序列构造二叉树
- 654. 最大二叉树
- 617. 合并二叉树
- 700.二叉搜索树中的搜索
- 98. 验证二叉搜索树
- 530. 二叉搜索树的最小绝对差
- 501. 二叉搜索树中的众数
- 236. 二叉树的最近公共祖先
- 235. 二叉搜索树的最近公共祖先
- 701. 二叉搜索树中的插入操作
- 450. 删除二叉搜索树中的节点
- 669. 修剪二叉搜索树
- 108.将有序数组转换为二叉搜索树
- 538.把二叉搜索树转换为累加树
参考刷题链接代码随想录
数组
1.二分查找
704. 二分查找
题目链接
https://leetcode-cn.com/problems/binary-search/
python代码
代码1:
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
左闭右开区间
"""
right,left=len(nums),0
while left < right:
key=(right+left)/2
if nums[key] < target:
left=key+1
elif nums[key] > target:
right=key
else :
return key
return -1
代码2
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
左闭右闭区间
"""
right,left=len(nums)-1,0
while left<=right:
key=(right+left)/2
if nums[key] < target:
left=key+1
elif nums[key] > target:
right=key-1
else :
return key
return -1
c++代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int k = nums.size();
int left=0;
int right=k-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid] > target){
right=mid-1;
}
else if(nums[mid] < target){
left=mid+1;
}
else
return mid ;
}
return -1;
}
};
相关题目:
35.搜索插入位置
力扣链接
解法一:暴力解法
c++
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
for (int i = 0; i < nums.size(); i++) {
// 分别处理如下三种情况
// 目标值在数组所有元素之前
// 目标值等于数组中某一个元素
// 目标值插入数组中的位置
if (nums[i] >= target) { // 一旦发现大于或者等于target的num[i],那么i就是我们要的结果
return i;
}
}
// 目标值在数组所有元素之后的情况
return nums.size(); // 如果target是最大的,或者 nums为空,则返回nums的长度
}
};
解法二:二分法
python
class Solution(object):
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left,right=0,len(nums)-1
while left<=right:
mid=(left+right)/2
if nums[mid]<target:
left=mid+1
elif nums[mid]>target:
right=mid-1
else:
return mid
#插在头部
#下面这部分可直接写成return right+1
if nums[0]>target:
return 0
elif nums[len(nums)-1]<target:
return len(nums)
else:
for i in range(len(nums)):
if nums[i]<target<nums[i+1]:
return i+1
c++代码:
写法一:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
//插在头部
if(nums[0]>target)
return 0;
//插在尾部
if(nums[right]<target)
return right+1;
//插在数组中间某个地方
while(left<=right){
int mid=(left+right)/2;
if(nums[mid] < target){
left=mid+1;
}
else if(nums[mid] >= target){
right=mid-1;
}
}
return left;
}
};
写法二:
while循环结束后会出现right+1=left的情况
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]<target){
left=mid+1;
}
else if(nums[mid]>target){
right=mid-1;
}
else return mid;
}
return right+1;//return left也可以
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
力扣题目
解法一:二分法
C++:
class Solution {
public:
/*
情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
*/
//寻找数组中第一个等于target的值
int seachLeft(vector<int>& nums, int target){
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>=target){
right=mid-1;
}
else {
left=mid+1;
}
}
return left;
}
int seachright(vector<int>& nums, int target){
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]<=target){
left=mid+1;
}
if(nums[mid]>target){
right=mid-1;
}
}
return right;
}
vector<int> searchRange(vector<int>& nums, int target) {
//二分查找法
int left=seachLeft(nums,target);
int right=seachright(nums,target);
if(right-left>=0) return {left,right};
else return {-1,-1};
}
};
写法二:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
int mid=0;
while(left<=right){
mid=(left+right)/2;
if(nums[mid]>target) right=mid-1;
else if(nums[mid]<target) left=mid+1;
else break;
}
if(left>right) return {-1,-1};//说明不存在
//说明找到了目标值
for(int i=mid;i>=0;i--){//这里是等于mid开始找,因为可能只有一个
if(nums[i]==nums[mid]) left=i;
else break;
}
for(int i=mid;i<nums.size();i++){
if(nums[i]==nums[mid]) right=i;
else break;
}
return {left,right};
}
};
python代码
class Solution(object):
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
#二分查找看是否存在target
def binlookTarget(nums,target):
left,right=0,len(nums)-1
while left<=right:
mid=(left+right)/2
if nums[mid] < target:
left=mid+1
elif nums[mid] > target:
right=mid-1
else:
return mid
return -1
s1=s2=binlookTarget(nums,target)
if s1==-1:
return [-1,-1]
else :
#根据找出的target下标左右滑动找出边界
while s1-1>=0 and nums[s1-1]==target :#注意这两个条件的先后顺序不能反,不然会出现超过list维度的错误
s1-=1#找出左边界
while s2+1<len(nums) and nums[s2+1]==target:
s2+=1#找出右边界
return [s1,s2]
69.x 的平方根
解法一:二分法
python代码:
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
#用二分法来求解
left,right=0,x
ans=0
while left<=right:
mid=(right+left)/2
if mid*mid > x:
right=mid-1
else:
left=mid+1
ans=mid
return ans
解法二:
这种情况x=1时要单独讨论,x=1时mid会等于0,不然会输出0
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
#用二分法来求解
left,right=0,x
if x==1:
return 1
else:
while left<=right:
mid=(right+left)/2
if mid*mid > x:
right=mid
if left==right-1:
return left
elif mid*mid < x:
left=mid
if left==right-1:
return left
else:
return mid
解法三:
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
long mid = l + (r - l) / 2;//注意这里必须是long,如果定义为int型,会导致下面定义的k发生溢出
/*小于INT最大值的常数,自动保存为INT类型
INT * INT 得到的结果也是INT,即使赋值给 long long 也有溢出的情况发生
如果数据太大 建议用 long long = long long * long long 保证不溢出
*/
long long k=mid*mid;
if (k <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};
c++:
class Solution {
public:
int mySqrt(int x) {
//二分法
int left=0;
int right=x;
if(x==0){
return 0;//后面用了/,所以要除了这种情况
}
if(x==1){
return 1;
}
while(left <= right){
int mid=left+(right-left)/2;
if(mid<x/mid){//这样写是防溢出
left=mid+1;
}
else if(mid>x/mid){
right=mid-1;
}
else return mid;
}
return right;
}
};
367.有效的完全平方数
python代码:
class Solution(object):
def isPerfectSquare(self, num):
"""
:type num: int
:rtype: bool
"""
left,right=1,num
while left<=right:
mid=(left+right)//2
if mid*mid>num:
right=mid-1
elif mid*mid<num:
left=mid+1
else:
return True
return False
c++
这道题就必须用m*m<num来判断了,因为必须刚好相等,用/会取整
class Solution {
public:
bool isPerfectSquare(int num) {
int left=1;
int right=num;
if(num==1) return true;
while(left<=right){
long m=left+(right-left)/2;//这里用long防止溢出
if(m*m<num){
left=m+1;
}
else if(m*m>num){
right=m-1;
}
else return true;
}
return false;
}
};
2.双指针
27.移除元素
题目链接
https://leetcode-cn.com/problems/remove-element/submissions/
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
python代码
法一:暴力求解
踩坑记录
先用python写了下面这段代码:
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
#法一:暴力解法,两层循环,找到后直接覆盖
k=len(nums)
for i in range(k):
if nums[i]==val:
for j in range(i+1,k):
nums[j-1]=nums[j]
k=k-1
i=i-1
return k
发现提交上去是错的,于是在vscode里面找了下原因:
原因:Python在使用for循环的时候不能修改循环中使用的变量
要想改变变量的值的话用while循环
正确python代码
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
k=len(nums)
i=0
while i<k:
if nums[i]==val:
for j in range(i+1,k):
nums[j-1]=nums[j]
k=k-1
i=i-1
i+=1
return k
c++:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//暴力求解
int s=nums.size();
int i=0;
while(i<s){
if(nums[i]==val){
for(int j=i;j<s-1;j++){
nums[j]=nums[j+1];
}
s--;
i--;//当后面一位也是val时也需要进行判断
}
i++;
}
return s;
}
};
法二:双指针
快指针只要一遇到val值就+1,直到遇到非val值,接着将该位置的值赋值给慢指针,接着慢指针+1,以此一直循环下去直到退出循环,最终慢指针所指的位置就是数组的长度
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
k=len(nums)
slowIndex,fastIndex=0,0
while fastIndex < k:
if nums[fastIndex]!=val:
nums[slowIndex]=nums[fastIndex]
slowIndex+=1
fastIndex+=1
return slowIndex
c++代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//法二:双指针
int fast=0;//定义快指针
int slow=0;
int s=nums.size();
while(fast<s){
if(nums[fast]!=val){
nums[slow]=nums[fast];
slow++;
}
fast++;
}
return slow;
}
};
法三:二分法
使用二分法查出val的起始位置,再进行覆盖
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left=0;
int right=nums.size()-1;
sort(nums.begin(),nums.end());
int mid=0;
while(left<=right){
mid=(right+left)/2;
if(nums[mid]<val) left=mid+1;
else if(nums[mid]>val) right=mid-1;
else break;
}
if(right<left) return nums.size();//没找到val
for(int i=mid;i>=0;i--){
if(nums[i]==val) left=i;
else break;
}
for(int i=mid;i<nums.size();i++){
if(nums[i]==val) right=i;
else break;
}
int index=right+1;
for(int i=left;index<nums.size();i++,index++){
nums[i]=nums[index];//每个数都需要前移
}
return nums.size()-(right-left+1);
}
};
相关题目
26.删除排序数组中的重复项
方法一:双指针
思路:使用双指针,刚开始fast在slow的后一位,如果fast和slow值相等就移动fast指针,直到找到和slow不同的值,由于是删除重复项,所以先将slow指针+1,再进行赋值。由于这道题只是删掉重复项,跟27题比起来还是有差别的,这道题代码中的slow最终指向的是输出数组的最后一个值,所以数组长度应该是slow+1
这个代码和27题代码很相似,需要多理解一下
c++:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
//双指针
int slow=0;
int fast=1;
int s=nums.size();
if(s==0) return 0;
while(fast<s){
if(nums[slow]!=nums[fast]){
slow++;
nums[slow]=nums[fast];
}
fast++;
}
return slow+1;
}
};
283.移动零
c++:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//不复制数组的话可以使用双指针
int slow=0;
int fast=0;
while(fast<nums.size()){
if(nums[fast]!=0){
nums[slow]=nums[fast];
slow++;
}
fast++;
}
for(slow;slow<nums.size();slow++){
nums[slow]=0;
}
}
};
844.比较含退格的字符串
法一:使用栈的思想
c++代码如下:
写法一:使用string充当栈
class Solution {
public:
//使用栈的思想,遇到#就将前一个数弹出来
string realString(string& s){
int i=0;
string s1;//放新的字符串的栈
while(i<s.size()){
if(s[i]!='#') s1+=s[i];
else if(!s1.empty()){
s1.pop_back();//一定要确定不是空栈才可以弹出
}
i++;
}
return s1;
}
bool backspaceCompare(string s, string t) {
string s_real=realString(s);
string t_real=realString(t);
return s_real==t_real;
}
};
写法二:使用stack
class Solution {
public:
bool backspaceCompare(string s, string t) {
stack<char> st;
for(char c:s){
if(c=='#'){
if(!st.empty()) st.pop();
}
else st.push(c);
}
stack<char> st2;
for(char c:t){
if(c=='#'){
if(!st2.empty()) st2.pop();
}
else st2.push(c);
}
if(st.size()!=st2.size()) return false;
while(!st.empty()&&!st2.empty()){
if(st.top()!=st2.top()) return false;
st.pop();
st2.pop();
}
return true;
}
};
法二:双指针
c++:
class Solution {
public:
//双指针
bool backspaceCompare(string s, string t) {
int i=s.size()-1;//s的指针
int j=t.size()-1;//t的指针
int skipnum_s=0;
int skipnum_t=0;
while(i>=0||j>=0){
while(i>=0){
if(s[i]=='#') {
skipnum_s++;
i--;
}
else if(skipnum_s!=0) {
skipnum_s--;
i--;
}
else{
break;//s[i]!=‘#’且未消除的#数量为0就跳出当前循环和t[j]进行比较
}
}
while(j>=0){
if(t[j]=='#') {
skipnum_t++;
j--;
}
else if(skipnum_t!=0)
{
skipnum_t--;
j--;
}
else{
break;
}
}
if(i>=0&&j>=0){
if(s[i]!=t[j]) return false;
}
else {
if(i>=0||j>=0) return false;
}
i--;
j--;
}
return true;
}
};
977.有序数组的平方
法一:暴力排序
c++:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
//法一:暴力排序
for(int i=0;i<nums.size();i++){
nums[i]=nums[i]*nums[i];
}
sort(nums.begin(),nums.end());
return nums;
}
};
法二:双指针
c++:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
//法二:双指针
//最大的数存在于数组两边
//利用双指针分别指向开头和结尾,同时向中间靠近,同时定义一个新数组,将最大的数一个一个放进去
int i=0;
int j=nums.size()-1;
vector<int> new_nums(j+1);
int k=j;
while(i<=j){
if(nums[i]*nums[i]<nums[j]*nums[j]){
new_nums[k] = nums[j]*nums[j];
j--;
}
else{
new_nums[k]=nums[i]*nums[i];
i++;
}
k--;
}
return new_nums;
}
};
3.长度最小的子数组
209.长度最小的子数组
法一:暴力法
c++代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
//法一:暴力法
int length=nums.size();
int s=0;
int label=0;
for(int i=0;i<nums.size();i++){
s=0;
for(int j=i;j<nums.size();j++){
s+=nums[j];
if(s>=target){
label=1;
if(length>j-i+1)
length=j-i+1;
break;
}
}
}
if(label) return length;
else return 0;
//不能这样写,因为会存在整个数组就是最小子数组的情况,本来该返回数组长度,结果返回了0,导致输出错误
//可以设置一个label来判断
// if(length!=nums.size()) return length;
// else return 0;
}
};
法二:滑动窗口法(可以理解为双指针的一种)
一些思考:滑动窗口更在乎的是窗口内的内容,而双指针法更在乎的是两个指针指向的数之间的一些联系
c++:
自写:二刷再提交已经超出时间限制了
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
//法二:滑动窗口法
int i=0;
int j=0;
int sum=0;
int length=nums.size()+1;
while(j<nums.size()){
sum=0;
for(int k=i;k<=j;k++)
{
sum+=nums[k];
}
if(sum>=target){
if(length>j-i+1)
length=j-i+1;
i++;
}
else j++;
}
return length==nums.size()+1?0:length;
}
};
代码随想录解法:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0; // 滑动窗口数值之和
int i = 0; // 滑动窗口起始位置
int subLength = 0; // 滑动窗口的长度
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
总结:代码随想录中的解法通过直接在sum中减掉当前nums[i]中的值,接着再进行i++操作来滑动窗口,这样就不需要每次都用一个新循环来计算窗口的数值总和,节约了很多时间。
相关题目
904 水果成篮
法一:哈希表+滑动窗口
c++:
class Solution {
public:
int totalFruit(vector<int>& tree) {
unordered_map<int, int> basket;//利用哈希表做篮子,键值不允许重复,所以就算遇到了相同类型水果也不用管
int result = 0, len = 0;//len为窗口的长度
int left = 0;
//i相当于右窗口,left相当于左窗口
for (int i = 0; i < tree.size(); i++) {
basket[tree[i]]++;//以数组形式插入
len++;
while (basket.size() > 2) {
basket[tree[left]]--;
if (basket[tree[left]] == 0) basket.erase(tree[left]);//为空必须要删除,不然算size1的时候还在里面
left++;//种类超过2个移动左窗口直到只有两个种类
len--;
}
if (result < len) {
result = len;//只有两个种类的水果最大数目
}
}
return result;
}
};
76.最小覆盖子串
法一:滑动窗口
最后一个测试用例超出时间限制
class Solution {
public:
bool compare_s(unordered_map<char,int> m,unordered_map<char,int> m_t){
//判断滑动窗口是否包含t
for(auto it=m_t.begin();it!=m_t.end();it++){
//个数小于t中字母个数,就错误
if(m[it->first]<it->second) return false;
}
return true;
}
string minWindow(string s, string t) {
if(s.size()<t.size()) return "";
unordered_map<char,int> m;
unordered_map<char,int> m_t;
for(char c:t){
m_t[c]++;
}
int slow=0;
int fast=0;
int len=INT_MAX;
vector<int> result={-1,-1};
while(fast<s.size()){
m[s[fast]]++;
while(compare_s(m,m_t)){//s包含了t
if(len>fast-slow+1){
result[0]=slow;
result[1]=fast;
len=fast-slow+1;
}
m[s[slow]]--;
if(m[s[slow]]==0) m.erase(s[slow]);
slow++;
}
fast++;
}
if(result[0]==-1) return "";
return s.substr(result[0],len);
}
};
官方写法思想和我的差不多,但是官方的能通过
class Solution {
public:
unordered_map <char, int> ori, cnt;
bool check() {
for (const auto &p: ori) {
if (cnt[p.first] < p.second) {
return false;
}
}
return true;
}
string minWindow(string s, string t) {
for (const auto &c: t) {
++ori[c];
}
int l = 0, r = -1;
int len = INT_MAX, ansL = -1, ansR = -1;
while (r < int(s.size())) {
if (ori.find(s[++r]) != ori.end()) {
++cnt[s[r]];
}
while (check() && l <= r) {
if (r - l + 1 < len) {
len = r - l + 1;
ansL = l;
}
if (ori.find(s[l]) != ori.end()) {
--cnt[s[l]];
}
++l;
}
}
return ansL == -1 ? string() : s.substr(ansL, len);
}
};
自己的思路修改后通过了,主要原因是比较函数的&,如果不加&的话就会对比较的哈希表进行复制再作比较,很花时间,所以不涉及修改操作时尽量还是用引用
class Solution {
public:
bool compare_s(unordered_map<char,int> &m,unordered_map<char,int> &m_t){
//判断滑动窗口是否包含t
for(auto it=m_t.begin();it!=m_t.end();it++){
//个数小于t中字母个数,就错误
if(m[it->first]<it->second) return false;
}
return true;
}
string minWindow(string s, string t) {
if(s.size()<t.size()) return "";
unordered_map<char,int> m;
unordered_map<char,int> m_t;
for(char c:t){
m_t[c]++;
}
int slow=0;
int fast=0;
int len=INT_MAX;
int result=-1;
while(fast<s.size()){
if(m_t.find(s[fast])!=m_t.end()) m[s[fast]]++;
while(compare_s(m,m_t)&&slow<=fast){//s包含了t
if(len>fast-slow+1){
result=slow;
len=fast-slow+1;
}
if(m_t.find(s[slow])!=m_t.end()) m[s[slow]]--;
slow++;
}
fast++;
}
if(result==-1) return "";
return s.substr(result,len);
}
};
4.螺旋矩阵II
59.螺旋矩阵II
链接:59.螺旋矩阵II
法一:模拟
矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2);如果n为奇数的话,需要单独给矩阵最中间的位置赋值
每一圈循环的次数都是4次,算出需要循环的圈数,如果n为奇数的话,再单独对最中间的位置进行赋值。
设置两个坐标分别控制xy方向的走向,上一次循环的起点就是下一次循环的终点。
每一圈的起始点在正方形矩阵的对角线上
c++代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
//初始化n * n二维动态数组,初始化值为0
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组,初始化为n个vector,这n个vector又通过n个0来初始化
int x,y=0;//每一圈起始坐标,第一圈从(0,0)开始赋值,第二圈从(1,1)开始赋值,第三圈从(2,2)开始赋值
int now_x,now_y=0;//现在正在赋值的坐标点
int turns=n/2;//需要循环的圈数
int num=1;//给二维数组赋值
int offset=1;//控制每一圈里面每一个循环的长度
while(turns--){//先进行循环再将turns的值-1
//均是左闭右开
for(now_y=y;now_y<y+n-offset;now_y++){//起点从y开始,那终点也应该加上y
res[x][now_y]=num++;//从左到右进行赋值
}
for(now_x=x;now_x<x+n-offset;now_x++){
res[now_x][now_y]=num++;//从上到下进行赋值
}
for(;now_y>y;now_y--){//注意这里的判断条件是大于y
res[now_x][now_y]=num++;//从右到左进行赋值
}
for(;now_x>x;now_x--){
res[now_x][now_y]=num++;//从下到上赋值
}
x++;
y++;
offset+=2;//第二圈比第一圈的边长小2
}
//判断n是否为奇数,奇数需要对最中心的值单独赋值
if(n%2==1){
res[n/2][n/2]=n*n;
}
return res;
}
};
写法二:更加简化
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
//n为偶数,中间没有单独的
//(n+1)/2圈
vector<vector<int>> result(n,vector<int>(n,0));
int turns=(n+1)/2;
int y=0;
int x=0;
int num=1;
for(int i=0;i<turns;i++){//i为起点坐标(i,i)
//从左到右
for(y=i;y<n-1-i;y++){//n-1-i为终点
result[i][y]=num++;
}
//从上到下
for(x=i;x<n-1-i;x++){
result[x][y]=num++;
}
//从右到左
for(y;y>i;y--){
result[x][y]=num++;
}
//下到上
for(x;x>i;x--){
result[x][y]=num++;
}
}
if(n%2==1) result[n/2][n/2]=num;
return result;
}
};
相关题目
54.螺旋矩阵
法一:模拟
只有最短边是奇数时才会出现凑不齐一圈的情况
c++代码:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int m=matrix.size();
int n=matrix[0].size();
vector<int> nums(m*n,0);//返回的一维数组
int turns=min(m,n)/2;//循环的圈数
int x,y=0;//每一圈起始坐标
int i,j=0;//当前正在返回的元素的坐标
int offset=1;
int k=0;//记录一维数组的下标
while(turns--){
for(j=y;j<y+n-offset;j++){
nums[k++]=matrix[x][j];//从左到右赋值
}
for(i=x;i<x+m-offset;i++){
nums[k++]=matrix[i][j];//从上到下赋值
}
for(j;j>y;j--){
nums[k++]=matrix[i][j];//从右到左赋值
}
for(i;i>x;i--){
nums[k++]=matrix[i][j];//从下到上赋值
}
x++;
y++;
offset+=2;
}
//中间的数需要单独输出,需要输出max-min+1个数
//判断是行数大于列数还是列数大于行数,两种情况不一样
if(min(m,n)%2==1){
int res=max(m,n)-min(m,n)+1;
if(m>n){
for(int p=0;p<res;p++){
nums[k++]=matrix[x+p][y];
}
}
else{
for(int p=0;p<res;p++){
nums[k++]=matrix[x][p+y];
}
}
}
return nums;
}
};
剑指 Offer 29. 顺时针打印矩阵
模拟:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> result;
int m=matrix.size();
if(m==0) return result;
int n=matrix[0].size();
int x=0;
int y=0;
int turns=min(m,n)/2;
for(int i=0;i<turns;i++){
for(y=i;y<n-1-i;y++) result.push_back(matrix[i][y]);
for(x=i;x<m-1-i;x++) result.push_back(matrix[x][y]);
for(y;y>i;y--) result.push_back(matrix[x][y]);
for(x;x>i;x--) result.push_back(matrix[x][y]);
}
if(min(m,n)%2==1){
int index=turns;
if(m>n){//竖着
for(int i=0;i<m-n+1;i++){
result.push_back(matrix[index++][turns]);
}
}
else{
for(int i=0;i<n-m+1;i++){
result.push_back(matrix[turns][index++]);
}
}
}
return result;
}
};
链表
203.移除链表元素
法一:定义一个虚拟头结点进行操作
c++:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//定义一个虚拟头结点进行操作
ListNode* new_head=new ListNode(0);
new_head->next=head;
ListNode* vir_head=new_head;
while(vir_head->next!=NULL){
if(vir_head->next->val==val){
ListNode* temp=vir_head->next;
vir_head->next=vir_head->next->next;
delete temp;//c++需要手动释放内存
}
else{
vir_head=vir_head->next;
}
}
head=new_head->next;//如果头结点需要删除的话new_head的指向是会变的,所以必须要重新赋值
delete new_head;
return head;
}
};
707.设计链表
这道题需要自己定义链表,这是一个链表类,类里定义了很多操作链表的方法,给的代码并没有给出节点的定义,所以里面还需要定义节点
c++:
踩坑记录:
delet temp->next;//不能这样写,会报错,思考了一下,大概是因为temp->next是指向temp的下一个节点的指针,不能删掉指针,应该是删掉节点
class MyLinkedList {//一个链表类,封装了链表的一些操作函数
public:
// 定义链表
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};//这里一定要记得写;
MyLinkedList() {
vir_head=new ListNode(0);//设置虚拟头结点进行操作
_size=0;//链表初始化大小为0
}
//获取链表中第 index 个节点的值。如果索引无效,则返回-1。
int get(int index) {
if(index>=_size||index<0) return -1;
ListNode* cur=new ListNode(0);
cur=vir_head->next;
while(index--){
cur=cur->next;
}
return cur->val;
}
//在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
void addAtHead(int val) {
ListNode* cur=new ListNode(val);//创建值为 val 的节点
cur->next=vir_head->next;
vir_head->next=cur;
_size++;
}
//将值为 val 的节点追加到链表的最后一个元素。
void addAtTail(int val) {
ListNode* cur=new ListNode(val);//创建值为 val 的节点
ListNode* temp=new ListNode(0);
temp=vir_head;
while(temp->next!=NULL){
temp=temp->next;
}
temp->next=cur;//经过while循环后temp->next为NULL
cur->next=NULL;
_size++;
}
//在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
void addAtIndex(int index, int val) {
if(index > _size) return;
ListNode* cur=new ListNode(val);//创建值为 val 的节点
if(index<0){
//在头部插入节点
cur->next=vir_head->next;
vir_head->next=cur;
}
ListNode* temp=new ListNode(0);
temp=vir_head;
while(index--){
temp=temp->next;
}
cur->next=temp->next;
temp->next=cur;
_size++;
}
//如果索引 index 有效,则删除链表中的第 index 个节点。
void deleteAtIndex(int index) {
if(index>=_size||index<0) return;
ListNode* temp=new ListNode(0);
temp=vir_head;
while(index--){
temp=temp->next;
}
//第index 个节点在temp的下一个节点
ListNode* temp1=temp->next;
temp->next=temp->next->next;
//delet temp->next;//不能这样写,会报错,思考了一下,大概是因为temp->next是指向temp的下一个节点的指针,不能删掉指针,应该是删掉节点
delete temp1;
_size--;
}
private:
int _size;
ListNode* vir_head;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
写法二:超出时间限制
class MyLinkedList {
public:
struct listNode{
int val;
listNode* next;
listNode(int x):val(x),next(NULL) {}
};
MyLinkedList() {
head=new listNode(0);//定义虚拟头节点
listSize=0;
}
int get(int index) {
if(index>=listSize||index<0){
return -1;
}
int s=0;
listNode* cur = head;
int k=0;
while(cur->next!=NULL){
if(s==index){
listNode* temp=cur->next;
k=temp->val;
}
s++;
cur=cur->next;
}
return k;
}
void addAtHead(int val) {
listNode* L=new listNode(val);
L->next=head->next;
head->next=L;
listSize++;
}
void addAtTail(int val) {
listNode* tailNode=new listNode(val);
listNode* cur=head;
while(cur->next!=NULL){
cur=cur->next;
}
cur->next=tailNode;
tailNode->next=NULL;
listSize++;
}
void addAtIndex(int index, int val) {
listNode* newNode=new listNode(val);
listNode* cur=head;
if(index<0){
newNode->next=head->next;
head->next=newNode;
}
if(index==listSize){
while(cur->next!=NULL){
cur=cur->next;
}
cur->next=newNode;
newNode->next=NULL;
}
if(index>listSize){
return;
}
int s=0;
cur=head;
while(cur->next!=NULL){
if(s==index){
newNode->next=cur->next;
cur->next=newNode;
}
s++;
cur=cur->next;
}
listSize++;
}
void deleteAtIndex(int index) {
listNode* cur=head;
int s=0;
while(cur->next!=NULL){
if(s==index){
listNode* temp=cur->next;
cur->next=cur->next->next;
delete temp;
listSize--;
}
}
}
private:
int listSize;
listNode* head;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
在visual studio上的调试代码如下:
#include <cstddef>
#include <iostream>
using namespace std;
class MyLinkedList {
public:
struct listNode {
int val;
listNode* next;
listNode(int x) :val(x), next(NULL) {}
};
MyLinkedList() {
head = new listNode(0);//定义虚拟头节点
listSize = 0;
}
int get(int index) {
if (index >= listSize || index < 0) {
return -1;
}
int s = 0;
listNode* cur = head;
int k = 0;
while (cur->next != NULL) {
if (s == index) {
listNode* temp = cur->next;
k = temp->val;
break;
}
s++;
cur = cur->next;
}
return k;
}
void addAtHead(int val) {
listNode* L = new listNode(val);
L->next = head->next;
head->next = L;
listSize++;
}
void addAtTail(int val) {
listNode* tailNode = new listNode(val);
listNode* cur = head;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = tailNode;
tailNode->next = NULL;
listSize++;
}
void addAtIndex(int index, int val) {
listNode* newNode = new listNode(val);
listNode* cur = head;
if (index < 0) {
newNode->next = head->next;
head->next = newNode;
}
if (index == listSize) {
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = newNode;
newNode->next = NULL;
}
if (index > listSize) {
return;
}
int s = 0;
cur = head;
while (cur->next != NULL) {
if (s == index) {
newNode->next = cur->next;
cur->next = newNode;
break;
}
s++;
cur = cur->next;
}
listSize++;
}
void deleteAtIndex(int index) {
if (index >= listSize || index < 0) return;
listNode* cur = head;
int s = 0;
while (cur->next != NULL) {
if (s == index) {
listNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
listSize--;
break;
}
s++;
cur = cur->next;
}
}
void output() {
listNode* cur = head;
while (cur->next != NULL) {
cout << cur->next->val;
cout << "-";
cur = cur->next;
}
cout << endl;
}
private:
int listSize;
listNode* head;
};
int main() {
MyLinkedList* obj = new MyLinkedList();
obj->addAtHead(1);
obj->output();
cout << "已在头部添加成功" << endl;
obj->addAtTail(3);
obj->output();
obj->addAtIndex(1,2);
obj->output();
cout<<obj->get(1)<<endl;
obj->deleteAtIndex(1);
obj->output();
cout<<obj->get(1)<<endl;
}
206.反转链表
法一:双指针法
c++:
踩坑记录:陷入了死循环。当cur为第一个时,cur=right、2指向了1后,下一次循环cur->next指向了1,陷入了死循环
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur=head;
ListNode* left;
ListNode* right;
while(cur->next!=NULL){
left=cur;
right=cur->next;
cur=cur->next;
right->next=left;
}
return cur;
}
};
正确写法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//使用双指针法反转链表,只需改变指针指向即可
ListNode* left=NULL;
ListNode* right=head;
ListNode* temp=new ListNode();
while(right!=NULL){
temp=right->next;
right->next=left;
left=right;
right=temp;
}
return left;
}
};
法二:递归法
c++:
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
return reverse(cur,temp);//最终会返回最后一次递归中pre的位置,也就是反转前的链表尾部
}
ListNode* reverseList(ListNode* head) {
// 和双指针法初始化是一样的逻辑
// ListNode* cur = head;
// ListNode* pre = NULL;
return reverse(NULL, head);
}
};
24. 两两交换链表中的节点
c++:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* left=head;
ListNode* right=head;
ListNode* temp;//交换时的临时节点
ListNode* vir_head=new ListNode(0);//虚拟头结点
vir_head->next=head;
ListNode* move_head=vir_head;//每一轮交换的虚拟头结点
int label=0;//记录虚拟头结点变化次数
while(left&&left->next){
right=left->next;
move_head->next=right;
//完成节点交换
temp=right->next;//while里面判断了right不为NULL
right->next=left;
left->next=temp;
//改变虚拟头结点指向
if(label<1){//虚拟头结点只会变换一次
vir_head->next=right;
label++;
}
//进行下一轮交换
left=temp;
//right=temp->next;//不能在这里移动right节点是因为temp此时可能为NULL,所以temp的下一位是无效的,代码会出错,需要在while里面判断一下此时的temp(left)是否为空
move_head=move_head->next->next;
}
return vir_head->next;
}
};
写法二:模拟
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL) return head;
ListNode* dummy_head=new ListNode(0);
dummy_head->next=head;
ListNode* cur=dummy_head;
while(cur->next!=NULL&&cur->next->next!=NULL){
ListNode* temp=cur->next;
ListNode* tmp1 = cur->next->next->next;
cur->next=cur->next->next;
cur->next->next=temp;
temp->next=tmp1;
cur=cur->next->next;
}
return dummy_head->next;
}
};
19.删除链表的倒数第N个节点
c++:
通过两个指针操作,代码随想录里面的解答就是通过fast指针先移动n+1,然后slow指针再移动,直到fast指针移动到null处,这样的话slow指针移动了size-n步,就可以一趟操作,slow指针就可以刚好移动到要被删除的结点前面的一个节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//使用双指针完成
//设置虚拟头结点
//删掉倒数第n个结点,指向虚拟节点的指针需要走size-n+1步走到需删除的节点处
ListNode* vir_head=new ListNode(0);//虚拟头结点
vir_head->next=head;
ListNode* left=vir_head;
ListNode* right=vir_head;
for(int i=0;i<n;i++){
right=right->next;//right指针先走n步
}
while(right->next){//right走到尾部
right=right->next;
left=left->next;//走到被删节点的上一个节点
}
ListNode* temp=left->next;
left->next=temp->next;
delete temp;
return vir_head->next;
//不用单独考虑头结点被删的情况,这种情况下,left没有发生移动,最终将left指向了头结点的下一个结点,此时left和vir_head都是指向的同一个指针,所以返回的vir_head->next和left->next是一样的,不会出错
}
};
写法二:先计算出链表长度,再找到需要删除的节点进行删除
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy_head=new ListNode(0);
dummy_head->next=head;
ListNode* cur=head;
int size=0;
while(cur){
size++;
cur=cur->next;
}
int turns=size-n;
cur=dummy_head;
while(turns--){
cur=cur->next;
}
ListNode* temp=cur->next;
cur->next=temp->next;
delete temp;
return dummy_head->next;
}
};
面试题 02.07. 链表相交
首先需要理解题意,如果存在交点的话,交点以及交点之后的所有节点的地址都是相等的
解法一:这种方法类似于暴力法,A指针指向A的头部,接着遍历B,若没有相等的指针则移动一位A,直到找到和A指针地址相同地址的指针B,否则返回null
解法一时间复杂度比较高
c++:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//首先需要理解题意,如果存在交点的话,交点以及交点之后的所有节点的地址都是相等的,并不是交点以及交点之后的值相等
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* A=headA;
ListNode* B=headB;
ListNode* tem=new ListNode(0);//存放相等的点
int label=0;
while(A!=NULL&&B!=NULL){
//经过该循环的B与A相等或B到了尾部
while(A!=B){
if(B!=NULL)
B=B->next;
else break;
}
if(B==NULL) B=headB;//这里写if(B)时会出错,所有的测试用例都直接输出的第一个数
else{
return A;
}
A=A->next;//
}
return 0;
}
};
解法二:
将两个单链表尾部对齐
c++:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA=headA;
ListNode* curB=headB;
int sizeA=0;
int sizeB=0;
while(curA){
sizeA++;
curA=curA->next;
}
while(curB){
sizeB++;
curB=curB->next;
}
curA=headA;curB=headB;
if(sizeA>sizeB){
int turns=sizeA-sizeB;
while(turns--) curA=curA->next;
}
else{
int turns=sizeB-sizeA;
while(turns--) curB=curB->next;
}
while(curA){
if(curA==curB) return curA;
curA=curA->next;
curB=curB->next;
}
return NULL;
}
};
142.环形链表II
这种情况下如果fast指针进入环内,而slow指针还在环外的话,会进入死循环,fast指针会一直在环内转圈
解法:若存在环,fast指针一定先进入环内,slow指针后进入环内,两者一定在环内相遇,且一定在slow进入环内的第一圈相遇(因为fast指针走得快,所以fast指针一定先走完第一圈,因为fast走的路程是slow的两倍,考虑极限情况,链表为一个环,x=0,若fast刚好走完两圈回到入环点时,slow指针刚好走完第一圈,此时在入环点相遇,一般情况下x!=0,当fast刚好走完两圈回到入环点时,slow指针一定还没走完第一圈,所以在第一圈的时候fast一定会经过slow,两者相遇)
推导过程见代码随想录
c++:
一定要在同一个起点开始行动
踩坑记录:刚开始把fast设为了head->next,然后循环体的最后再移动fast和slow,虽然这样写两个指针肯定都能进入环内。但是后面的判断入口的公式就被推翻了,有可能永远也找不到入口,就会导致超出时间限制。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow=head;
ListNode* fast=head;
ListNode* meet=new ListNode(0);
//判断是否有环,有环的话快慢指针一定会相遇
//需要思考没有环的情况怎么判断,fast指针每次走两步,所以只需判断fast或者fast->next是否为null
while(fast!=NULL&&fast->next!=NULL){
slow=slow->next;
fast=fast->next->next;//fast指针每次走两步
if(fast==slow){
meet=fast;
break;//找到了相遇节点退出循环
}
}
if(fast==NULL||fast->next==NULL){//说明没有环
return NULL;
}
slow=head;
while(slow!=meet){
slow=slow->next;
meet=meet->next;
}
return slow;
}
};
写法二:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL) return NULL;
ListNode* slow=head;
ListNode* fast=head;
while(fast&&fast->next!=NULL&&slow->next!=NULL){
slow=slow->next;
fast=fast->next->next;
if(slow==fast){
ListNode* index1=head;
ListNode* index2=slow;
while(index1!=index2){
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}
};
哈希表
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
242.有效的字母异位词
c++:
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26]={0};
for(int i=0;i<s.size();i++){
record[s[i]-'a']++;
}
for(int j=0;j<t.size();j++){
record[t[j]-'a']--;
}
for(int k=0;k<26;k++){
if(record[k]!=0) return false;
}
return true;
}
};
相关题目
383.赎金信
c++:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char,int> m;
for(char c:ransomNote) m[c]--;
for(char c:magazine) m[c]++;
for(char c:ransomNote){
if(m[c]<0) return false;
}
return true;
}
};
49.字母异位词分组
c++:
首先这道题的函数的返回类型就已经带点提示了,map里面当key相同时,value是不可以重复的,但是这里的value类型是vector类型,所以可以装很多string字符
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
//使用排序
unordered_map<string, vector<string>> mp;//创建哈希表
for (string str: strs) {//读取每个字符串
string key = str;
sort(key.begin(), key.end());//排序
mp[key].emplace_back(str);//将排序后相同的字符串作为key值,原来字符串用vector容器接收
}
//返回类型是vector<vector<string>>
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {//auto会自动判断it类型
//it是迭代器,类型是vector<string>
//it->second是value值
ans.emplace_back(it->second);//将哈希表的内容一个个传入ans容器中,也可以用push_back()
}
return ans;
}
};
438.找到字符串中所有字母异位词
c++:
踩坑记录:
c++中不可以直接对两个数组整体判断是否相等(eg:if(AB)),必须单独对其中的每一个值进行比较,除非重载“”,但是STL容器(eg:vector等)可以直接对其进行比较
sort()函数是类似于快速排序的方法,时间复杂度为n*log2(n),执行效率较高。
有时候用sort超时了,可以新建哈希表记录每个元素的个数,这样时间复杂度是n
刚开始使用哈希表map来记录个数,发现超时了,但是换成固定大小的数组就不会超时。可能是因为在初始化unordered_map时不会确定大小,在赋值时一个个插入,比较费时,所以如果能确定size的题目,并且使用了嵌套循环的可以使用数组
正确代码如下:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> result;
int record_s[26]={0};
int record_p[26]={0};
int len=s.size()-p.size();
int num=0;
if(len<0) return result;//这种情况要单独讨论
for(int k=0;k<p.size();k++){
record_p[p[k]-'a']++;
record_s[s[k]-'a']++;
}
for(int k=0;k<26;k++){
if(record_s[k]==record_p[k])
num++;
}
if(num==26)
result.push_back(0);
for(int i=0;i<len;i++){
//将前p.size()个数据清空,记录下一轮的数据,最后再和record_p进行对比,这个解法很妙
//我之前是在这个循环下面又嵌套一层循环去截取p.size()个数据,结果超过时间限制了
//这里相当于每次减去第i个位置的值,然后再加上第i+p.size()位置的值
--record_s[s[i]-'a'];
++record_s[s[i+p.size()]-'a'];
//判断数组是否相等
num=0;
for(int k=0;k<26;k++){
if(record_s[k]==record_p[k])
num++;
}
if(num==26)
result.push_back(i+1);
}
return result;
}
};
二刷用了嵌套循环没有超出时间限制
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int p_record[26]={0};
vector<int> result;
int compare=0;
if(s.size()<p.size()) return result;
for(int i=0;i<p.size();i++)
{
p_record[p[i]-'a']++; //记录p字符串中的各个字母的数目
}
//主要思想是用一个和p一样大的窗口在s上做滑动,每次取出p.size()个字符串,然后进行比较
for(int i=0;i<(s.size()-p.size()+1);i++){
int s_record[26]={0};
for(int j=0;j<p.size();j++){
s_record[s[i+j]-'a']++;
}
//这里可以改进用vector直接存储结果就可以直接进行对比
compare=0;
for(int j=0;j<26;j++){
if(s_record[j]==p_record[j]){
compare++;
}
}
if(compare==26){
result.push_back(i);
}
}
return result;
}
};
349. 两个数组的交集
c++:
解法一:利用哈希表unordered_set
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//利用哈希表 无序唯一
unordered_set<int> num1(nums1.begin(),nums1.end());//nums1.begin(),nums1.end()区间内的数据构造哈希表
unordered_set<int> num2(nums2.begin(),nums2.end());
unordered_set<int> result;
for(int num:num2){
if(num1.find(num)!=num1.end()){//find函数返回的是迭代器
//如果没有找到num这个数的话,会返回这个容器的结束迭代器(set.end())
//end返回unordered_set最后一个元素下一个位置的迭代器
result.insert(num);
}
}
return vector<int>(result.begin(),result.end());
}
};
解法二:使用数组来记录两个数组的元素的值和个数
但是数组需要指定大小,这道题的nums1和nums2都限制了元素的大小,因此可以指定data1的大小
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> result;
int data1[1001]={0};//存放nums1的元素的值
int data2[1001]={0};
for(int i=0;i<nums1.size();i++){
data1[nums1[i]]++;
}
for(int i=0;i<nums2.size();i++){
data2[nums2[i]]++;
}
for(int i=0;i<1001;i++){
if(data1[i]!=0&&data2[i]!=0){
result.push_back(i);
}
}
return result;
}
};
解法三:使用unordered_map<int,int>,需要进行擦除,因为结果中每个数只会出现一次
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int,int> m1;
vector<int> result;
for(auto it=nums1.begin();it!=nums1.end();it++) m1[*it]++;
for(auto it=nums2.begin();it!=nums2.end();it++){
if(m1.find(*it)!=m1.end()){
result.push_back(*it);
m1.erase(*it);
}
}
return result;
}
};
相关题目
350.两个数组的交集 II
c++:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
//利用哈希表
//先处理短的数组,再处理长数组
unordered_map<int,int> m;
vector<int> result;
if(nums1.size()>nums2.size()){
nums1.swap(nums2);
}
for(int num:nums1){
++m[num];//先存放短的数组中各个数字的个数
}
for(int num:nums2){
//先判断nums1中有无该数字
if(m[num]!=0){
result.push_back(num);//有该数字,说明属于交集,放入结果中
--m[num];//不管num在哪个数组的数量多,该代码都只会执行最少次数
}
}
return result;
}
};
二刷解法:利用数组,感觉这里用的数组的作用和unordered_map<int,int>的作用类似,下标/第一个int都是放在nums中的元素值,数组值/第二个int都是放的元素个数值
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> result;
int data1[1001]={0};
int data2[1001]={0};
for(int i=0;i<nums1.size();i++){
data1[nums1[i]]++;
}
for(int i=0;i<nums2.size();i++){
data2[nums2[i]]++;
}
for(int i=0;i<1001;i++){
if(data1[i]!=0&&data2[i]!=0){
int s=data1[i]>data2[i]?data2[i]:data1[i];
for(int j=0;j<s;j++){
result.push_back(i);
}
}
}
return result;
}
};
202. 快乐数
c++:
set会自动排序,unordered_set是无序的
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> s;
s.insert(n);
while(n!=1){
s.insert(n);
int sum=0;
while(n){
int k=n%10;
sum+=k*k;
n=n/10;
}
if(s.find(sum)!=s.end()) return false;
n=sum;
}
return true;
}
};
写法二:
class Solution {
public:
bool isHappy(int n) {
//需要快速判断一个数是否出现在循环里面,考虑用哈希表的方法
//值会越来越大,最后接近无穷大。这种情况永远不会出现,具体见官方解答,所有的数最终都不会超过三位数,所以只会变成1或者进入循环
unordered_set<int> m;//这里用set也会通过
int sum=0;
while(1){
while(n){
sum+=(n%10)*(n%10);
n=n/10;
}
if(sum==1)
return true;
if(m.find(sum)!=m.end()) return false;
m.insert(sum);
n=sum;
sum=0;
}
}
};
1. 两数之和
c++:
解法一:暴力解法,使用两层for循环
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> result;
int s=0;
unordered_map<int,int> temp;
for(int i=0;i<nums.size()-1;i++){
for(int j=i+1;j<nums.size();j++){
s=0;
s=nums[i]+nums[j];
if(s==target){
result.push_back(i);
result.push_back(j);
}
}
}
return result;
}
};
解法二:使用哈希表
疑问:如果出现nums[i]相同的情况会怎么样?
答:不重要,出现第一个答案就返回了
eg:nums={3,3}
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//利用哈希表查找
unordered_map<int,int> m;
vector<int> result;
for(int i=0;i<nums.size();i++){
m[nums[i]]=i;//将数组元素放入哈希表中(如果出现nums[i]相同的情况会怎么样)
}
int i=0;
while(i<nums.size()){
auto iter=m.find(target-nums[i]);
if(iter!=m.end()){
if(iter->second!=i){//判断是否找的是本身(eg:target=6,3+3=6,此时两个下标相等)
result.push_back(iter->second);
result.push_back(i);
return result;
}
}
i++;
}
return {};//返回一个空容器
}
};
代码随想录的解法很巧妙的避免了我的代码中的会返回两个相同的下标的情况,因为它是先查询哈希表中的数是否有和当前需要插入的数符合要求的,如果没有再插入该数,如果有的话再返回这两个数的下标,所以不会出现重复的
写法三:使用multimap<int,int> 重复的key也会记录下来
和map容器相比,multimap未提供at()成员方法,也没有重载[ ] 运算符。
find函数如果有多个结果都可以匹配,那么只会返回第一个了
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> result;
multimap<int,int> m;
for(int i=0;i<nums.size();i++) m.insert(pair<int,int>(nums[i],i));
for(auto it=m.begin();it!=m.end();it++){
auto index=m.find(target-it->first);
if(index!=m.end()&&index!=it){
result.push_back(index->second);
result.push_back(it->second);
break;
}
}
return result;
}
};
第454题.四数相加II
c++:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//这道题只需要输出满足条件的个数,不需要输出具体的下标
//使用哈希表m1存放数组A和B的每两个元素之和以及个数
unordered_map<int,int> m1;
int sum=0;
for(int i=0;i<nums1.size();i++){
for(int j=0;j<nums2.size();j++){
++m1[nums1[i]+nums2[j]];
}
}
for(int c:nums3){
for(int d:nums4){
if(m1.find(-(c+d))!=m1.end()){
//在剩下两个数组中搜寻是否有四数相加等于0的情况
sum+=m1[-(c+d)];
}
}
}
return sum;
}
};
二刷写法:
结果超时了,和上面的写法思想上差不多,感觉超时的原因可能是因为用了multiset。因为multiset会对插入的数进行排序,比较耗时。但是想要两两相加的和可以出现重复的,如果使用unordered_set还是会出现重复的,所以可以考虑使用unordered_map,这些哈希表都有find。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//两个数组为一组,计算两两组合的和,然后再利用哈希表进行查找
//和可以出现重复的
multiset<int> s1;
// multiset<int> s2;
int s=0;
int n=nums1.size();
int result=0;
//记录第一个和第二个数组中的和
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
s=nums1[i]+nums2[j];
s1.insert(s);
}
}
//记录第三个和第四个数组中的和
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
s=nums3[i]+nums4[j];
if( s1.find(-s)!=s1.end()){
result=result+s1.count(-s);
}
}
}
return result;
}
};
第15题. 三数之和
c++:
这道题刚开始使用哈希表来做,但是认真读题才发现是输出不重复的三个数组值,并不是下标值,浪费了很多时间。
以下答案参考了随想录
vector内部没有find函数,采用的是algorithm的find函数
朴素的查找方法,O(n).
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//采用双指针方法
vector<vector<int>> result;
sort(nums.begin(), nums.end());
int left=0;
int right=nums.size()-1;
for(int i=0;i<nums.size()-2;i++){
//如果每次循环的第一个数大于0了,说明没有符合条件的三元组了,直接返回result就行
if(nums[i]>0){
return result;
}
//a+b+c=0
// 正确去重a方法
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
left=i+1;
right=nums.size()-1;
while(left<right){
if((nums[i]+nums[right]+nums[left])==0){
//这样进行去重会超出时间限制,vector的find所需时间比较久
// if(find(result.begin(),result.end(),vector<int>{nums[i], nums[left], nums[right]})==result.end())
// result.push_back(vector<int>{nums[i], nums[left], nums[right]});
result.push_back({nums[i],nums[right],nums[left]});
//还需要去重b和c
//排序后b和c发生重复的情况都是相邻的位置发生重复
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
else if((nums[i]+nums[right]+nums[left])>0){
right--;
}
else{
left++;
}
}
}
return result;
}
};
第18题. 四数之和
c++:
第一次踩坑记录,剪枝没做好,这道题的target是任意数,如果是负数的话上一道题的写法就不太适用,需要加上限制条件
感觉这道题的去重和剪枝比较难想全面,需要多总结多思考
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());//由小到大排序
int left=0;
int right=nums.size()-1;
if(nums.size()<4) return result;
//使用两层for循环确定a和b
for(int i=0;i<nums.size()-3;i++){//限制了nums的长度必须大于等于4
if(nums[i]>target){//如果整个数组都是负数,target也是负数的话,这样写就容易错过正确答案
return result;
}
if(i>0&&nums[i]==nums[i-1]){
continue;
}
for(int j=i+1;j<nums.size()-2;j++){
right=nums.size()-1;
if((j>i+1)&&nums[j]==nums[j-1]){
j++;
}
left=j+1;
while(left<right){
if((nums[i]+nums[j]+nums[left]+nums[right])==target){
result.push_back({nums[i],nums[j],nums[right],nums[left]});
while(left<right&&nums[left]==nums[left+1])left++;
while(left<right&&nums[right]==nums[right-1])right--;
left++;
right--;
}
else if((nums[i]+nums[j]+nums[left]+nums[right])>target){
right--;
}
else{
left++;
}
}
}
}
return result;
}
};
改正后的答案:
踩坑记录:
long sum=nums[i]+nums[j]+nums[left]+nums[right];
这样写会溢出,因为四个数都是int型的,在相加的过程中都是用int储存,会溢出
正确写法:long sum=(long)nums[i]+nums[j]+nums[left]+nums[right];
这样写相当于把nums[i]转化成long型数据,依次相加的结果都是long型的
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());//由小到大排序
int left=0;
int right=nums.size()-1;
if(nums.size()<4) return result;
//使用两层for循环确定a和b
for(int i=0;i<nums.size()-3;i++){//限制了nums的长度必须大于等于4
if(nums[i]>target&&nums[i]>=0){//如果整个数组都是负数,target也是负数的话,这样写就容易错过正确答案
return result;
}
if(i>0&&nums[i]==nums[i-1]){
continue;
}
for(int j=i+1;j<nums.size()-2;j++){
if((nums[i]+nums[j]>target)&&nums[i]+nums[j]>=0){
break;
}
right=nums.size()-1;
if((j>i+1)&&nums[j]==nums[j-1]){
// j++;//这里不能这么写,因为直接这样写不加continue的话j并没有得到判断,容易超出范围
continue;
}
left=j+1;
while(left<right){
//这里不能对四个数相加的和加括号,不然也会溢出,
//因为四个数相加会 直接得到int型的数,会溢出,而不加括号是直接转换成long型的
if((long)nums[i]+nums[j]+nums[left]+nums[right]==target){
result.push_back({nums[i],nums[j],nums[right],nums[left]});
while(left<right&&nums[left]==nums[left+1])left++;
while(left<right&&nums[right]==nums[right-1])right--;
left++;
right--;
}
else if((long)nums[i]+nums[j]+nums[left]+nums[right]>target){
right--;
}
else{
left++;
}
}
}
}
return result;
}
};
字符串
344 反转字符串
c++:
class Solution {
public:
void reverseString(vector<char>& s) {
//使用双指针
int slow=0;
int fast=s.size()-1;
int temp=0;
while(slow<fast){
temp=s[slow];
s[slow]=s[fast];
s[fast]=temp;
slow++;
fast--;
}
}
};
541. 反转字符串 II
c++:
这里我自己写的解法比随想录的解法要复杂一点,但是会比较清晰,随想录里面是每次移动2k个位置,然后判断剩余的字符数是否大于k,相当于把
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
这两种情况合并为一起了,因为都是反转前k个字符
class Solution {
public:
string reverseStr(string s, int k) {
int epoch=s.size()/(2*k);
for(int i=0;i<s.size(),epoch>0;i+=2*k,epoch--){//反转整数个2k
reverse(s.begin()+i,s.begin()+i+k);
}
int res=s.size()-s.size()/(2*k)*(2*k);
if(res<k){
reverse(s.begin()+s.size()/(2*k)*(2*k),s.end());
}
else{
reverse(s.begin()+s.size()/(2*k)*(2*k),s.end()-res+k);
}
return s;
}
};
剑指 Offer 05. 替换空格
c++:
踩坑记录:先声明string s,再通过下标对s进行赋值是会越界的,但是可以用push_back进行插入
法一:使用额外的辅助空间
class Solution {
public:
string replaceSpace(string s) {
//法一:使用额外的辅助空间
string result;
for(int i=0;i<s.size();i++){
if(s[i]==' '){
result.push_back('%');
result.push_back('2');
result.push_back('0');
}
else{
result.push_back(s[i]);
}
}
return result;
}
};
法二:代码随想录里面的双指针的写法
扩充原数组大小,通过j指针判断当前是否是空格,通过i指针从后往前对新数组的每一个位置进行赋值,不需要额外空间
class Solution {
public:
string replaceSpace(string s) {
//双指针
int cout=0;
for(char c:s){
if(c==' ') cout++;
}
int i=s.size()-1;
s.resize(s.size()+2*cout);
int j=s.size()-1;
for(i,j;i>=0;i--){
if(s[i]==' '){
s[j]='0';
s[j-1]='2';
s[j-2]='%';
j=j-3;
}
else{
s[j--]=s[i];
}
}
return s;
}
};
151. 反转字符串中的单词
解法:不使用额外空间,先对字符串反转,再去除字符串的多余空格,最后再对每个单词进行反转操作
这道题主要是想考双指针,所以尽量不要用库函数。
注意erase的时间复杂度还是很高的
class Solution {
public:
void reverse(string &s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
string reverseWords(string s) {
int slowIndex=0;
int fastIndex=s.size()-1;
while(slowIndex<s.size()&&s[slowIndex]==' ') slowIndex++;
while(fastIndex>=0&&s[fastIndex]==' ') fastIndex--;
s.erase(fastIndex+1,s.size()-fastIndex);
s.erase(0,slowIndex);
//去除中间空格
slowIndex=0;
int i=0;//快指针
for(;i<s.size();i++){
if(s[i]!=' '){
if(slowIndex!=0) s[slowIndex++]=' ';
while(i<s.size()&&s[i]!=' '){
s[slowIndex++]=s[i++];
}
}
}
s.resize(slowIndex);//取前面的slowIndex个
//开始反转
for(int i=0;i<s.size();i++){
if(s[i]!=' '){
int start=i;
while(i<s.size()&&s[i]!=' '){
i++;
}
reverse(s,start,i-1);
}
}
reverse(s,0,s.size()-1);
return s;
}
};
剑指Offer58-II.左旋转字符串
c++
法一:申请额外空间,先插入后部分字符,再插入前n个字符
class Solution {
public:
string reverseLeftWords(string s, int n) {
string result;
for(int i=n;i<s.size();i++){
result.push_back(s[i]);
}
for(int i=0;i<n;i++){
result.push_back(s[i]);
}
return result;
}
};
法二:
class Solution {
public:
void reverse(string &s,int begin ,int end){
for(int i=begin,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
string reverseLeftWords(string s, int n) {
//先单独反转前n个字符和后面的字符
reverse(s,0,n-1);
reverse(s,n,s.size()-1);
//最后反转整个字符串
reverse(s,0,s.size()-1);
return s;
}
};
28. 找出字符串中第一个匹配项的下标
c++
解法一:属于暴力解法。依次访问haystack的每个字符,并检查是否和needle的第一个字符相等,如果相等的话就开始检查接下来的几个字符和needle是否相等
class Solution {
public:
int strStr(string haystack, string needle) {
int slow=0;
int result=-1;
int j=1;
for(int i=0;i<haystack.size();i++){
if(haystack[i]==needle[0]){
for(j=1;j<needle.size();j++){
if(haystack[i+j]!=needle[j]) break;
}
if(j==needle.size()) {
result=i;
break;
}
}
}
return result;
}
};
解法二:kmp算法。目前还不是理解的很透彻,匹配失败后跳转的模式串的位置可以保证这个位置前的字符和文本串的光标所在位置的前几个字符相等,所以这部分字符就不需要再匹配了,可以节约时间,这样做相当于可以直接找到文本串中下一次开始的位置,而不需要像法一中每次移动一步去寻找
next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)
定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。
class Solution {
public:
void getNext(int* next,const string &s){
//统一做了-1操作
next[0]=-1;
int j=-1;
for(int i=1;i<s.size();i++){
while(j>=0&&s[i]!=s[j+1]){
j=next[j];
}
if(s[i]==s[j+1]){
j++;
}
next[i]=j;
}
}
int strStr(string haystack, string needle) {
int next[needle.size()];
getNext(next,needle);//得到next表
int j=-1;//j指向模式串里面的下标-1
for(int i=0;i<haystack.size();i++){//i为文本串起始位置
while(j>=0&&haystack[i]!=needle[j+1]){//不匹配的情况
j=next[j];//查找上一个匹配的下标位置
}
if(haystack[i]==needle[j+1]){
j++;//同时后移i和j
}
if(j==(needle.size()-1))
{
return i-j;//说明匹配上了,返回开始的下标值
}
}
return -1;
}
};
459. 重复的子字符串
c++
法一:暴力解法,循环查找每一个子串
class Solution {
public:
bool repeatedSubstringPattern(string s) {
bool result=false;
for(int i=1;i<=s.size()/2;i++){
//子串的长度为i+1
string sub_s;
if(s.size()%i!=0)
{
continue; //强迫开始下一次循环
}
//比较子串和剩余部分是否匹配
int j=0;
for(j=i;j<s.size();j++){
if(s[j]!=s[j-i]){//这样写就可以遍历所有情况了
break;//不匹配,跳出循环
}
}
if(j==s.size()){
result=true;
break;
}
}
return result;
}
};
法二:移动匹配
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string t=s+s;
//去掉首尾字符,避免搜索到首尾字符串
t.erase(t.begin());
t.erase(t.end()-1);
//std::string::npos等于size_type类型可以表示的最大值,用来表示一个不存在的位置
if(t.find(s)!=std::string::npos) return true;
else return false;
}
};
法三:KMP算法
因为 最长相等前后缀的规则,当一个字符串由重复子串组成的,最长相等前后缀不包含的子串就是最小重复子串。
class Solution {
public:
void getNext(int* next,string &s){
next[0]=-1;//统一做了减一的操作
int j=-1;//j代表此时的下标减一/i下标之前的最长相等前后缀的长度-1
for(int i=1;i<s.size();i++){
while(j>=0&&s[i]!=s[j+1]){
j=next[j];//查询前一位的next值,进行回退,因为下标是做了-1操作,所以j就代表前一位的位置
}
if(s[i]==s[j+1]){
j++;
}
next[i]=j;//更新next数组的值
}
}
bool repeatedSubstringPattern(string s) {
//KMP算法
int next[s.size()];
getNext(next,s);
int len=s.size();
if(next[len-1]!=-1&&len%(len-next[len-1]-1)==0){
return true;
}
else return false;
}
};
栈与队列
栈和队列都是容器适配器,两者都不允许有遍历行为
232.用栈实现队列
c++
class MyQueue {
public:
stack<int> stin;//输入栈
stack<int> stout;//输出栈
MyQueue() {
}
void push(int x) {
stin.push(x);
}
int pop() {
if(stout.empty()){//输出栈为空
while(!stin.empty()){
stout.push(stin.top());
stin.pop();
}
}
int k=stout.top();
stout.pop();
return k;
}
int peek() {
//这个代码的思路和pop类似,应该再精简代码,直接利用队列已有的pop函数,再把弹出的数添加回去
if(stout.empty()){//输出栈为空
while(!stin.empty()){
stout.push(stin.top());
stin.pop();
}
}
return stout.top();
}
bool empty() {
if(stin.empty()&&stout.empty()){
return true;
}
return false;
}
};
/**
* 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();
*/
写法二:
class MyQueue {
public:
stack<int> stin;//输入栈
stack<int> stout;//输出栈
MyQueue() {
}
void push(int x) {
//stout里面放了队列的全部元素,栈顶就是队头
while(!stout.empty()){
stin.push(stout.top());
stout.pop();
}
stin.push(x);
while(!stin.empty()){
stout.push(stin.top());
stin.pop();
}
}
int pop() {
int k=stout.top();
stout.pop();
return k;
}
int peek() {
return stout.top();
}
bool empty() {
return stout.empty();
}
};
225. 用队列实现栈
c++
法一:用两个队列实现,但是不知道为什么用for循环就会出错,for和while实现的不是都一样的吗?
发现问题所在:for循环的条件里面不能有变量,不然每次循环去读取的条件都会变,刚开始以为条件第一次就确定好了,不会因为循环体里面改变了就变
class MyStack {
public:
queue<int> q1;
queue<int> q1_copy;//q1队列的备份
MyStack() {
}
void push(int x) {
q1.push(x);
}
int pop() {
//相当于移除q1的队尾元素
int size=q1.size()-1;
// while(size--){
// q1_copy.push(q1.front());
// q1.pop();
// }
for(int i=0;i<size;i++){//这里不能用写成i<q1.size()-1,因为q1是变量
q1_copy.push(q1.front());//将队尾元素前的元素都保存到副本中
q1.pop();
}
int k=q1.front();
q1.pop();
int size2=q1_copy.size();
for(int i=0;i<size2;i++){
q1.push(q1_copy.front());
q1_copy.pop();
}
return k;
}
int top() {
return q1.back();
}
bool empty() {
return q1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
法二:用单队列实现
class MyStack {
public:
queue<int> q1;
MyStack() {
}
void push(int x) {
q1.push(x);
}
int pop() {
//将最后一个元素前的元素全移动到队尾
int size=q1.size()-1;
while(size--){
q1.push(q1.front());
q1.pop();
}
int result=q1.front();
q1.pop();
return result;
}
int top() {
return q1.back();
}
bool empty() {
return q1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
20. 有效的括号
注意:下面这种情况也是不合要求的。题目中说了左括号必须以正确的顺序闭合
c++:
class Solution {
public:
bool isValid(string s) {
stack<char> temp;
if(s.size()%2==1){
return false;
}
for(int i=0;i<s.size();i++){
//将左括号的对应右括号压栈
if(s[i]=='[') temp.push(']');
else if(s[i]=='{') temp.push('}');
else if(s[i]=='(') temp.push(')');
//左括号必须以正确的顺序闭合,如果不相等说明没有括号类型不匹配
//如果为空的话说明此时进来的是右括号,并且没有与之匹配的左括号,右括号多余
else if(temp.empty()||temp.top()!=s[i]) return false;//在使用pop前先判断是否为空
else temp.pop();//如果栈不为空并且第i个字符和栈顶字符相等,说明匹配上了,将栈顶元素弹出
}
return temp.empty();//循环完后如果不为空说明无效,左括号多余
}
};
二刷写法:个人认为这种思路更好理解,相当于把栈当作对对碰
class Solution {
public:
bool isValid(string s) {
stack<char> st;
for(int i=0;i<s.size();i++){
if(!st.empty()&&s[i]=='}'&&st.top()=='{') st.pop();
else if(!st.empty()&&s[i]==']'&&st.top()=='[') st.pop();
else if(!st.empty()&&s[i]==')'&&st.top()=='(') st.pop();
else st.push(s[i]);
}
return st.empty();
}
};
1047. 删除字符串中的所有相邻重复项
c++
class Solution {
public:
string removeDuplicates(string s) {
stack<char> temp;
for(int i=0;i<s.size();i++){
if(!temp.empty()&&s[i]==temp.top()){//如果不为空且出现重复项,则弹出元素
temp.pop();
}
else temp.push(s[i]);//为空或者没有出现重复项,将元素压栈
}
string result="";
int k=temp.size();
while(k--){
result+=temp.top();
temp.pop();
}
reverse(result.begin(),result.end());//反转
return result;
}
};
150. 逆波兰表达式求值
c++
C++ 字符串转换为数字(三:stoi,stoll,stof,stod)
class Solution {
public:
int evalRPN(vector<string>& tokens) {
//利用栈来计算
stack<int> result;
for(int i=0;i<tokens.size();i++){
if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/"){
int temp1=result.top();
result.pop();
int temp2=result.top();
result.pop();
if(tokens[i]=="+") result.push(temp1+temp2);
if(tokens[i]=="-") result.push(temp2-temp1);
if(tokens[i]=="*") result.push(temp1*temp2);
if(tokens[i]=="/") result.push(temp2/temp1);
}
else{
result.push(stoi(tokens[i]));//将字符串转换为整型
}
}
return result.top();
}
};
239. 滑动窗口最大值
c++
法一:采用嵌套循环,但是超出时间限制了,时间复杂度为O(k*n)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
for(int i=0;i<nums.size()-k+1;i++){//i为每个滑动窗口的起始位置
int max=nums[i];
for(int j=1;j<k;j++){
max=max>nums[i+j]?max:nums[i+j];
}
result.push_back(max);
}
return result;
}
};
法二:采用单调队列,队内单调递减,队头存放滑动窗口的最大值,单调队列的pop函数这样操作是因为当要弹出的值(弹出的值是前一个窗口的第一个值)不是最大值时,在前一个窗口中就会被最大值给干掉了,只有当第一个值是最大值时才会在前一个窗口中存活到最后,所以pop函数才要判断。
class Solution {
private:
//实现单调队列,队内单调递减
class myqueue{
public://类内默认为私有,所以记得加public
deque<int> que;
void pop(int value){
if(!que.empty()&&que.front()==value){//当要弹出的元素等于队头元素值时才弹出
que.pop_front();
}
}
void push(int value){
while(!que.empty()&&value>que.back()){
que.pop_back();
}
que.push_back(value);
}
int front(){
return que.front();//队头元素就是最大的
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
myqueue que;
vector<int> result;
//先处理第一个窗口
for(int i=0;i<k;i++){
que.push(nums[i]);
}
result.push_back(que.front());
//再处理后续窗口
for(int i=k;i<nums.size();i++){
que.pop(nums[i-k]);//先弹出窗口的第一个元素
que.push(nums[i]);//存放新窗口的下一个值
result.push_back(que.front());
}
return result;
}
};
347. 前 K 个高频元素
c++
用priority_queue(优先级队列)来实现,用小顶堆
参考资料:STL之优先级队列(priority_queue)
class Solution {
public:
class mycomprison{
public:
bool operator()(const pair<int,int>& left,const pair<int,int>& right){
return left.second>right.second;//按照second的大小进行升序排列,形成小顶堆
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> result;
map<int,int> temp;
for(int i=0;i<nums.size();i++){
temp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,mycomprison> pri_que;
for(map<int,int>::iterator it=temp.begin();it!=temp.end();it++){
pri_que.push(*it);//把元素和对应的频率值放进去
if(pri_que.size()>k){//始终保持优先级队列中只有k个元素
pri_que.pop();
}
}
for(int i=0;i<k;i++){
result.push_back(pri_que.top().first);//可以按 任意顺序 返回答案,first的值就是数组元素
pri_que.pop();
}
return result;
}
};
71. 简化路径
c++
踩坑:使用栈一个个字符进行操作,这样做特别容易出bug,这种写法不好考虑到其他组合的点
class Solution {
public:
string simplifyPath(string path) {
stack<char> result;
result.push('/');
for(int i=1;i<path.size();i++){
if(path[i]=='/' && path[i-1]=='/')
{
continue;//出现多个连续'/',跳过
}
else if(path[i]=='.' ){
if(path[i+1]=='.'&&i<path.size()-1){//两个点
//弹出1个'/',直到遇到第2个'/'时停止
int s=0;
while(s<2&&result.size()>1){
if(result.top()=='/')
{
s++;
}
if(s==2) break;
else result.pop();
}
i++;//加一跳出第二个点
}
else{//一个点
continue;
}
}
else if(result.top()=='/'&&path[i]=='/'){
continue;
}
else result.push(path[i]);
}
//最后一个目录名(如果存在)不能以 '/' 结尾。
if(result.top()=='/'&&result.size()>1) result.pop();
string s="";
int size=result.size();
while(size--){
s+=result.top();
result.pop();
}
reverse(s.begin(),s.end());
return s;
}
};
解法:使用栈只存放文件/目录名称,跳过所有的"/",当遇到“.“和”…"时再另外操作
注意if那里踩坑的地方,判断空是否在if条件里是有区别的
class Solution {
public:
string simplifyPath(string path) {
stack<string> result;//栈里面只存放文件/目录名称
// result.push('/');
for(int i=0;i<path.size();i++){
if(path[i]=='/'){
continue;
}
string temp="";
while(path[i]!='/'&&i<path.size()){
temp+=path[i++];//跳过所有的"/"
}
if(temp==".") continue;
// else if(temp==".."&&!result.empty()){//注意这里判断是否为空不能放在这个if括号里面,不然会出错,如果此时栈为空,但是又遇到了..,本来应该不做任何操作的,但是此时会将".."压栈
// result.pop();
// }
else if(temp==".."){
if(!result.empty())
result.pop();
}
else result.push(temp);
}
string s="";
stack<string> re_reverse;
int size=result.size();
while(size--){//先对栈的结果进行反转
re_reverse.push(result.top());
result.pop();
}
size=re_reverse.size();
if(size==0) s="/";//如果没有目录的话,记得加上"/"
while(size--){
s=s+"/"+re_reverse.top();
re_reverse.pop();
}
return s;
}
};
二叉树
144. 二叉树的前序遍历
c++
法一:使用递归法,当走到空节点时返回
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void tranverse(TreeNode* root,vector<int>& vec){
if(root==NULL) return;//root代表当前指针
vec.push_back(root->val);
tranverse(root->left,vec);
tranverse(root->right,vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
tranverse(root,result);
return result;
}
};
法二:使用迭代法
用栈来实现二叉树的前序遍历,递归也是每次调用就放入一个值,也可以通过栈来实现这种功能
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> temp;//这里放的是指针
if(root==NULL) return result;//二叉树为空直接返回
temp.push(root);//先把头结点放进去
while(!temp.empty()){
TreeNode* node=temp.top(); // 中,因为上一次循环最后放进的是左孩子节点,所以这次循环先取出的top就相当于先遍历左孩子
result.push_back(node->val);
temp.pop();
if(node->right) temp.push(node->right);//先放右边节点
if(node->left) temp.push(node->left);//左
}
return result;
}
};
145. 二叉树的后序遍历
c++
法一:使用递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void tranverse(TreeNode* root,vector<int>& vec){//这里一定要记得加引用
if(root==NULL) return;
tranverse(root->left,vec);
tranverse(root->right,vec);
vec.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
tranverse(root,result);
return result;
}
};
法二:使用迭代法
和前序遍历类似
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
//左右中
vector<int> result;
stack<TreeNode*> temp;
if(root==NULL) return result;
//处理顺序:中右左
temp.push(root);//中
while(!temp.empty()){
TreeNode* t=temp.top();
temp.pop();
result.push_back(t->val);
if(t->left) temp.push(t->left);//先放左边,和先序遍历相反
if(t->right) temp.push(t->right);
}
//进行反转
reverse(result.begin(),result.end());
return result;
}
};
94. 二叉树的中序遍历
c++
法一:使用递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void tranverse(TreeNode* root,vector<int>& vec){//这里一定要记得加引用
if(root==NULL) return;
tranverse(root->left,vec);
vec.push_back(root->val);
tranverse(root->right,vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
tranverse(root,result);
return result;
}
};
法二:使用迭代法
中序遍历就是要一直访问到最左边然后再返回去访问其他的,所以可以一直访问到最左下角的结点并且将这些节点压入栈中
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
//左中右
vector<int> result;
stack<TreeNode*> temp;
TreeNode* cur=root;
while(cur!=NULL||!temp.empty()){//当temp为空时也有可能还没访问结束
if(cur!=NULL){
temp.push(cur);
cur=cur->left;//指针一直访问左结点
}
else{
//为空时说明访问到了最底部
//此时开始弹出栈里面的元素
cur=temp.top();
result.push_back(cur->val);//左
temp.pop();
//指针开始指向右结点,若不为空,下次循环将压入栈中
cur=cur->right;//右
}
}
return result;
}
};
二叉树的统一迭代法
记住一定要先判断root是否为空,不然一开始的push(root)会报错
(感觉统一的更好记忆,可以记这种)
前序遍历和后序遍历的代码和中序遍历相比只改变了两行代码,主要思路就是在要处理的节点后面添加空节点
中序遍历
中序遍历是左中右,所以代码里面节点压栈的顺序就是右中左,另外两种也是这样处理
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
//左中右
vector<int> result;
stack<TreeNode*> temp;
TreeNode* cur=root;
if(root!=NULL) temp.push(root);
while(!temp.empty()){
cur=temp.top();
if(cur!=NULL){
temp.pop();//不为空说明该节点还需要再访问,先弹出之前压栈的该节点,重新处理
if(cur->right) temp.push(cur->right);//右节点
temp.push(cur);//把刚刚弹出的节点再压栈,保证中序遍历的顺序
temp.push(NULL);
if(cur->left) temp.push(cur->left);//左
}
else{
//遇到了空节点,说明需要将结果放入结果容器中
temp.pop();//先弹出空节点
cur=temp.top();//取出非空节点
temp.pop();
result.push_back(cur->val);
}
}
return result;
}
};
前序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
//中左右
vector<int> result;
stack<TreeNode*> temp;
TreeNode* cur=root;
if(root!=NULL) temp.push(root);
while(!temp.empty()){
cur=temp.top();
if(cur!=NULL){
temp.pop();//不为空说明该节点还需要再访问,先弹出之前压栈的该节点,重新处理
if(cur->right) temp.push(cur->right);//右节点
if(cur->left) temp.push(cur->left);//左
temp.push(cur);//把刚刚弹出的节点再压栈,保证中序遍历的顺序
temp.push(NULL);
}
else{
//遇到了空节点,说明需要将结果放入结果容器中
temp.pop();//先弹出空节点
cur=temp.top();//取出非空节点
temp.pop();
result.push_back(cur->val);
}
}
return result;
}
};
后序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
//左右中
vector<int> result;
stack<TreeNode*> temp;
TreeNode* cur=root;
if(root!=NULL) temp.push(root);
while(!temp.empty()){
cur=temp.top();
if(cur!=NULL){
temp.pop();//不为空说明该节点还需要再访问,先弹出之前压栈的该节点,重新处理
temp.push(cur);//把刚刚弹出的节点再压栈,保证中序遍历的顺序
temp.push(NULL);
if(cur->right) temp.push(cur->right);//右节点
if(cur->left) temp.push(cur->left);//左
}
else{
//遇到了空节点,说明需要将结果放入结果容器中
temp.pop();//先弹出空节点
cur=temp.top();//取出非空节点
temp.pop();
result.push_back(cur->val);
}
}
return result;
}
};
二叉树的层次遍历
使用队列实现层次遍历,每次循环队列里面只会放置同一层的节点
102.二叉树的层序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> temp;
if(root!=NULL) temp.push(root);
while(!temp.empty()){
int size=temp.size();
vector<int> vec;//while的每次循环都重新又定义了一个vec,所以for循环结束不需要清空
for(int i=0;i<size;i++){
//一层一层进行访问,处理节点顺序和访问顺序一样
TreeNode* node=temp.front();
temp.pop();
vec.push_back(node->val);
if(node->left) temp.push(node->left);
if(node->right) temp.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
107.二叉树的层次遍历 II
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
//从上往下遍历再进行反转
if(root!=NULL) que.push(root);
while(!que.empty()){
vector<int> vec;
int size=que.size();//每次循环队列里面只会放置同一层的节点
for(int i=0;i<size;i++){
TreeNode* node=que.front();
vec.push_back(node->val);
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
result.push_back(vec);
}
reverse(result.begin(),result.end());
return result;
}
};
199.二叉树的右视图
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
//不一定是右节点,也可能是左节点
vector<vector<int>> temp;//层序遍历所有的节点
vector<int> result;//放置temp中每一层的最后的节点
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
vec.push_back(node->val);
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
temp.push_back(vec);
}
for(vector<vector<int>>::iterator it=temp.begin();it<temp.end();it++){
vector<int> vec=*it;
result.push_back(vec.back());
}
return result;
}
};
637. 二叉树的层平均值
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<double> result;
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
//层次遍历
while(!que.empty()){
int size=que.size();
double sum=0;//注意这里不能定义成int,不然除法的结果就是int
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
sum+=node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
result.push_back(sum/size);
}
return result;
}
};
429. N 叉树的层序遍历
c++
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
//一个节点有多个孩子
vector<vector<int>> result;
queue<Node*> que;
if(root!=NULL) que.push(root);//放入头节点
//层次遍历法
while(!que.empty()){
vector<int> vec;
int size=que.size();
for(int i=0;i<size;i++){
Node* node=que.front();
que.pop();
vec.push_back(node->val);
vector<Node*> temp=node->children;
int sub_size=temp.size();
for(int j=0;j<sub_size;j++){//遍历孩子节点
que.push(temp[j]);//将下一层节点的值放入队列中
}
}
result.push_back(vec);
}
return result;
}
};
515. 在每个树行中找最大值
c++
也可以直接先定义max为INT_MIN(为 -2^31 , 即 2147483648 ),再进行对比
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
vector<int> result;
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
int max=0;
for(int i=0;i<size;i++){
TreeNode* node=que.front();//每一层的最左边的节点
if(i==0) max=node->val;//先对max赋初值(最左边节点的值)
que.pop();
if(max<node->val) max=node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
result.push_back(max);
}
return result;
}
};
116. 填充每个节点的下一个右侧节点指针
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;//存放层序遍历的节点
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
Node* node=que.front();
que.pop();
if(i<size-1) node->next=que.front();
else node->next=NULL;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
117. 填充每个节点的下一个右侧节点指针 II
c++
和116题的解法一样
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
Node* node=que.front();
que.pop();
if(i<size-1) node->next=que.front();
else node->next=NULL;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
104. 二叉树的最大深度
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
//使用层次遍历法
vector<vector<int>> result;
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
result.push_back(vec);
}
return result.size();
}
};
111. 二叉树的最小深度
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
//使用层次遍历法,当到某一层的某个节点的左右孩子节点都为空时返回结果
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
int min=0;
while(!que.empty()){
int size=que.size();
min++;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
if(node->left==NULL&&node->right==NULL) return min;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return min;
}
};
226.翻转二叉树
c++
法一:使用队列和层次遍历法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
if(node->left||node->right){//有一个孩子节点不为空
//交换左右节点,这里可以直接用swap
TreeNode* temp=node->left;
node->left=node->right;
node->right=temp;
}
//放入下一层的节点
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
法二:使用递归法
递归法代码很简单
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//递归法 前序遍历
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return root;//终止条件
swap(root->left,root->right);//交换左右孩子
invertTree(root->left);//反转左子树
invertTree(root->right);//反转右子树
return root;
}
};
法三:使用统一迭代法,使用栈
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//使用统一迭代法。前序遍历
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if(root!=NULL) st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){//说明左右子树至少存在一个
//入栈顺序:右左中
st.pop();//先弹出,避免重复操作
//按照前序遍历顺序压栈
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
st.push(node);
st.push(NULL);
}
else{
st.pop();//先弹出空指针
node=st.top();
st.pop();
swap(node->left,node->right);
}
}
return root;
}
};
101. 对称二叉树
c++
法一:使用层次遍历法,访问每一层,空节点用NULL表示,再判断每一层是否是对称的
这种方法算是迭代法?
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
//使用层次遍历法
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
int depth=0;
while(!que.empty()){
depth++;
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++){
if(que.front()==NULL){
vec.push_back(-101);//用节点val以外的值对空节点进行赋值
que.pop();
continue;
}
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
else que.push(NULL);//空节点也要存放进队列
if(node->right) que.push(node->right);
else que.push(NULL);
}
//判断每一层是否是对称的
for(int i=0;i<vec.size()/2;i++){
if(vec[i]!=vec[vec.size()-1-i]) return false;
}
}
return true;
}
};
法二:使用递归
一定要理清递归三部曲
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//使用递归法
bool compare(TreeNode* left,TreeNode* right){//确定递归函数的参数和返回值
//确定终止条件
if(left==NULL&&right!=NULL) return false;
else if(left!=NULL&&right==NULL) return false;
else if(left==NULL&&right==NULL) return true;
else if(left->val!=right->val) return false;//两个都不为空但是值不相等
// 两个都不为空且值相等
// 此时才做递归,做下一层的判断
bool outside=compare(left->left,right->right);//比较外侧
bool inside=compare(left->right,right->left);//比较内侧
return outside&&inside;
}
bool isSymmetric(TreeNode* root) {
if(root==NULL) return false;
return compare(root->left,root->right);
}
};
100. 相同的树
c++
法一:使用递归法,和101题类似,两颗树可以看成是一颗二叉树的左右子树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool compare(TreeNode* left,TreeNode* right){
if(left==NULL&&right!=NULL) return false;
else if(left!=NULL&&right==NULL) return false;
else if(left==NULL&&right==NULL) return true;
else if(left->val!=right->val) return false;//两个都不为空但是值不相等
// 两个都不为空且值相等
// 此时才做递归,做下一层的判断
bool outside=compare(left->left,right->left);//比较左侧
bool inside=compare(left->right,right->right);//比较右侧
return outside&&inside;
}
bool isSameTree(TreeNode* p, TreeNode* q) {
return compare(p,q);
}
};
法二:使用队列,和101题类似,两颗树可以看成是一颗二叉树的左右子树
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
queue<TreeNode*> que;
//先判断为空的情况
if(p==NULL||q==NULL){
if(p==NULL&&q==NULL) return true;
else return false;
}
//两个都不为空的情况
que.push(p);
que.push(q);
while(!que.empty()){
TreeNode* node_l=que.front();que.pop();
TreeNode* node_r=que.front();que.pop();
if(!node_l&&!node_r) continue;
if(!node_l||!node_r||(node_l->val!=node_r->val)) return false;
que.push(node_l->left);
que.push(node_r->left);
que.push(node_l->right);
que.push(node_r->right);
}
return true;
}
};
572. 另一棵树的子树
c++
法一:使用递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool compare(TreeNode* left,TreeNode* right){
if(left==NULL&&right!=NULL) return false;
else if(left!=NULL&&right==NULL) return false;
else if(left==NULL&&right==NULL) return true;
else if(left->val!=right->val) return false;//两个都不为空但是值不相等
// 两个都不为空且值相等
// 此时才做递归,做下一层的判断
bool outside=compare(left->left,right->left);//比较左侧
bool inside=compare(left->right,right->right);//比较右侧
return outside&&inside;
}
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
//使用层序遍历法一层层访问查看是否有节点值相同的
//一旦递归返回true就停止
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
TreeNode* node=que.front();
int size=que.size();
que.pop();
for(int i=0;i<size;i++){
if(node->val==subRoot->val){
bool result=compare(node,subRoot);
if(result==true) return true;
}
}
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
return false;
}
};
559. N 叉树的最大深度
c++
法一:使用层次遍历法
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
int maxDepth(Node* root) {
//使用层次遍历法
queue<Node*> que;
if(root!=NULL) que.push(root);
int depth=0;
while(!que.empty()){
depth++;
int size=que.size();
for(int i=0;i<size;i++){//遍历每一层的节点
Node* n=que.front();
que.pop();
vector<Node*> temp=n->children;
for(int j=0;j<temp.size();j++){
que.push(temp[j]);//遍历该层节点的孩子节点
}
}
}
return depth;
}
};
222.完全二叉树的节点个数
c++
法一:层次遍历法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int countNodes(TreeNode* root) {
//层次遍历法
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
vector<int> vec;
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return vec.size();
}
};
法二:递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int countNodes(TreeNode* root) {
//递归
if(root==NULL) return 0;//确定终止条件
//确定单层递归
int l=countNodes(root->left);
int r=countNodes(root->right);
return l+r+1;//再加上中间节点
}
};
110. 平衡二叉树
c++
法一:使用层次遍历法遍历所有节点,并且用递归法求出各个节点的树的高度,再做判断
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int height_tree(TreeNode* node){
//递归法
if(node==NULL) return 0;
int l=height_tree(node->left);//左子树高度
int r=height_tree(node->right);//右子树高度
int height=l>r?l:r;
return 1+height;//记得加上节点的高度
}
bool isBalanced(TreeNode* root) {
//层次遍历法遍历所有节点
if(root==NULL) return true;//这里有坑,如果为空也是平衡二叉树
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
int l=height_tree(node->left);//左子树高度
int r=height_tree(node->right);//右子树高度
int max=l>r?l:r;
int min=l>r?r:l;
if(max-min>1) return false;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return true;
}
};
法二:使用层次遍历求某个节点的高度,使用统一迭代法遍历各个节点判断是否符合要求
前中后序遍历都可以通过
class Solution {
public:
int getHeight(TreeNode* cur){
if(cur==NULL) return 0;
queue<TreeNode*> que;
que.push(cur);
int height=0;
while(!que.empty()){
int size=que.size();
height++;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return height;
}
bool isBalanced(TreeNode* root) {
if(root==NULL) return true;
stack<TreeNode*> st;
if(root!=NULL) st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){
st.pop();
st.push(node);
st.push(NULL);
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
else{
st.pop();
TreeNode* temp=st.top();
st.pop();
int height_l=getHeight(temp->left);
int height_r=getHeight(temp->right);
if(abs(height_l-height_r)>1) return false;
}
}
return true;
}
};
法三:直接用递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int height_tree(TreeNode* root){
//递归法求出左右子树的高度差
if(root==NULL) return 0;//确定终止条件
//确定单层递归
//如果=-1,说明已经不是平衡二叉树了
int l=height_tree(root->left);//左子树高度
if(l==-1) return -1;
int r=height_tree(root->right);//右子树高度
if(r==-1) return -1;
return abs(l-r)>1?-1:1+max(l,r);//判断左右子树高度差,如果不平衡了则返回-1做标记
}
bool isBalanced(TreeNode* root) {
return height_tree(root)==-1?false:true;
}
};
257. 二叉树的所有路径
c++
法一:递归+回溯
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void path_tree(TreeNode* cur,vector<int>& path,vector<string>& result){
path.push_back(cur->val);//先将当前访问的节点放入路径中,相当于中节点
if(cur->left==NULL&&cur->right==NULL){//确定终止条件
//到了叶子节点
string sub_path="";
for(int i=0;i<path.size()-1;i++){
sub_path+=to_string(path[i]);//这里需要把int转成string
sub_path+="->";
}
sub_path+=to_string(path[path.size()-1]);
result.push_back(sub_path);//记录下一条路径
}
//确定单层递归
if(cur->left){
path_tree(cur->left,path,result);//左子树开始递归
path.pop_back();//回溯
}
if(cur->right){
path_tree(cur->right,path,result);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
path_tree(root,path,result);
return result;
}
};
法二:使用迭代法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
//迭代法 前序遍历
stack<string> path;//单个路径
stack<TreeNode*> st;//前序遍历节点
vector<string> result;
if(root!=NULL) {
st.push(root);
path.push(to_string(root->val));
}
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
string sub_path=path.top();
path.pop();//弹出要处理的路径,避免重复处理
if(node->left==NULL&&node->right==NULL){//遇到叶子节点
result.push_back(sub_path);//直接将取出来的结果放入容器中
}
if(node->right){
st.push(node->right);
path.push(sub_path+"->"+to_string(node->right->val));//记录访问的节点顺序
}
if(node->left){
st.push(node->left);
path.push(sub_path+"->"+to_string(node->left->val));//记录访问的节点顺序
}
}
return result;
}
};
404. 左叶子之和
c++
法一:递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if(root==NULL) return 0;//确定终止条件
int sum=0;
if(root->left!=NULL&&root->left->left==NULL&&root->left->right==NULL){
sum=root->left->val;//这里不需要return,右子树还没判断
}
int l=sumOfLeftLeaves(root->left);
int r=sumOfLeftLeaves(root->right);
return sum+l+r;
}
};
写法二:
class Solution {
public:
int sum=0;
void tranversal(TreeNode* cur){
if(cur->left!=NULL&&cur->left->left==NULL&&cur->left->right==NULL){
sum+=cur->left->val;
}
if(cur->left) tranversal(cur->left);
if(cur->right) tranversal(cur->right);
return;
}
int sumOfLeftLeaves(TreeNode* root) {
tranversal(root);
return sum;
}
};
法二:迭代法
使用前序遍历法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
//使用迭代法
if(root==NULL) return 0;
stack<TreeNode*> st;
st.push(root);
int sum=0;
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
if(node->left!=NULL&&node->left->left==NULL&&node->left->right==NULL)
{
sum+=node->left->val;
}
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return sum;
}
};
使用统一迭代法:
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
//迭代法
stack<TreeNode*> st;
int sum=0;
if(root!=NULL) st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
if(node!=NULL){
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
st.push(node);
st.push(NULL);
}
else{
node=st.top();
st.pop();
if(node->left!=NULL&&node->left->left==NULL&&node->left->right==NULL){
sum+=node->left->val;
}
}
}
return sum;
}
};
513.找树左下角的值
c++
法一:使用层次遍历法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
//使用层次遍历
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
int result;//记录每一层最左边的值
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
if(i==0) result=node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return result;
}
};
法二:使用递归+回溯
递归时先查左子树,这样再判断result的时候只会记录最左边的节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int max_depth=-1;
int result;
void find_leftvalue(TreeNode* root,int depth){
if(root->left==NULL&&root->right==NULL){
if(depth>max_depth){
result=root->val;
max_depth=depth;
}
else return;
}
if(root->left){
depth++;
find_leftvalue(root->left,depth);
depth--;//记得回溯返回上一层
}
if(root->right){
depth++;
find_leftvalue(root->right,depth);
depth--;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
//使用递归
find_leftvalue(root,0);
return result;
}
};
112. 路径总和
c++
法一:使用迭代法
踩坑记录:刚开始只是定义了一个int型的sum去记录访问过的节点值之和,但是这样是不对的,因为先序遍历时是中左右,但是路径是不可能访问了左孩子又访问右孩子的
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
stack<pair<TreeNode*,int>> st;
if(root!=NULL) st.push(pair<TreeNode*, int>(root, root->val));
while(!st.empty()){
pair<TreeNode*,int> node=st.top();
st.pop();
if(node.first->left==NULL&&node.first->right==NULL&&node.second==targetSum){
return true;
}
if(node.first->right){
//第二个值记录经历过的路径的节点值的和
st.push(pair<TreeNode*,int>(node.first->right,node.second+node.first->right->val));
}
if(node.first->left){
st.push(pair<TreeNode*,int>(node.first->left,node.second+node.first->left->val));
}
}
return false;
}
};
法二:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool compare(TreeNode* root, int targetSum) {
//递归法
if(root->left==NULL&&root->right==NULL&&targetSum==0) //遇到叶子节点且路径总和符合要求
return true;
if(root->left==NULL&&root->right==NULL)
return false;
if(root->left){//因为当遇到叶子节点时就会停止,所以这里需要判断去掉空节点
if(compare(root->left,targetSum-root->left->val)) return true;
}
if(root->right){
if(compare(root->right,targetSum-root->right->val)) return true;
}
return false;
}
bool hasPathSum(TreeNode* root,int targetSum){
if(root==NULL) return false;
return compare(root,targetSum-root->val);
}
};
113. 路径总和 II
这种题目其实用迭代法会复杂一些,能掌握递归方式就够了!
c++
法一:使用递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> result;
vector<int> vec;//存放单条路径
void look_tree(TreeNode* root,int targetSum){
if(root->left==NULL&&root->right==NULL&&targetSum==0){//目标叶子节点
result.push_back(vec);
return;
}
if(root->left){
vec.push_back(root->left->val);
look_tree(root->left,targetSum-root->left->val);//变量里面已经包含回溯了,targetSum的值并没有变
vec.pop_back();//回溯
}
if(root->right){
vec.push_back(root->right->val);
look_tree(root->right,targetSum-root->right->val);
vec.pop_back();//回溯
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
if(root==NULL) return result;
vec.push_back(root->val);//不为空先放入根节点
look_tree(root,targetSum-root->val);
return result;
}
};
106. 从中序与后序遍历序列构造二叉树
c++
法一:使用递归法
根节点为后序遍历最后一个值
根据中序遍历可找出左右子树的节点
再根据后序遍历又可以找出左右子树的根节点
踩坑记录:在用迭代器的时候使用了it+1,这样报错了,使用it++就不会报错
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void build_tree(TreeNode* node,vector<int>& inorder, vector<int>& postorder){
//使用递归
if(inorder.size()==1){//遇到了叶子节点
return;
}
//单层递归
//找到当前节点的左子树
vector<int> left_inorder;//左子树的中序遍历
vector<int>::iterator it=find(inorder.begin(),inorder.end(),node->val);
for(vector<int>::iterator i=inorder.begin();i!=it;i++){
left_inorder.push_back(*i);
}
vector<int> left_postorder;//左子树的后序遍历
for(int i=0;i<left_inorder.size();i++){
left_postorder.push_back(postorder[i]);
}
if(left_inorder.size()!=0){
TreeNode* temp=new TreeNode(left_postorder.back());
node->left=temp;
build_tree(temp,left_inorder,left_postorder);
}
//找到当前节点的右子树
vector<int> right_inorder;//右子树的中序遍历
it++;//注意这里不能写+1
for(it;it!=inorder.end();it++){
right_inorder.push_back(*it);
}
vector<int> right_postorder;//右子树的后序遍历
for(int i=left_inorder.size();i<left_inorder.size()+right_inorder.size();i++){
right_postorder.push_back(postorder[i]);
}
if(right_inorder.size()!=0){
TreeNode* temp=new TreeNode(right_postorder.back());
node->right=temp;
build_tree(temp,right_inorder,right_postorder);
}
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
//根节点为后序遍历最后一个值
//根据中序遍历可找出左右子树的节点
//再根据后序遍历又可以找出左右子树的根节点,
if(inorder.size()==0||postorder.size()==0) return NULL;
int val=postorder.back();
TreeNode* root=new TreeNode(val);//根节点
build_tree(root,inorder,postorder);
return root;
}
};
105. 从前序与中序遍历序列构造二叉树
c++
法一:递归
踩坑记录:刚开始觉得递归第一步就判断了size==1时就结束递归,所以递归函数的最后面就没加容器的size是否大于0的判断,就报错了,这样做是不对的,第一步的递归结束条件是判断访问到的单个节点是否为叶子节点,即使此时这个节点不是叶子节点,它的左右子树也有可能有一个是空的,所以必须要加判断
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void build_tree(TreeNode* node,vector<int>& preorder, vector<int>& inorder){
if(preorder.size()==1) return;
vector<int> left_pre;//左子树的前序遍历
vector<int> left_in;//左子树的中序遍历
vector<int> right_pre;//右子树的前序遍历
vector<int> right_in;//右子树的中序遍历
vector<int>::iterator it=find(inorder.begin(),inorder.end(),node->val);//找到根节点在中序遍历的位置
for(vector<int>::iterator i=inorder.begin();i!=it;i++){
left_in.push_back(*i);
}
for(++it;it!=inorder.end();it++){
right_in.push_back(*it);
}
for(int i=1;i<left_in.size()+1;i++){
left_pre.push_back(preorder[i]);
}
for(int i=left_in.size()+1;i<left_in.size()+1+right_in.size();i++){
right_pre.push_back(preorder[i]);
}
if(left_pre.size()>0){//这里必须要加判断,不然会不通过,比如只有根节点和左孩子节点,此时的右子树的两个容器就是0
TreeNode* l=new TreeNode(left_pre[0]);//前序遍历的第一个值是根节点
node->left=l;
build_tree(l,left_pre,left_in);
}
if(right_pre.size()>0){
TreeNode* r=new TreeNode(right_pre[0]);//前序遍历的第一个值是根节点
node->right=r;
build_tree(r,right_pre,right_in);
}
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size()==0) return NULL;
TreeNode* root=new TreeNode(preorder[0]);
build_tree(root,preorder,inorder);
return root;
}
};
二刷写法:更好理解。有返回值
class Solution {
public:
TreeNode* build_tree(vector<int>& preorder, vector<int>& inorder){
if(preorder.size()==0) return NULL;
TreeNode* root=new TreeNode(preorder[0]);
vector<int> left_pre;//左子树的前序遍历
vector<int> left_in;//左子树的中序遍历
vector<int> right_pre;//右子树的前序遍历
vector<int> right_in;//右子树的中序遍历
vector<int>::iterator it=find(inorder.begin(),inorder.end(),preorder[0]);//找到根节点在中序遍历的位置
for(vector<int>::iterator i=inorder.begin();i!=it;i++){
left_in.push_back(*i);
}
for(++it;it!=inorder.end();it++){
right_in.push_back(*it);
}
for(int i=1;i<left_in.size()+1;i++){
left_pre.push_back(preorder[i]);
}
for(int i=left_in.size()+1;i<left_in.size()+1+right_in.size();i++){
right_pre.push_back(preorder[i]);
}
root->left=build_tree(left_pre,left_in);
root->right=build_tree(right_pre,right_in);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size()==0) return NULL;
return build_tree(preorder,inorder);
}
};
654. 最大二叉树
c++
法一:递归法
递归里面不写结束条件也通过了,因为会自动结束
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void build_tree(TreeNode* node,vector<int>& nums){
//这句不加也可以通过,因为最后有对容器大小判断,所以大小一定是大于0的
// if(nums.size()==0) return;//这里必须是0才停止,因为当size为1时还需要对val进行赋值
//确定单层递归逻辑
vector<int> left;
vector<int> right;
//找到容器中最大值的下标
int max=0;
int max_val=INT_MIN;
for(int i=0;i<nums.size();i++){
if(nums[i]>max_val){
max=i;
max_val=nums[i];
}
}
node->val=max_val;
int left_max=INT_MIN;
for(int i=0;i<max;i++){
// if(nums[i]>left_max) left_max=nums[i];
left.push_back(nums[i]);
}
int right_max=INT_MIN;
for(int i=max+1;i<nums.size();i++){
// if(nums[i]>right_max) right_max=nums[i];
right.push_back(nums[i]);
}
if(left.size()>0){//这里也必须有判断,不能等于0
TreeNode* l=new TreeNode(0);
node->left=l;
build_tree(l,left);
}
if(right.size()>0){
TreeNode* r=new TreeNode(0);
node->right=r;
build_tree(r,right);
}
// return;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if(nums.size()==0) return NULL;
TreeNode* root=new TreeNode(0);
build_tree(root,nums);
return root;
}
};
617. 合并二叉树
c++
法一:迭代
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
//使用迭代法
if(root1==NULL) return root2;
if(root2==NULL) return root1;
queue<TreeNode*> que;
//此时两棵树都不为空
que.push(root1);
que.push(root2);
root1->val+=root2->val;//在root1上进行根节点的操作
while(!que.empty()){
//每次取出两个节点进行操作
TreeNode* node1=que.front();
que.pop();
TreeNode* node2=que.front();
que.pop();
//只有两个节点都不为空时才进行压入队列操作
//两个左节点都不为空
if(node1->left!=NULL&&node2->left!=NULL){
node1->left->val+=node2->left->val;
que.push(node1->left);
que.push(node2->left);
}
//两个右节点都不为空
if(node1->right!=NULL&&node2->right!=NULL){
node1->right->val+=node2->right->val;
que.push(node1->right);
que.push(node2->right);
}
//其中有一个为空的情况
if(node1->left==NULL&&node2->left!=NULL){//root1不为空,root为空的情况就不需要再做任何操作了
node1->left=node2->left;//直接把root2的左节点全部拿过来就行,后面就不需要再操作了
}
if(node1->right==NULL&&node2->right!=NULL){
node1->right=node2->right;
}
}
return root1;
}
};
法二:递归
class Solution {
public:
TreeNode* rebuilt(TreeNode* root1, TreeNode* root2){
if(root1==NULL&&root2==NULL) return NULL;
TreeNode* node=new TreeNode(0);
if(root1==NULL&&root2!=NULL){
return root2;
}
else if(root1!=NULL&&root2==NULL) return root1;
else{
node->val=root1->val+root2->val;
node->left=rebuilt(root1->left,root2->left);
node->right=rebuilt(root1->right,root2->right);
}
return node;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return rebuilt(root1,root2);
}
};
700.二叉搜索树中的搜索
c++
法一:迭代法,利用二叉搜索树的特性
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
//使用迭代法
if(root==NULL) return NULL;
while(root!=NULL){
if(root->val>val) root=root->left;
else if(root->val<val) root=root->right;
else return root;
}
return NULL;
}
};
法二:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
//使用递归
if(root==NULL) return NULL;
if(root->val==val) return root;
if(root->val<val) return searchBST(root->right,val);
else if(root->val>val) return searchBST(root->left,val);
return NULL;
}
};
98. 验证二叉搜索树
c++
法一:迭代法,使用中序遍历形成一个递增的数组
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
//使用中序遍历形成一个递增的数组
stack<TreeNode*> st;
if(root!=NULL) st.push(root);
vector<int> result;
while(!st.empty()){
TreeNode* node=st.top();//中
if(node!=NULL){
st.pop();
if(node->right) st.push(node->right);
st.push(node);
st.push(NULL);
if(node->left) st.push(node->left);
}
else{
st.pop();
node=st.top();
st.pop();
result.push_back(node->val);
}
}
int size=result.size();
for(int i=0;i<size-1;i++){
if(result[i]>=result[i+1]) return false;//这里很容易漏掉等号
}
return true;
}
};
法二:使用递归存放中序遍历的节点值
踩坑记录:刚开始写了个bool is_true(TreeNode* root)的递归函数去查看每个节点的左右节点是否都符合要求,但是右边节点可能大于当前节点但是小于右子树的某个节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> result;//存放中序遍历的节点值
void inorder(TreeNode* root,vector<int>& result){
//递归实现中序遍历
// if(root==NULL) return;//不加这句也能通过
if(root->left) inorder(root->left,result);
result.push_back(root->val);
if(root->right) inorder(root->right,result);
}
bool isValidBST(TreeNode* root) {
inorder(root,result);
int size=result.size();
for(int i=0;i<size-1;i++){
if(result[i]>=result[i+1]) return false;//这里很容易漏掉等号
}
return true;
}
};
530. 二叉搜索树的最小绝对差
c++
法一:使用迭代法,将中序遍历的结果放入数组中,接着再判断最值
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
//中序遍历迭代法
stack<TreeNode*> st;
if(root!=NULL) st.push(root);
vector<int> result;
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){
st.pop();
if(node->right) st.push(node->right);
st.push(node);
st.push(NULL);
if(node->left) st.push(node->left);
}
else{
st.pop();
node=st.top();
st.pop();
result.push_back(node->val);
}
}
int min=INT_MAX;
for(int i=0;i<result.size()-1;i++){
if(abs(result[i]-result[i+1])<min){
min=abs(result[i]-result[i+1]);
}
}
return min;
}
};
法二:递归实现中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> result;
void inorder(TreeNode* root,vector<int>& result){
//递归实现中序遍历
if(root==NULL) return;
if(root->left) inorder(root->left,result);
result.push_back(root->val);
if(root->right) inorder(root->right,result);
}
int getMinimumDifference(TreeNode* root) {
inorder(root,result);
int min=INT_MAX;
for(int i=0;i<result.size()-1;i++){
if(abs(result[i]-result[i+1])<min){
min=abs(result[i]-result[i+1]);
}
}
return min;
}
};
501. 二叉搜索树中的众数
c++
法一:迭代法实现中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> findMode(TreeNode* root) {
vector<int> result;
map<int,int> temp;//第一个int放节点值,第二个放频率值
stack<TreeNode*> st;
if(root!=NULL) st.push(root);
//中序遍历
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){
st.pop();
if(node->right) st.push(node->right);
st.push(node);
st.push(NULL);
if(node->left) st.push(node->left);
}
else{
st.pop();
node=st.top();
st.pop();
temp[node->val]++;
}
}
int f=0;//找出频率最高的次数
for (map<int, int>::iterator it = temp.begin(); it != temp.end(); it++){
if(it->second>f) f=it->second;
}
for (map<int, int>::iterator it = temp.begin(); it != temp.end(); it++){
if(it->second==f) result.push_back(it->first);
}
return result;
}
};
236. 二叉树的最近公共祖先
c++
法一:使用递归+回溯,后序遍历
如果只找到了一个目标节点,就只返回那个节点,如果两个都找到了就返回root,如果两个都没找到就返回NULL
可以这样想:每一个节点的左右节点都会给它返回一个节点,只有当返回的两个都不为空时才说明找到了最近公共祖先
下面的代码的写法已经包含了节点本身p(q)拥有一个子孙节点q§的情况了,因为直接就返回节点本身了
/**
* 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:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==p||root==q||root==NULL) return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(left!=NULL&&right!=NULL) return root;
if(left==NULL&&right!=NULL) return right;
else if(left!=NULL&&right==NULL) return left;
else{
return NULL;
}
}
};
235. 二叉搜索树的最近公共祖先
c++
法一:递归。直接用236中普通二叉树的递归代码也可以通过
利用二叉搜索树的特性,当从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。
/**
* 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:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
int max=p->val>q->val?p->val:q->val;
int mi=min(p->val,q->val);
if(root->val<max&&root->val>mi) return root;//符合条件的节点,直接返回
TreeNode* result;
if(root->val<mi) result= lowestCommonAncestor(root->right,p,q);//小于最小值,说明在右子树
if(root->val>max) result= lowestCommonAncestor(root->left,p,q);
return result;
}
};
法二:迭代法
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root) {
if (root->val > p->val && root->val > q->val) {
root = root->left;
} else if (root->val < p->val && root->val < q->val) {
root = root->right;
} else return root;
}
return NULL;
}
};
701. 二叉搜索树中的插入操作
c++
法一:递归,根据二叉搜索树节点值大小关系一直搜索到空节点就插入
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* pre=NULL;//记录前一个节点
TreeNode* insert(TreeNode* cur,int val){
//一直搜索到空节点插入即可
if(cur==NULL) {
TreeNode* temp=new TreeNode(val);
return temp;
}
if(cur->val>val) {
TreeNode* node= insert(cur->left,val);
if(node!=NULL) cur->left=node;
}
if(cur->val<val) {
TreeNode* node= insert(cur->right,val);
if(node!=NULL) cur->right=node;
}
return NULL;//没有找到就返回空节点
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL) {//必须加这个判断
TreeNode* temp=new TreeNode(val);
return temp;
}
TreeNode*node= insert(root,val);
return root;
}
};
写法二:递归函数返回值为空
class Solution {
public:
TreeNode* pre=NULL;//记录前一个节点
void trans(TreeNode* root,int val){
if(root==NULL){
TreeNode* temp=new TreeNode(val);
if(pre->val>val) pre->left=temp;
else pre->right=temp;
return;
}
pre=root;
if(root->val<val) trans(root->right,val);
else trans(root->left,val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL) return new TreeNode(val);
trans(root,val);
return root;
}
};
450. 删除二叉搜索树中的节点
c++
法一:使用递归,每次递归返回一个节点,如果没找到要删除的节点,就返回当前这个节点,如果找到了就对这个删除节点按照以下四种情况进行分析
找到删除的节点
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
代码:
class Solution {
public:
TreeNode* dele(TreeNode* root, int key){
if(root==NULL) return NULL;//找到空节点直接返回
if(root->val==key) {
if(root->left==NULL&&root->right==NULL) {
delete root;
return NULL;
}
else if(root->left==NULL) {
TreeNode* temp=root->right;
delete root;
return temp;
}
else if(root->right==NULL) {
TreeNode* temp=root->left;
delete root;
return temp;
}
else{
TreeNode* cur=root->right;
while(cur->left!=NULL){
cur=cur->left;//右子树的最左边的节点
}
cur->left=root->left;
TreeNode* temp=root;
root=root->right;
delete temp;//释放节点
return root;
}
}
if(root->val<key) root->right=dele(root->right,key);
else root->left=dele(root->left,key);
return root;//如果没找到删除节点就返回当前节点
}
TreeNode* deleteNode(TreeNode* root, int key) {
root=dele(root,key);
return root;
}
};
669. 修剪二叉搜索树
c++
法一:递归法
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root==NULL) return NULL;
//如果不在区间内,就一直搜索
//如果搜索到了区间内的节点就返回节点,如果没搜索到就一直搜索直到空节点
if(root->val<low){
TreeNode* right=trimBST(root->right,low,high);
return right;
}
if(root->val>high){
TreeNode* left=trimBST(root->left,low,high);
return left;
}
//在区间内的节点,返回当前符合要求的节点
root->left=trimBST(root->left,low,high);
root->right=trimBST(root->right,low,high);
return root;
}
};
108.将有序数组转换为二叉搜索树
c++
法一:递归。切割数组,以中间的值作为中节点,中间值左边为左子树的节点,右边的数组为右子树的节点,通过递归进行构建
优化:直接传入数组的两个下标来确定范围,可以节省空间
class Solution {
public:
TreeNode* build_tree(vector<int>& nums){
if(nums.size()==0) return NULL;
vector<int> left_nums;
vector<int> right_nums;
for(int i=0;i<nums.size()/2;i++){
left_nums.push_back(nums[i]);//vector用push_back进行插入,刚开始用[i]进行赋值,一直出错
}
int k=nums.size()/2;
for(int i=k+1;i<nums.size();i++){
right_nums.push_back(nums[i]);
}
TreeNode* mid=new TreeNode(nums[k]);
mid->left=build_tree(left_nums);//左节点
mid->right=build_tree(right_nums);//右节点
return mid;//返回中间的节点
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
//选取数组中间节点作为根节点
if(nums.size()==0) return NULL;
TreeNode* root=new TreeNode(0);选取数组中间节点作为根节点
root= build_tree(nums);
return root;
}
};
538.把二叉搜索树转换为累加树
c++
法一:递归,按照右中左的顺序进行遍历
class Solution {
public:
int pre=0;//前一个节点处理后的值
void tree_add(TreeNode* root){
//右中左遍历节点
if(root==NULL) return;
if(root->right){//访问右节点
tree_add(root->right);
}
root->val+=pre;//先处理当前节点的值
pre=root->val;//再更新pre
if(root->left) tree_add(root->left);
}
TreeNode* convertBST(TreeNode* root) {
tree_add(root);
return root;
}
};