LeetCode hot-100 简单and中等难度,31-40.

75. 颜色分类

难度中等527收藏分享切换为英文关注反馈

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

进阶:

  • 一个直观的解决方案是使用计数排序的两趟扫描算法。
    首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
  • 你能想出一个仅使用常数空间的一趟扫描算法吗?
用三个指针(p0, p2 和curr)来分别追踪0的最右边界,2的最左边界和当前考虑的元素。
While curr <= p2 :

若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。cur++
若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。cur不变。
若 nums[curr] = 1 :将指针curr右移。

题解少了一个状态维持的关键说明:
curr 左边全都是0/1的有序序列!
curr位置是0时,与左边的0位置交换,因为保证curr左边全是0/1, 所以交换过来的必然是0/1,状态维持住了;
curr位置是2时,交换后,curr不能移动,因为一移动,没法保证交换过来的是0/1;所以这里不移动;这时状态也维持住了
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int l=0, r=nums.size()-1;
        int cur=0;
        while(cur<=r){
            if(nums[cur]==0){
                swap(nums[cur],nums[l]);l++;cur++;
            }else if(nums[cur]==2){
                swap(nums[cur],nums[r]);r--;
            }else cur++;
        }
        return;
    }
};

77. 组合

难度中等325

给定两个整数 nk,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
class Solution {
public:
    void get(vector<int>& temp,int num,int k,vector<vector<int>> &ans,int index,int n){
//这个在前
        if(num==k){
            ans.push_back(temp);return;
        }
//这个在后...
        if(index==n+1||num>k) return;
        temp.push_back(index);
        get(temp,num+1,k,ans,index+1,n);
        temp.pop_back();
        get(temp,num,k,ans,index+1,n);

    }
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> ans;
        if(n==0||k==0) return ans;
        vector<int> temp;
        get(temp,0,k,ans,1,n);
        return ans;
    }
};

78. 子集

难度中等689

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
class Solution {
public:
    vector<int> tmp;
    void get(int xuan,int sum,int n,vector<vector<int>> &ans,vector<int>& nums){
        if(sum==n){
            // if(xuan==0) return;
            ans.push_back(tmp);return;
        }
        get(xuan,sum+1,n,ans,nums);
        tmp.push_back(nums[sum]);
        get(xuan+1,sum+1,n,ans,nums);
        tmp.pop_back();
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> ans;
        // ans.push_back({});
        //每一个元素,选或者不选~
        get(0,0,nums.size(),ans,nums);
        return ans;
    }
};

79. 单词搜索

难度中等506

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

 

示例:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false

典型题目,好像有点....

class Solution {
public:
    int t[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    bool ans=false;
    void get(vector<vector<char>>& board,vector<vector<bool>>& visit,int index,string word,int x,int y,int n,int m){
        if(ans) return;
        if(index==word.size()-1&&board[x][y]==word[index]){
            ans= true;return;
        }
        if(board[x][y]==word[index]){
            visit[x][y]=true;
            for(int i=0;i<4;i++){
                int tx=x+t[i][0];
                int ty=y+t[i][1];
                if(tx<0||ty<0||tx>=n||ty>=m||visit[tx][ty]==true) continue;
                get(board,visit,index+1,word,tx,ty,n,m);
            }
            visit[x][y]=false;
        }
    }
    bool exist(vector<vector<char>>& board, string word) {
        //这就是简单的dfs吧..
        int n=board.size();
        int m=board[0].size();
        vector<vector<bool>> visit(n,vector<bool>(m,false));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(visit[i][j]==false){
                    get(board,visit,0,word,i,j,n,m);
                    if(ans) return ans;
                }
            }
        }
        return false;
    }
};

 

//经常不知道起点终点的...真是笨
class Solution {
    int M, N;
public:
    bool exist(vector<vector<char>>& board, string word) {
        M = board.size();
        if(!M) return false;
        N = board[0].size();
        for(int i = 0; i < M; i++)
            for(int j = 0; j < N; j++)
                if(board[i][j] == word[0])
                    if(backtrack(board, word, i, j, 0))
                        return true;
        return false;
    }
    bool backtrack(vector<vector<char>>& board, string& word, int row, int col, int num)
    {
        if(row < 0 || row >= M || col < 0 || col >= N || board[row][col] != word[num])
            return false;
        if(num == word.size()-1)
            return true;
        char curr = board[row][col];
        board[row][col] = '#';
        if(backtrack(board, word, row, col + 1, num + 1)) return true;
        if(backtrack(board, word, row, col - 1, num + 1)) return true;
        if(backtrack(board, word, row + 1, col, num + 1)) return true;
        if(backtrack(board, word, row - 1, col, num + 1)) return true;
        board[row][col] = curr;
        return false;
    }
};

