69. x 的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
提示:
0 < = x < = 2 31 − 1 0 <= x <= 2^{31} - 1 0<=x<=231−1
假设初始二分区间从1到x,然后进行二分;
需要注意计算mid的时候越界的问题,防止临时变量超出int范围,代码如下:
#include <iostream>
using namespace std;
// 二分查找
class Solution
{
public:
int mySqrt(int x)
{
int left = 1, right = x;
int ans = 0;
while (left <= right)
{
// int mid = (left + right) / 2; // 会 overflow(1 + 2147483647 cannot be represented in type 'int')
int mid = left + (right - left) / 2; // 不会 overflow
if (mid <= x / mid)
{
ans = mid;
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return ans;
}
};
int main()
{
Solution sol;
int res = sol.mySqrt(5);
cout << res;
return 0;
}
91. 解码方法
一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
‘A’ -> “1”
‘B’ -> “2”
…
‘Z’ -> “26”
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,“11106” 可以映射为:
“AAJF” ,将消息分组为 (1 1 10 6)
“KJF” ,将消息分组为 (11 10 6)
注意,消息不能分组为 (1 11 06) ,因为 “06” 不能映射为 “F” ,这是由于 “6” 和 “06” 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
示例 1:
输入:s = “12”
输出:2
解释:它可以解码为 “AB”(1 2)或者 “L”(12)。
示例 2:
输入:s = “226”
输出:3
解释:它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
示例 3:
输入:s = “06”
输出:0
解释:“06” 无法映射到 “F” ,因为存在前导零(“6” 和 “06” 并不等价)。
提示:
1 <= s.length <= 100
s 只包含数字,并且可能包含前导零。
这道题目可以利用动态规划进行解决,dp[i] 表示长度为 i 的前缀子串的可能情况;
状态转移方程为:
d p [ i ] = 第 i 位是否满足 ? d p [ i − 1 ] : 0 + 第 i − 1 位和第 i 位组合到一起是否满足 ? d p [ i − 1 ] : 0 ; dp[i] = 第i位是否满足?dp[i-1]:0+第i-1位和第i位组合到一起是否满足?dp[i-1]:0; dp[i]=第i位是否满足?dp[i−1]:0+第i−1位和第i位组合到一起是否满足?dp[i−1]:0;
初始化: d p [ 0 ] dp[0] dp[0] 表示长度为 0 的子串。解码也为空,所以只有一种可能,所以dp[0] = 1。
代码如下:
class Solution
{
public:
int numDecodings(string s)
{
int length = s.size();
vector<int> dp(length + 1);
dp[0] = 1;
dp[1] = s[0] >= '1' && s[0] <= '9' ? 1 : 0;
for (int i = 2; i <= length; i++)
{
int first = s[i - 2] - '0';
int second = s[i - 1] - '0';
if (second >= 1 && second <= 9)
{
dp[i] += dp[i - 1];
}
if (first > 0 && second + first * 10 >= 1 && second + first * 10 <= 26)
{
dp[i] += dp[i - 2];
}
}
return dp[length];
}
};
由于动态规划数组只和上一位和上两位有关系,所以可以将数组优化为3个变量,进一步节约空间:
class Solution
{
public:
int numDecodings(string s)
{
int length = s.size();
int dp0 = 1;
int dp1 = s[0] >= '1' && s[0] <= '9' ? 1 : 0;
for (int i = 2; i <= length; i++)
{
int first = s[i - 2] - '0';
int second = s[i - 1] - '0';
int tmp = 0;
if (second >= 1 && second <= 9)
{
tmp += dp1;
}
if (first > 0 && second + first * 10 >= 1 && second + first * 10 <= 26)
{
tmp += dp0;
}
dp0 = dp1;
dp1 = tmp;
}
return dp1;
}
};
118. 杨辉三角
已解答
简单
相关标签
相关企业
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
输入: numRows = 1
输出: [[1]]
提示:
1 < = n u m R o w s < = 30 1 <= numRows <= 30 1<=numRows<=30
自己的代码,中间vector空间大小处理的不好,存在内存的多次拷贝情况。
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<int> vec = {1};
vector<vector<int>> res;
res.push_back(vec);
for(int i=1; i< numRows;i++){
vec = vector<int>(i+1);
vec[0] = 1;
for(int j=1; j<i;j++){
vec[j] = res[i-1][j-1] + res[i-1][j];
}
vec[vec.size()-1] = 1;
res.push_back(vec);
}
return res;
}
};
答案的做法在内存处理上一步到位,利用resize去除掉了预留的空间,更加合理。
// 参考题解,进行了一点代码优化
class Solution
{
public:
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> res(numRows);
for (int i = 0; i < numRows; i++)
{
res[i].resize(i + 1);
res[i][0] = res[i][i] = 1;
for (int j = 1; j < i; j++)
{
res[i][j] = res[i - 1][j - 1] + res[i - 1][j];
}
}
return res;
}
};
116. 填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例 1:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,‘#’ 标志着每一层的结束。
示例 2:
输入:root = []
输出:[]
提示:
树中节点的数量在
[
0
,
2
12
−
1
]
[0, 2^{12} - 1]
[0,212−1] 范围内
-1000 <= node.val <= 1000
进阶:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
自己写的有点乱,利用nullptr来进行每一层节点的区分,其实没必要。题解利用for循环的做法更加巧妙。
#include <queue>
using namespace std;
// Definition for a Node.
class Node
{
public:
int val;
Node *left;
Node *right;
Node *next;
Node() : val(0), left(nullptr), right(nullptr), next(nullptr) {}
Node(int _val) : val(_val), left(nullptr), right(nullptr), next(nullptr) {}
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 == nullptr)
{
return root;
}
que.push(root);
que.push(nullptr);
while (!que.empty())
{
Node *tmp = que.front();
// 如果遇到了一个 nullptr,那么说明一层已结束,加入一个 nullptr
if (tmp == nullptr)
{
que.pop();
if (!que.empty())
que.push(nullptr);
continue;
}
// 如果 tmp 不是 nullptr,那么把它的左右指针加入进来
if (tmp->left != nullptr)
que.push(tmp->left);
if (tmp->right != nullptr)
que.push(tmp->right);
que.pop();
if (!que.empty())
{
tmp->next = que.front();
}
}
return root;
}
};
用Java来写的:
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {
}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
class Solution {
public Node connect(Node root) {
if (root == null) {
return null;
}
Queue<Node> queue = new LinkedList<>();
// List<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
// 取出队列头部的元素
Node curr = queue.poll();
if (i < size - 1) {
// 如果不是当前层的最后一个元素,那么将其next指向队列头部的元素
curr.next = queue.peek();
}
// 将当前节点的左右子节点加入队列
if (curr.left != null) {
queue.add(curr.left);
}
if (curr.right != null) {
queue.add(curr.right);
}
}
}
// 返回根节点
return root;
}
}
第二种做法,也很简洁,没有利用队列,利用上一行的链表,生成下一行的链表来实现的。
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {
}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
class Solution {
public Node connect(Node root) {
if (root == null) {
return null;
}
// 从根节点开始,每一行最左边的节点
Node leftMost = root;
while (leftMost != null) {
// 遍历一行的头指针
Node head = leftMost;
while (head != null) {
if (head.left != null) {
head.left.next = head.right;
}
if (head.right != null && head.next != null) {
head.right.next = head.next.left;
}
head = head.next;
}
leftMost = leftMost.left;
}
return root;
}
}
140. 单词拆分 II
给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意:词典中的同一个单词可能在分段中被重复使用多次。
示例 1:
输入:s = “catsanddog”, wordDict = [“cat”,“cats”,“and”,“sand”,“dog”]
输出:[“cats and dog”,“cat sand dog”]
示例 2:
输入:s = “pineapplepenapple”, wordDict = [“apple”,“pen”,“applepen”,“pine”,“pineapple”]
输出:[“pine apple pen apple”,“pineapple pen apple”,“pine applepen apple”]
解释: 注意你可以重复使用字典中的单词。
示例 3:
输入:s = “catsandog”, wordDict = [“cats”,“dog”,“sand”,“and”,“cat”]
输出:[]
提示:
1 <= s.length <= 20
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 10
s 和 wordDict[i] 仅有小写英文字母组成
wordDict 中所有字符串都 不同
思路较为清晰,主要就是利用了dfs的思想:
// dfs 思路
class Solution
{
void dfs(string s, vector<string> &res, int idx, string &curStr, unordered_set<string> &st)
{
if (idx == s.size())
{
res.push_back(curStr);
return;
}
for (int i = idx; i < s.size(); i++)
{
string str = s.substr(idx, i - idx + 1);
if (st.find(str) != st.end())
{
int pos = curStr.size();
if (pos > 0)
{
curStr.append(" ");
}
curStr.append(str);
dfs(s, res, i + 1, curStr, st);
curStr.erase(curStr.begin() + pos, curStr.end());
}
}
}
public:
vector<string> wordBreak(string s, vector<string> &wordDict)
{
unordered_set<string> st(wordDict.begin(), wordDict.end());
string curStr = "";
vector<string> res;
dfs(s, res, 0, curStr, st);
return res;
}
};
由于中间进行了大量的重复计算,可以采用记忆化搜索进行优化,如下:
// dfs + 记忆化搜搜
class Solution
{
unordered_set<string> wordSet;
unordered_map<int, vector<string>> res;
void backtrace(string &s, int index)
{
if (res.find(index) == res.end())
{
if (index == s.size())
{
res[index] = {""};
return;
}
res[index] = {}; // 将记录表置为空
for (int i = index + 1; i <= s.size(); i++)
{
string word = s.substr(index, i - index);
if (wordSet.find(word) != wordSet.end())
{
backtrace(s, i);
// 如果res[i]为空,就不会进入for循环
for (auto succ : res[i])
{
res[index].push_back(succ.empty() ? word : word + " " + succ);
}
}
}
}
}
public:
vector<string> wordBreak(string s, vector<string> &wordDict)
{
wordSet = unordered_set(wordDict.begin(), wordDict.end());
backtrace(s, 0);
return res[0];
}
};