文章目录
- 二分法
- BM21 旋转数组的最小数字
- BM22 比较版本号
- 力扣-旋转数组的查找
- 力扣-两个非空链表逆序相加
- 二叉树
- BM23 二叉树的前序遍历
- BM24 二叉树的中序遍历
- BM25 二叉树的后序遍历
- BM26 求二叉树的层序遍历
- BM27 按之字形顺序打印二叉树
- BM28 二叉树的最大深度
- BM29 二叉树中和为某一值的路径(一)
- BM30 二叉搜索树与双向链表
二分法
BM21 旋转数组的最小数字
#include <climits>
class Solution {
public:
int minNumberInRotateArray(vector<int>& nums) {
if(nums.size() <= 0) return 0;
int left=0, right=nums.size()-1;
while(left <= right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[right])//右边是降序 最小的数字在mid右边
left = mid+1;
else if(nums[mid] < nums[right])//右边是升序 最小数字要么是mid(奇数个数字) 要么在mid左边(偶数个)
right = mid;
else right--;//无法判断,一个一个试
}
return nums[left];
}
};
BM22 比较版本号
看解答写的,两个指针依次遍历两个字符串,分为字符处理与比较两部分:
首先把小数点之前的字符转化为数字,跳过当前小数点,处理两个字符串;
然后得到的数字。
class Solution {
public:
int compare(string version1, string version2) {
int res = 0;
int v1 = 0, v2 = 0;
long long num1 = 0, num2 = 0;
while(v1<version1.size() || v2<version2.size())
{
num1 = 0;
//截取最近一个小数点之前的数字
while(v1 < version1.size() && version1[v1] != '.')
{
num1 = num1 * 10 + version1[v1] - '0';
v1++;
}
//跳过该小数点
v1++;
//相同的处理
num2 = 0;
while(v2 < version2.size() && version2[v2] != '.')
{
num2 = num2 * 10 + version2[v2] - '0';
v2++;
}
v2++;
//比较数字
if(num1 > num2) return 1;
if(num1 < num2) return -1;
}
return 0;
}
};
一开始自己想的就是这种办法,使用流输入istringstream来分割:
- 使用字符串流输入,按照点将两个原始字符串分割,使每个修订号的数字单独呈现在数组中;
- 遍历数组,每次各自取出一个数字比较,较短的版本号没有可取的数字了,就直接取0;
- 遍历取出的数字字符串,将其转换成数字,比较数字大小,根据大小关系返回值。如果全部比较完都无法比较出大小关系,则返回0。
这种解法要学习的一个是流输入的使用,一个是较短版本号的处理
#include <sstream>
class Solution {
public:
int compare(string version1, string version2) {
//使用流输入istringstream
vector<string> num1;
vector<string> num2;
istringstream ss1(version1);
istringstream ss2(version2);
string temp;
//流输入分割
while(getline(ss1, temp, '.'))
num1.push_back(temp);
while(getline(ss2, temp,'.'))
num2.push_back(temp);
//字符串转数字 比较
for(int i=0; i<num1.size() || i<num2.size(); i++)
{
//较短的版本号取0 这个处理很好
string s1 = i < num1.size() ? num1[i] : "0";
string s2 = i < num2.size() ? num2[i] : "0";
long long num1 = 0, num2 = 0;
for(int j = 0; j<s1.size(); j++)
num1 = num1 * 10 + s1[j] - '0';
for(int j=0; j<s2.size(); j++)
num2 = num2 * 10 + s2[j] - '0';
//比较数字
if(num1 > num2) return 1;
if(num1 < num2) return -1;
}
return 0;
}
};
力扣-旋转数组的查找
二分里边再次二分
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
int mid = 0, index = 0;
while(left <= right)
{
mid = left + (right - left) / 2;
if(nums[mid] == target) return mid;
//先看mid 在左边还是右边 先根据 nums[mid] 与 nums[lo] 的关系判断 mid 是在左段还是右段
if(nums[left] <= nums[mid])//mid在左边
{
//再看target在mid的左边还是右边 再判断 target 是在 mid 的左边还是右边,从而调整左右边界 lo 和 hi
if(target >= nums[left] && target < nums[mid])//在左边 后半段不能取等号
{
right = mid-1;
}
else left = mid+1;
}
else//nums[left] > nums[mid]
{
if(target > nums[mid] && target <= nums[right])//在mid右边
left = mid+1;
else right = mid-1;
}
}
return -1;
}
};
力扣-两个非空链表逆序相加
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
//if(l1->val==0 && l2->val==0) return l1;
ListNode* dummyhead = new ListNode(0);
ListNode* cur = dummyhead;
int flag = 0;
int val1 = 0, val2 = 0, sum = 0;
while(l1!=nullptr || l2!=nullptr)
{
//数字处理部分
val1 = (l1 == nullptr) ? 0 : l1->val;
val2 = (l2 == nullptr) ? 0 : l2->val;
sum = val1 + val2 + flag;
flag = sum / 10;//进位不加判断 直接取整数 大于等于10取1,否则取0
sum = sum % 10;//取余数
//cout << "l1:" << val1 << " l2:" << val2 << endl;
//cout << "sum:" << sum << " flag:" << flag << endl;
cur->next = new ListNode(sum);//指向新结点,连接链表
//节点更新
cur = cur->next;
if(l1 != nullptr) l1 = l1->next;
if(l2 != nullptr) l2 = l2->next;
}
//如果还有进位的话要加上 最高位 如测试用例3
if(flag == 1) cur->next = new ListNode(flag);
return dummyhead->next;
}
};
二叉树
BM23 二叉树的前序遍历
描述:给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
- 递归法很简单
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
#include <vector>
class Solution {
public:
vector<int> res;
vector<int> preorderTraversal(TreeNode* root) {
res.clear();
if(root == nullptr) return res;
//递归
dfs(root);
return res;
}
void dfs(TreeNode* root)
{
if(root == nullptr)
return;
res.push_back(root->val);
dfs(root->left);
dfs(root->right);
}
};
- 迭代法,用辅助栈,入栈顺序是右左中
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
#include <vector>
class Solution {
public:
vector<int> res;
vector<int> preorderTraversal(TreeNode* root) {
res.clear();
if(root == nullptr) return res;
//迭代
stack<TreeNode*> st;
st.push(root);
TreeNode* temp;
while(!st.empty())
{
//访问栈顶
temp = st.top();
st.pop();
res.push_back(temp->val);
//前序 中左右 栈 先进后出 入栈 右左中
if(temp->right) st.push(temp->right);
if(temp->left) st.push(temp->left);
}
return res;
}
};
- 统一迭代法
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
#include <vector>
class Solution {
public:
vector<int> res;
vector<int> preorderTraversal(TreeNode* root) {
res.clear();
if(root == nullptr) return res;
//统一迭代法
bfs(root);
return res;
}
//统一迭代法
void bfs(TreeNode* root)
{
if(root == nullptr)
return;
stack<TreeNode*> st;
st.push(root);
TreeNode* temp;
while(!st.empty())
{
temp = st.top();
if(temp != nullptr)
{
st.pop();
if(temp->right) st.push(temp->right);
if(temp->left) st.push(temp->left);
st.push(temp);
st.push(nullptr);//当前层结束
}
else {
st.pop();
temp = st.top();
st.pop();
res.push_back(temp->val);
}
}
}
};
BM24 二叉树的中序遍历
描述:给定一个二叉树的根节点root,返回它的中序遍历结果。进阶:空间复杂度 O(n),时间复杂度 O(n)。
- 统一迭代法,关键就是在中结点后面加一个空节点标记
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<int> result;
vector<int> inorderTraversal(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
unitedbfs(root);
return result;
}
void unitedbfs(TreeNode* root)
{
if(root == nullptr) return;
stack<TreeNode*> st;
st.push(root);
TreeNode* temp;
while(!st.empty())
{
temp = st.top();
st.pop();
if(temp != nullptr)
{
if(temp->right) st.push(temp->right);
st.push(temp);
st.push(nullptr);
if(temp->left) st.push(temp->left);
}
else
{
//更新当前节点
temp = st.top();
st.pop();
result.push_back(temp->val);
}
}
}
};
- 递归法
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<int> result;
vector<int> inorderTraversal(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
dfs(root);
return result;
}
void dfs(TreeNode* root)
{
if(root == nullptr) return;
if(root->left) dfs(root->left);
result.push_back(root->val);
if(root->right) dfs(root->right);
}
};
BM25 二叉树的后序遍历
描述:给定一个二叉树,返回他的后序遍历的序列。后序遍历是值按照 左节点->右节点->根节点 的顺序的遍历。
- 统一迭代法
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<int> result;
vector<int> postorderTraversal(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
//dfs(root);
unitedbfs(root);
return result;
}
void unitedbfs(TreeNode* root)
{
stack<TreeNode*> st;
if(root != nullptr) st.push(root);
TreeNode* temp;
while(!st.empty())
{
temp = st.top();
st.pop();
if(temp != nullptr)
{
st.push(temp);
st.push(nullptr);
if(temp->right) st.push(temp->right);
if(temp->left) st.push(temp->left);
}
else {
temp = st.top();
st.pop();
result.push_back(temp->val);
}
}
}
void dfs(TreeNode* root)
{
if(root == nullptr) return;
if(root->left) dfs(root->left);
if(root->right) dfs(root->right);
result.push_back(root->val);
}
};
- 递归法
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<int> result;
vector<int> postorderTraversal(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
dfs(root);
return result;
}
void dfs(TreeNode* root)
{
if(root == nullptr) return;
if(root->left) dfs(root->left);
if(root->right) dfs(root->right);
result.push_back(root->val);
}
};
BM26 求二叉树的层序遍历
- 迭代法
和上面三个题不一样的地方来了,这里的层序遍历用队列。队列存放当前层的结点,for循环逐个遍历队列,每一层通过队列的大小(节点个数)来控制。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<vector<int> > result;
vector<vector<int> > levelOrder(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
queue<TreeNode* > que;
que.push(root);
TreeNode* cur;
int size = 0;
while(!que.empty())
{
vector<int> temp;
size = que.size();
for(int i=0; i<size; i++)
{
cur = que.front();
que.pop();
temp.push_back(cur->val);
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
result.push_back(temp);
}
return result;
}
};
- 递归法,通过深度来控制
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<vector<int> > result;
vector<vector<int> > levelOrder(TreeNode* root) {
result.clear();
if(root == nullptr) return result;
int depth = 0;
dfs(root, depth);
return result;
}
void dfs(TreeNode* root, int depth)
{
if(root == nullptr) return;
if(result.size() == depth) result.push_back(vector<int>{});
result[depth].push_back(root->val);
if(root->left) dfs(root->left, depth+1);
if(root->right) dfs(root->right, depth+1);
}
};
BM27 按之字形顺序打印二叉树
- 栈实现,两个栈,一个保存从左到右的结点,一个保存从右到左的节点。处理每个栈的时候有点层序遍历的意思,遍历当前栈的所有节点,同时把下一层的结点保存在另外一个栈中。可以用for或者while实现。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
#include <iterator>
#include <ratio>
#include <stack>
class Solution {
public:
vector<vector<int> > result;
vector<vector<int> > Print(TreeNode* pRoot) {
result.clear();
if(pRoot == nullptr) return result;
stackdone(pRoot);
return result;
}
void stackdone(TreeNode* node)
{
stack<TreeNode*> st1;
stack<TreeNode*> st2;
vector<int> v;
TreeNode* cur;
if(node != nullptr) st1.push(node);
while(!st1.empty() || !st2.empty())
{
if(!st1.empty())
{
//左到右
v.clear();
int size = st1.size();
for(int i=0; i<size; i++)
{
cur = st1.top();//当前结点
v.push_back(cur->val);//保存当前结点值
//栈 先进后出 下一层在st2 入栈顺序是左 右;出栈就是右 左
if(cur->left) st2.push(cur->left);
if(cur->right) st2.push(cur->right);
st1.pop();
}
result.push_back(v);
}
if(!st2.empty())
{
//从右到左
v.clear();
while(!st2.empty())
{
cur = st2.top();
v.push_back(cur->val);
if(cur->right) st1.push(cur->right);
if(cur->left) st1.push(cur->left);
st2.pop();
}
result.push_back(v);
}
}
}
};
- 队列实现,也是遍历当前队列的所有节点,同时把下一层的结点保存在另外一个队列中。如果当前层是偶数,从左往右,尾部保存结点值;奇数,从右往左,头部保存结点值。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
#include <iterator>
#include <ratio>
#include <stack>
class Solution {
public:
vector<vector<int> > result;
vector<vector<int> > Print(TreeNode* pRoot) {
result.clear();
if(pRoot == nullptr) return result;
queuedone(pRoot);
return result;
}
void queuedone(TreeNode* node)
{
queue<TreeNode*> que;
vector<int> v;
TreeNode* cur;
if(node != nullptr) que.push(node);
int size = 0, depth = 0;
while (!que.empty())
{
size = que.size();
v.clear();
for(int i=0; i<size; i++)
{
cur = que.front();
que.pop();
if(cur == nullptr) continue;//空元素跳过
que.push(cur->left);
que.push(cur->right);
if(depth % 2 == 0) v.push_back(cur->val);
else v.insert(v.begin(), cur->val);
}
depth++;
if(!v.empty()) result.push_back(v);
}
}
};
BM28 二叉树的最大深度
描述:求给定二叉树的最大深度,深度是指树的根节点到任一叶子节点路径上节点的数量。最大深度是所有叶子节点的深度的最大值。(注:叶子节点是指没有子节点的节点。)
数据范围:0 ≤ n ≤ 100000,树上每个节点的val满足 ∣val∣≤100
要求: 空间复杂度 O(1),时间复杂度 O(n)
-
补充:
使用前序求的就是深度,使用后序求的是高度。
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。 -
递归法
先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
int res = 0;
if(root == nullptr) return res;
res = dfs(root);
return res;
}
int dfs(TreeNode* root)
{
if(root == nullptr) return 0;
int leftdepth = dfs(root->left);
int rightdepth = dfs(root->right);
int res = max(leftdepth, rightdepth) + 1;
return res;
}
};
- 迭代法,队列,层序遍历
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
int res = 0;
if(root == nullptr) return res;
//res = dfs(root);
res = bfs(root);
return res;
}
int bfs(TreeNode* root)
{
if(root == nullptr) return 0;
queue<TreeNode*> que;
que.push(root);
int dep = 0, size = 0;
TreeNode* temp;
while(!que.empty())
{
size = que.size();
dep++;
for(int i=0; i<size; i++)
{
temp = que.front();
que.pop();
if(temp->left) que.push(temp->left);
if(temp->right) que.push(temp->right);
}
}
return dep;
}
int dfs(TreeNode* root)
{
if(root == nullptr) return 0;
int leftdepth = dfs(root->left);
int rightdepth = dfs(root->right);
int res = max(leftdepth, rightdepth) + 1;
return res;
}
};
BM29 二叉树中和为某一值的路径(一)
- 减法,递归
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == nullptr) return false;
return dfs(root, sum - root->val);
}
bool dfs(TreeNode* cur, int target)
{
//遍历到叶子节点 且为0 用减法
//当前节点为叶子节点并且目标路径存在时
if(cur->left == nullptr && cur->right == nullptr && target == 0) return true;
//当前节点为叶子节点并且目标路径 不存在时
if(cur->left == nullptr && cur->right == nullptr) return false;
// 对左右分支进行 dfs
if(cur->left)
{
target -= cur->left->val;
if(dfs(cur->left, target)) return true;
target += cur->left->val;
}
if(cur->right)
{
target -= cur->right->val;
if(dfs(cur->right, target)) return true;
target += cur->right->val;
}
return false;
}
};
加法,递归
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == nullptr) return false;
int res = 0;
return dfs_add(root, sum, res);
}
bool dfs_add(TreeNode* cur, int sum, int res)
{
if(cur == nullptr) return false;
res += cur->val;
if(!cur->left && !cur->right && res == sum) return true;
return dfs_add(cur->left, sum, res) || dfs_add(cur->right, sum, res);
}
};
BM30 二叉搜索树与双向链表
描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
递归法,看解答的,变成双向链表,cur左指针指向上一个结点pre,pre的右指针指向cur:
- 二叉搜索树的最左叶子节点
- 中序遍历,左中右
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* pre;
TreeNode* Convert(TreeNode* pRootOfTree) {
if(pRootOfTree == nullptr) return nullptr;
TreeNode* cur = pRootOfTree;
while(cur->left) cur = cur->left;
inorder(pRootOfTree);
return cur;
}
void inorder(TreeNode* root)
{
if(root == nullptr) return;
inorder(root->left);//左
//中 修改指向
root->left = pre;
if(pre)
pre->right = root;
pre = root;//更新pre 指向当前结点,作为下一个结点的前继
inorder(root->right);
}
};
用栈,迭代
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* pre = nullptr;
TreeNode* head = nullptr;
stack<TreeNode*> st;
TreeNode* Convert(TreeNode* pRootOfTree) {
if(pRootOfTree == nullptr) return nullptr;
stackdone(pRootOfTree);
return head;
}
void stackdone(TreeNode* root)
{
while(!st.empty() || root != nullptr)
{
//找到表头
while(root != nullptr)
{
st.push(root);
root = root->left;
}
if(!st.empty())
{
//取出栈的结点
root = st.top();
st.pop();
//第一次出栈的是表头
if(pre == nullptr)
head = root;
else //建立双向连接
{
pre->right = root;
root->left = pre;
}
pre = root;//更新结点
root = root->right;//连接链表
}
}
}
};
最后两题,要再写写。