leetcode c++(2)(二叉树、动态规划、用队列迭代、广度优先队列、深度优先栈、find_last_of( )、erase()、reverse( ))

文章目录

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;   
    }

这题广度优先会快一点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值