94. 二叉树的中序遍历

难度中等612

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void get(TreeNode* root,vector<int> &ans){
        if(root==NULL) return;
        get(root->left,ans);
        ans.push_back(root->val);
        get(root->right,ans);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        if(root==NULL) return {};
        vector<int> ans;
        get(root,ans);
        return ans;
    }
};
/*
颜色标记法-一种通用且简明的树遍历方法
使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为灰色,则将节点的值输出。

作者:hzhu212
链接:https://leetcode-cn.com/problems/binary-tr
ee-inorder-traversal/solution/yan-se-biao-ji-fa
-yi-chong-tong-yong-qie-jian-ming/
*/
解释一下为什么需要“右子节点、自身、左子节点依次入栈”
我们有一棵二叉树:
               中
              /  \
             左   右
前序遍历:中,左,右
中序遍历:左,中,右
后序遍历:左,右,中
本题需要中序遍历。
栈是一种 先进后出的结构,出栈顺序为左,中,右
那么入栈顺序必须调整为倒序,也就是右,中,左
同理,如果是前序遍历,入栈顺序为 右,左,中;后序遍历,入栈顺序中,右,左

这个题解的宗旨就是使用一个颜色或者任何东西,记录节点的访问次数。
在中序中,节点第二次访问会被输出。因此使用颜色,或者hash表来记
录节点的访问次数,次数使用hash表来记录
class Solution {
	vector<int>ans;
public:
	vector<int> inorderTraversal(TreeNode* root) {
		int white = 0;
		int gray = 1;
		stack<pair<int, TreeNode*>>s;
		s.push(make_pair(white,root));
		while (!s.empty())
		{
			int color = s.top().first;
			TreeNode* t = s.top().second;
			s.pop();
			if (t == NULL) continue;
			if (color == white)
			{
				s.push(make_pair(white, t->right));
				s.push(make_pair(gray, t));
				s.push(make_pair(white, t->left));
			}
			else ans.push_back(t->val);
		}
		return ans;
	}
};
前序遍历:

在迭代算法中,思路演变成,每到一个节点 A,就应该立即访问它。
因为,每棵子树都先访问其根节点。对节点的左右子树来说,也一定是先访问根。
在 A 的两棵子树中,遍历完左子树后,再遍历右子树。
因此,在访问完根节点后,遍历左子树前,要将右子树压入栈。

栈S;
p= root;
while(p || S不空){
    while(p){
        访问p节点;
        p的右子树入S;
        p = p的左子树;
    }
    p = S栈顶弹出;
}


后序遍历与前序遍历相对称。
思路: 每到一个节点 A,就应该立即访问它。 然后将左子树压入栈,再次遍历右子树。
遍历完整棵树后,结果序列逆序即可。
栈S;
p= root;
while(p || S不空){
    while(p){
        访问p节点;
        p的左子树入S;
        p = p的右子树;
    }
    p = S栈顶弹出;
}
结果序列逆序;

二叉树的中序遍历
思路:每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。
在访问完 A 后,A 就可以出栈了。因为 A 和其左子树都已经访问完成。

栈S;
p= root;
while(p || S不空){
    while(p){
        p入S;
        p = p的左子树;
    }
    p = S.top 出栈;
    访问p;
    p = p的右子树;
}


96. 不同的二叉搜索树

难度中等745收藏分享切换为英文关注反馈

给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

 

 动态规划,其实从二叉搜索树的中序来入手

本题思路解释
由于1,2...n这个数列是递增的,所以我们从任意一个位置“提起”这课树,都满足二叉搜索树的这个条件:左边儿子数小于爸爸数,右边儿子数大于爸爸数

从1,2,...n数列构建搜索树,实际上只是一个不断细分的过程
例如,我要用[1,2,3,4,5,6]构建
首先,提起"2"作为树根,[1]为左子树,[3,4,5,6]为右子树

现在就变成了一个更小的问题:如何用[3,4,5,6]构建搜索树?
比如,我们可以提起"5"作为树根,[3,4]是左子树,[6]是右子树

现在就变成了一个更更小的问题:如何用[3,4]构建搜索树?
那么这里就可以提起“3”作为树根,[4]是右子树
或"4"作为树根,[3]是左子树

