文章目录
- 1.最后一个单词的长度
- find_last_of()
- eraese()用法
- ①在string中应用,直接用下标,删除包括该位在内的后面的元素
- ②在string中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
- ③在vector中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
- ps:vector中不能用下标来删元素,会报错!!!!
- ④在set中应用,删除与xxx相等的元素,返回被移除元素个数//时间复杂度(logN,N位set中元素个数)
- ⑤在set中应用,直接用地址pos,无返回值//时间复杂度O(1)
- ⑥在set中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
- 2.加一
- 注意溢出!!!
- 3.二进制求和
- reverse()头尾反转
- 4.x的平方根
- 二分法 (这题二分法非常典型)
- 5.爬楼梯(斐波那契数列)
- ①倒序 递归 下面这个例子会超时(因为冗余操作多),40以上都会超时:
- ②也是倒序 递归 不过用数组记住了已经计算过的值,不超时了
- 在递归中,用数组存值可以去除冗余步骤:
- ③动态规划做法,比递归好
- 动态规划:
- 6.删除排序链表中重复的元素
- 7.合并两个有序数组
- 8.相同的树
- 二叉树基础知识:①二叉树构造方法
- ②注意:
- 9.对称二叉树
- ①递归法
- ②迭代法(队列的应用):
- 10.二叉树的最大深度
- ①广度优先:队列
- ②深度优先:递归
1.最后一个单词的长度
find_last_of()
find_first_of()同理
当找不到括号里的字符串时,值为-1
int lengthOfLastWord(string s) {
if (s == "")//空串
return 0;
int kg_pos = s.find_last_of(' ');//找最后一个空格位置
while (kg_pos==s.size()-1)
{
s.erase(kg_pos);
if (s == "")//删完之后是空串
return 0;
kg_pos = s.find_last_of(' ');
}
return s.size()-kg_pos-1;
//如果只有一个单词,kg_pos是-1,减负一就是加一
}
int main()
{
string s = "happy world ";
cout << lengthOfLastWord(s);
return 0;
}
eraese()用法
①在string中应用,直接用下标,删除包括该位在内的后面的元素
string s = "01234";
s.erase(2);//这里的s一定要写
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
输出:01 //解释:第二位以后的都没有了
②在string中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
string s = "01234";
s.erase(s.begin()+2);//这里的s一定要写
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
输出:0134 //解释:只删除了第二位地址上的元素
string s = "01234";
s.erase(s.begin() + 2,s.begin()+4);//这里的s一定要写
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
输出:014//解释:删除了23,因为左闭右开
③在vector中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
vector<int>array = { 0,1,2,3,4 };
array.erase(array.begin()+2);
for (int i = 0; i < array.size(); i++)
{
cout << array[i];
}
输出:0134 //解释:只删除了第二位地址上的元素
vector<int>array = { 0,1,2,3,4 };
array.erase(array.begin() + 2, array.begin() + 4);
for (int i = 0; i < array.size(); i++)
{
cout << array[i];
}
输出:014//删除了23,因为左闭有开
ps:vector中不能用下标来删元素,会报错!!!!
④在set中应用,删除与xxx相等的元素,返回被移除元素个数//时间复杂度(logN,N位set中元素个数)
set<int>array = { 0,2,2,3,3,4 };
cout<<array.erase(2)<<endl;
for (auto i = array.begin(); i != array.end(); i++)
{
cout << *i;
}
return 0;
输出:
1
034
//解释:第一行的1是array.erase(2)的返回值,删除了一个元素2,第二行是删除了元素2之后的set容器内的元素值
⑤在set中应用,直接用地址pos,无返回值//时间复杂度O(1)
set<int>array = { 0,2,2,3,3,4 };
array.erase(array.find(2));
for (auto i = array.begin(); i != array.end(); i++)
{
cout << *i;
}
return 0;
输出:
034
//解释:找到了值为2的元素地址删除了该地址的元素,输出set容器内的元素值
⑥在set中应用,用地址,只删除地址的那一位。或者用地址区间,删除区间元素(左闭右开)
set<int>array = { 0,2,2,3,3,4};
array.erase(array.find(2),array.find(4));
for (auto i = array.begin(); i != array.end(); i++)
{
cout << *i;
}
return 0;
输出:
04
//解释:删除了23,因为左闭右开
ps:set是有自动排序自动除重功能的,mutiset没有自动除重(是set的特殊)
ps:sort也是左闭右开的
2.加一
注意溢出!!!
题目的意思是:数组的最后一位加一,0-8就直接加一,9就要往前进位(注意如果整个数组每一位都是九的话,就溢出了,比如999就要变成1000,解法:建一个新的长度+1)
vector<int> plusOne(vector<int>& digits) {
for (int i = digits.size() - 1; i >= 0; i--)
{
digits[i]++;
digits[i] %= 10;
if (digits[i] != 0)//该位不是10就可以return了,否则就要一直往前进位
return digits;
}
//下面这段是为了解决整个数组全是9的情况
vector<int>temp(digits.size() + 1);
temp[0] = 1;
return temp;
}
int main()
{
vector<int>digits = { 9,9,9,9 };
digits=plusOne(digits);
for (int i = 0; i < digits.size(); i++)
{
cout << digits[i];
}
return 0;
}
//这段代码输出结果为10000
3.二进制求和
reverse()头尾反转
除了string之外,vector也可以用
用法:reverse(名字.begin(),名字.end());
string addBinary(string a, string b) {
string ans;
string maxs, mins;
if (a.size() >= b.size())
{
maxs = a;
mins = b;
}
else
{
maxs = b;
mins = a;
}
int as=maxs.size()-1, is=mins.size()-1;
for (int i = 0; i<maxs.size(); i++)
{
if (as - i >= 0)
{
ans += maxs[as - i]-48;//把string变长
}
if (is - i >= 0)
{
ans[i] += mins[is - i]-48;
}
ans[i] += 48;//ascll码
}
int ans_size = ans.size()-1;
for (int i = 0; i <=ans_size; i++)
{
if (ans[i]-48 >= 2)
{
ans[i] -= 2;
if (i + 1 <= ans_size)
ans[i + 1] += 1;
else
ans += '1';
}
}
reverse(ans.begin(), ans.end());//string的头尾反转,vector也可以用这个
//(原理大概是把它们从头到尾的地址反过来)
return ans;
}
int main()
{
cout << addBinary("0", "0");
return 0;
}
4.x的平方根
二分法 (这题二分法非常典型)
如果是乘法的话,记得用long,不然会溢出。
如果是除法的话,可以用int,不会溢出。
但乘法会比乘法快。
①right=mid+1。返回条件right-left<=2
除法:
int mySqrt(int x) {
int left = 0, right = x;//用int
if (x == 0)
return 0;
if (x <= 3)
return 1;
while (left < right)
{
int mid = left + (right - left) / 2;//用int
if (mid> x/mid)//答案在左边
{
right = mid+1;
}
else if (mid< x/mid)//答案在右边,除法不容易溢出
{
left = mid;
}
else
return mid;
if (right - left <= 2)
return left;
}
return 0;
}
乘法:
int mySqrt(int x) {
long left = 0, right = x;
if (x == 0)
return 0;
if (x <= 3)
return 1;
while (left < right)
{
long mid = left + (right - left) / 2;
if (mid*mid>x)//乘法容易溢出,但会快一点点
{
right = mid+1;
}
else if (mid*mid< x)
{
left = mid;
}
else
return mid;
if (right - left <= 2)
return left;
}
return 0;
}
②right=mid。返回条件right-left==1或者写成right-left<=1也行
int mySqrt(int x) {
long left = 0, right = x;
if (x == 0)
return 0;
if (x <= 3)
return 1;
while (left < right)
{
long mid = left + (right - left) / 2;
if (mid*mid > x)//乘法容易溢出,但会快一点点
{
right = mid;
}
else if (mid*mid < x)
{
left = mid;
}
else
return mid;
if (right - left == 1)
return left;
}
return 0;
}
(其实在这题没有太大区别,先保证正确不溢出,再提速)
5.爬楼梯(斐波那契数列)
①倒序 递归 下面这个例子会超时(因为冗余操作多),40以上都会超时:
int climbStairs(int n) {
long c1 = n - 1, c2 = n - 2;
if (n <=1)
return 1;
return climbStairs(c1) + climbStairs(c2);
}
int main()
{
cout << climbStairs(40);
return 0;
}
②也是倒序 递归 不过用数组记住了已经计算过的值,不超时了
在递归中,用数组存值可以去除冗余步骤:
long dp[100];
long climbStairs(int n) {
long c1 = n - 1, c2 = n - 2;
if (n <= 1)
return 1;
if(dp[n]!=0)
return dp[n];
else
{
dp[n]= climbStairs(c1) + climbStairs(c2);
return dp[n];
}
}
③动态规划做法,比递归好
动态规划:
正序就不会超时,因为冗余操作少:
long dp[100];
long climbStairs(int n) {
dp[0] = 1; dp[1] = 1;
for (int i = 2; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
int main()
{
cout << climbStairs(60);
return 0;
}
和上面那个差不多
long climbStairs(int n) {
long dp[n+1];//加1是为了在测试用例是1时,dp[1]不溢出
n--;
dp[0]=1,dp[1]=2;
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
6.删除排序链表中重复的元素
(链表很少用)
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
ListNode* deleteDuplicates(ListNode* head) {
auto cur = head;//其实不用cur直接用head来循环也不是不行,但是不表意了
while (cur&&cur->next) {
if (cur->next->val == cur->val) {
cur->next = cur->next->next;
}
else {
cur = cur->next;
//注意,题目给的是已排好序的链表,所以一旦cur和cur->next的值不等,那么后面全部都不会出现和cur的值相等的元素了,所以cur可以往后移动一位
//cur=cur->next是往后移一位,地址是下一位的地址了
}
}
return head;
}
7.合并两个有序数组
multiset和set差不多都是容器(它们都是一插进去就自动排序),set里自动除重,multiset里可以有重复的元素
用法传送门:https://blog.csdn.net/sodacoco/article/details/84798621
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
nums1.erase(nums1.end() - n, nums1.end());
for (int i = 0; i < n; i++)
{
nums1.push_back(nums2[i]);
}
sort(nums1.begin(), nums1.end());
}
ps:sort()是左闭右开的
vector<int>array = { 5,4,3,2,1 };
sort(array.begin(), array.begin() + 3);//实际上只排序到了+2的位置,也就是只排了012三位
for (int i = 0; i < array.size(); i++)
{
cout << array[i];
}
输出:34521//解释:只排序了第012位,没有排array.begin()+3位,因为左闭右开
8.相同的树
二叉树基础知识:①二叉树构造方法
②注意:
int main里那个->一定要写成->不能写成点 .
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode() :val(0), left(nullptr), right(nullptr) {};
//构造函数,那些0、nullptr是默认值
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
TreeNode(int x,TreeNode *left,TreeNode *right) :val(x), left(left), right(right) {};
};
int main()
{
TreeNode *left = new TreeNode(2);//左子树
TreeNode *right = new TreeNode(3);//右子树
TreeNode *NEWTREE = new TreeNode(1,left,right);//树根
cout << NEWTREE->val << endl;//根的值
cout << NEWTREE->left->val;//左子树根的值
cout << NEWTREE->right->val << endl;//右子树根的值
TreeNode *kt = new TreeNode();//左右子树默认为空
if(kt == nullptr)
cout <<"根null" ;
if (kt->left == nullptr)//写nullptr或者NULL都行,但后者NULL一定要大写,小写不行
cout << "左子树null";
return 0;
}
题目:
#include <iostream>
using namespace std;
#include<bits/stdc++.h>
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode() :val(0), left(nullptr), right(nullptr) {};//构造函数,那些0、nullptr是默认值
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
TreeNode(int x,TreeNode *left,TreeNode *right) :val(x), left(left), right(right) {};
};
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p == nullptr&&q == nullptr)//两树都空
return true;
else if (p == nullptr||q == nullptr)//只有一树空
return false;
else if (p->val != q->val)//根的值不相等
return false;
else//根的值相等,就判断左右子树
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
//测试用例2:
int main()
{
TreeNode *TA = new TreeNode(1,new TreeNode(2),new TreeNode());//树根
TreeNode *TB = new TreeNode(1, new TreeNode(), new TreeNode(2));
cout<<isSameTree(TA, TB);
return 0;
}
9.对称二叉树
①递归法
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
bool check(TreeNode *Left, TreeNode *Right)//用这个递归
{
if (Left == NULL && Right == NULL)//都空
return true;
else if (Left == NULL || Right == NULL)//只有一个空
return false;
else
return Left->val == Right->val&&check(Left->left, Right->right) && check(Left->right, Right->left);
//重点在这行,左左、右右是一组,左右、右左是一组,他们符合才对称
}
bool isSymmetric(TreeNode* root) {//用这个输出答案
return check(root, root);
}
int main()
{
TreeNode *tree = new TreeNode(1);
tree->left = new TreeNode(2);
tree ->right = new TreeNode(2);
tree->left->left = new TreeNode(3);
tree->left->right = new TreeNode(4);
tree->right->left = new TreeNode(4);
tree->right->right = new TreeNode(3);
cout<<isSymmetric(tree);
return 0;
}
②迭代法(队列的应用):
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
bool check(TreeNode *Left, TreeNode *Right)
{
queue<TreeNode*>q;//一层一层地放到队列里,每两个两个相等的话就是对称
q.push(Left);
q.push(Right);
while (!q.empty())
{
Left = q.front();
q.pop();
Right = q.front();
q.pop();
if (Left == NULL && Right == NULL)
continue;
else if (Left == NULL || Right == NULL)//只有一个空
return false;
else if (Left->val != Right->val)
return false;
else//这里的判断和递归方法里的差不多
{
q.push(Left->left);
q.push(Right->right);
q.push(Left->right);
q.push(Right->left);
}
}
return true;//队列空了就是所有元素遍历完了都对称
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
③下面这个是自己写的,太憨了。。
用的是中序遍历然后反转…
vector<int>ans;
void zx(TreeNode *tree)
{
if (tree != NULL)
{
if (tree->left != NULL && tree->right == NULL)
tree->right = new TreeNode(-100);
if (tree->right != NULL && tree->left == NULL)
tree->left = new TreeNode(-100);
zx(tree->left);
ans.push_back(tree->val);
zx(tree->right);
}
}
bool isSymmetric(TreeNode* root) {
if(root!=NULL&&root->left != NULL && root->right != NULL)
if(root->left->val!=root->right->val)
return false;
zx(root);
vector<int>tempans=ans;
reverse(ans.begin(), ans.end());
if (tempans == ans)
return true;
else
return false;
}
10.二叉树的最大深度
①广度优先:队列
是从上一题的迭代法改出来的(和参考答案差不多)
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
int couDepth(TreeNode* root)
{
int count = 0;
queue<TreeNode*>q;
q.push(root);
while (!q.empty())
{
int S = q.size();
int flag = 0;
//对比了参考答案,其实这个flag设来并没用,q不为空的话,直接count++没问题的。
//用小于size来进行for循环的话就不会受到新push进去元素的影响
for (int i = 0; i < S; i++)//把本层的弄出来
{
TreeNode* Node = q.front();
q.pop();
if (Node != NULL)
{
flag = 1;//这层有东西
q.push(Node->left);
q.push(Node->right);
}
}
if (flag == 1)
count++;
}
return count;
}
int maxDepth(TreeNode* root) {//输出答案
return couDepth(root);
}
②深度优先:递归
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
int maxDepth(TreeNode* root)//深度,这种算法会短很多
{
if(root==NULL) return 0;
int l=maxDepth(root->left)+1;
int r=maxDepth(root->right)+1;
return l>r?l:r;
}
这题广度优先会快一点