给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false
class Solution {
public:
/*
把一个字母转换为大写或小写 异或32
*/
bool check(char c) //检查是不是字母和数字
{
return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}
bool isPalindrome(string s) {
for(int i = 0, j = s.size() - 1; i < j; i++, j--)
{
while(i < j && !check(s[i])) i ++;//不是字母和数字 下一位 check(s[i])
while(i < j && !check(s[j])) j --;
if(s[i] != s[j] && s[i] != (s[j]^32)) return false;
}
return true;
}
};
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
class Solution {
public:
//归并排序
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int k = m + n - 1;
m--, n--;
while(m >= 0 && n >= 0)
{
if(nums1[m] > nums2[n]) nums1[k--] = nums1[m--];
else nums1[k--] = nums2[n--];
}
while(n >= 0) nums1[k--] = nums2[n--];
}
};
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
// Forward declaration of isBadVersion API.
bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int l = 1, r = n;
while(l < r)
{
int mid = l + 0ll + r >> 1;
if(isBadVersion(mid)) r = mid;
else l = mid + 1;
}
return l;
}
};
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
/**
* 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:
bool isValidBST(TreeNode* root) {
if(!root) return true;
int minv, maxv;
return dfs(root, minv, maxv);
}
bool dfs(TreeNode* root, int &minv, int &maxv) //这里要改变值 用&
{
minv = maxv = root->val;
if(root->left)
{
int leftminv, leftmaxv;
if(!dfs(root->left, leftminv, leftmaxv)) return false;//左子树不是合法二叉搜索树
if(leftmaxv >= root->val) return false;
minv = leftminv;
}
if(root->right)
{
int rightminv, rightmaxv;
if(!dfs(root->right, rightminv, rightmaxv)) return false;
if(rightminv <= root->val) return false;
maxv = rightmaxv;
}
return true;
}
};
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用 next() 将返回二叉搜索树中的下一个最小的数。
示例:
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:
next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class BSTIterator {
public:
//用栈模拟非递归的中序遍历
stack<TreeNode*> stk;
BSTIterator(TreeNode* root) {
while(root)
{
stk.push(root);
root = root->left;
}
}
/** @return the next smallest number */
int next() {
auto t = stk.top();
stk.pop();
for(auto p = t->right; p; p = p->left) stk.push(p);
return t->val;
}
/** @return whether we have a next smallest number */
bool hasNext() {
return !stk.empty();
}
};
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator* obj = new BSTIterator(root);
* int param_1 = obj->next();
* bool param_2 = obj->hasNext();
*/
给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
class Solution {
public:
/*
分成两半 前面一半相乘 * 后面一半相乘
output[i]:0, 1, ..., i - 1, (i) , i + 1, i + 2, ... n - 1
*/
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> res(nums.size());
for(int i = 0, t = 1; i < nums.size(); i++)
{
res[i] = t; //t表示从0到i-1个数的乘积
// cout << "i =" << i << " " << res[i] << endl;
t *= nums[i];
// cout << t << endl;
}
for(int i = nums.size() - 1, t = 1; i >= 0; i--)
{
res[i] *= t;
t *= nums[i];
// cout << "i =" << i << " " << res[i] << endl;
// cout << t << endl;
}
return res;
}
};
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
/*
1.按照区间左端点从小到大排序,从前往后遍历区间,遍历时维护当前区间
[st, ed]
interval[i] 遍历的区间
1.
interval[i].start(左端点) > ed:
把[st, ed]添加到答案中去
令[st, ed] = interval[i]
2.interval[i].start < = ed:
ed = max(ed, interval[i].end)
*/
class Solution {
public:
//比较函数
static bool cmp(vector<int> &a, vector<int> &b)
{
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> res;
if(intervals.empty()) return res;
sort(intervals.begin(), intervals.end(), cmp);
int st = intervals[0][0], ed = intervals[0][1];
for(int i = 1; i < intervals.size(); i ++)
{
if(ed < intervals[i][0]) //intervals[i][0]
{
res.push_back({st, ed});
st = intervals[i][0], ed = intervals[i][1];
}
else ed = max(ed, intervals[i][1]);
}
res.push_back({st, ed});
return res;
}
};
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
class Solution {
public:
void sortColors(vector<int>& nums) {
int zero = 0 , second = nums.size() - 1;
for(int i = 0; i <= second; i++)//i <= second
{
while(nums[i] == 2 && i < second) swap(nums[i], nums[second --]);
if(nums[i] == 0) swap(nums[i], nums[zero ++]);
}
}
};
给定无向连通图中一个节点的引用,返回该图的深拷贝(克隆)。图中的每个节点都包含它的值 val(Int) 和其邻居的列表(list[Node])。
示例:
输入:
{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …"neighbors":[{"id”:“2”,“neighbors”:[{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"1"}̲,{"id”:“3”,“neighbors”:[{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"2"}̲,{"id”:“4”,“neighbors”:[{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"3"}̲,{"ref”:“1”}],“val”:4}],“val”:3}],“val”:2},{"$ref":“4”}],“val”:1}
解释:
节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
提示:
节点数介于 1 到 100 之间。
无向图是一个简单图,这意味着图中没有重复的边,也没有自环。
由于图是无向的,如果节点 p 是节点 q 的邻居,那么节点 q 也必须是节点 p 的邻居。
必须将给定节点的拷贝作为对克隆图的引用返回。
/*
1.先把每个点复制一遍,维护哈希表,可以从原点得到新点
2.遍历所有边(a,b)在新点之间,建立新的边(a',b')
/*
/*
// Definition for a Node.
class Node {
public:
int val;
vector neighbors;
Node() {}
Node(int _val, vector _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
unordered_map<Node*, Node*> hash;
Node* cloneGraph(Node* node) {
if(!node) return node;
//创建新点
auto p = new Node(node->val);
hash[node] = p;//可以从node得到新的点
dfs(node);
return p;
}
void dfs(Node* node) //遍历整个图
{
for(auto ver : node->neighbors)//遍历邻接节点
{
if(!hash.count(ver))//没有节点 先创建
{
hash[ver] = new Node(ver->val);
dfs(ver); //新创建节点 遍历
}
//连接新点
hash[node]->neighbors.push_back(hash[ver]);
}
}
};
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
public:
//双指针
string minWindow(string s, string t) {
string res;
unordered_map<char, int> S, T;//S维护区间当中的字母个数
//统计t里面每个字母出现次数
for(auto c : t) T[c] ++;
int total = T.size(); //t有多少个字母
int satisfy = 0; //当前满足了多少个字母
for(int i = 0, j = 0; i < s.size(); i++)
{
S[s[i]] ++;
if(S[s[i]] == T[s[i]]) satisfy ++;
while(S[s[j]] > T[s[j]]) S[s[j ++]] --;//先--后++ 有多余的字母
if(satisfy == total && (res.empty() || i - j + 1 < res.size()))//当前答案小于答案最小长度就更新
res = s.substr(j, i - j + 1);
}
return res;
}
};