可见n=6时的问题是可以不断拆分成更小的问题的

假设f(n) = 我们有n个数字时可以构建几种搜索树
我们可以很容易得知几个简单情况 f(0) = 1, f(1) = 1, f(2) = 2
(注:这里的f(0)可以理解为=1也可以理解为=0,这个不重要,我们这里理解为=1,即没有数字时只有一种情况,就是空的情况)

那n=3时呢?
我们来看[1,2,3]
如果提起1作为树根,左边有f(0)种情况,右边f(2)种情况,左右搭配一共有f(0)*f(2)种情况
如果提起2作为树根,左边有f(1)种情况,右边f(1)种情况,左右搭配一共有f(1)*f(1)种情况
如果提起3作为树根,左边有f(2)种情况,右边f(0)种情况,左右搭配一共有f(2)*f(0)种情况
容易得知f(3) = f(0)*f(2) + f(1)*f(1) + f(2)*f(0)

同理,
f(4) = f(0)*f(3) + f(1)*f(2) + f(2)*f(1) + f(3)*f(0)
f(5) = f(0)*f(4) + f(1)*f(3) + f(2)*f(2) + f(3)*f(1) + f(4)*f(0)
......
发现了咩?
对于每一个n,其式子都是有规律的
每一项两个f()的数字加起来都等于n-1

既然我们已知f(0) = 1, f(1) = 1
那么就可以先算出f(2),再算出f(3),然后f(4)也可以算了...
计算过程中可以把这些存起来,方便随时使用
最后得到的f(n)就是我们需要的解了

作者:xiao-yan-gou
链接:https://leetcode-cn.com/problems/unique-binary-search-trees/solution/er-cha-sou-suo-shu-fu-xi-li-zi-jie-shi-si-lu-by-xi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数,则

G(n)=f(1)+f(2)+f(3)+f(4)+...+f(n)

当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,则

f(i)=G(i−1)∗G(n−i)

综合两个公式可以得到 卡特兰数 公式

G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)

作者:guanpengchn
链接:https://leetcode-cn.com/problems/unique-b
inary-search-trees/solution/hua-jie-suan-fa-96-
bu-tong-de-er-cha-sou-suo-shu-b/

class Solution {
public:
    int numTrees(int n) {
        vector<int> G(n + 1, 0);
        G[0] = 1;
        G[1] = 1;

        for (int i = 2; i <= n; ++i) {
            for (int j = 1; j <= i; ++j) {
                G[i] += G[j - 1] * G[i - j];
            }
        }
        return G[n];
    }
};

98. 验证二叉搜索树

难度中等698收藏分享切换为英文关注反馈

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。
//主要是max和min的值可以就是根的值,那么递归的时候直接用就行。
bool isValidBST(TreeNode* root, TreeNode* min,TreeNode* max){
        if(root==NULL) return true;
        if(min!=NULL&&root->val<=min->val) return false;
        if(max!=NULL&&root->val>=max->val) return false;
        return isValidBST(root->left,min,root)&&
        isValidBST(root->right,root,max);
    }
bool isValidBST(TreeNode* root){
        return isValidBST(root,NULL,NULL);
    }
//还有一种方法是使用中序遍历
//因为中序遍历的结果总是 升序。

注意& 或者用全局变量
class Solution {
public:
    bool isBSTUtil(TreeNode* root,long long& prev){
        if(root){
            if(!isBSTUtil(root->left,prev)) return false;
// 当前结点小于等于它的直接前驱顶点,返回false 
            if(root->val<=prev) return false;
            prev=root->val;
            return isBSTUtil(root->right,prev);
        }
return true;
    }
    bool isValidBST(TreeNode* root) {
        long long prev = LONG_MIN; 
        return isBSTUtil(root, prev); 
    }
};

作者:novice2master
链接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/bao-zhun-sheng-guo-guan-fang-ti-jie-by-novice2mast/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

101. 对称二叉树

难度简单948收藏分享切换为英文关注反馈

给定一个二叉树,检查它是否是镜像对称的。

 

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

 

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3
class Solution {
public:
    bool isSame(TreeNode* root1,TreeNode* root2){
        if(root1==NULL&&root2==NULL) return true;
        if(root1==NULL||root2==NULL) return false;
        if(root1->val!=root2->val) return false;
        return isSame(root1->left,root2->right)&&isSame(root1->right,root2->left);
    }
    bool isSymmetric(TreeNode* root) {
       if(root==NULL) return true; 
       return isSame(root->left,root->right);
    }
};

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值