- 单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
双向BFS:使用两个set,分别从start和end两头开始BFS
每次选择较小的set开始BFS, 也就是将小的作为start,大的作为end
如果end中能找到start,就结束
否则,在访问set中加入访问记录,并加入到tmp中,作为子节点。
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> dict(wordList.begin(), wordList.end());
if (dict.find(endWord) == dict.end() ) return 0;
// 初始化起始和终点
unordered_set<string> beginSet, endSet, tmp, visited;
beginSet.insert(beginWord);
endSet.insert(endWord);
int len = 1;
while (!beginSet.empty() && !endSet.empty()){
if (beginSet.size() > endSet.size()){
tmp = beginSet;
beginSet = endSet;
endSet = tmp;
}
tmp.clear();
for ( string word : beginSet){
for (int i = 0; i < word.size(); i++){
char old = word[i];
for ( char c = 'a'; c <= 'z'; c++){
if ( old == c) continue;
word[i] = c;
if (endSet.find(word) != endSet.end()){
return len+1;
}
if (visited.find(word) == visited.end() && dict.find(word) != dict.end()){
tmp.insert(word);
visited.insert(word);
}
}
word[i] = old;
}
}
beginSet = tmp;
len++;
}
return 0;
}
};
- 最长连续序列
给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。
要求时间复杂度O(n),所以采用哈希表是很自然的选择。
class Solution {
public:
int longestConsecutive(vector<int>& nums)
{
if(nums.size()<2)
return nums.size();
unordered_set<int> s(nums.begin(),nums.end());
int res=1;
for(int num : s)
{
// 剪枝:若 num - 1 存在于hashset之中,那么num位于该段连续子序列的中间位置,
// 只有num位于这段子序列的左边界时,才能一次就统计到这段子序列的长度,避免多次统计该段子序列的子子序列
if(s.count(num - 1) != 0)
continue;
int len=1;
while(s.count(num+1)!=0)
{
len++;
num++;
}
res = max(res,len);
}
return res;
}
};
- 求根到叶子节点数字之和
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
很简单的题目,DFS即可
/**
* 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 {
int ret;
public:
int sumNumbers(TreeNode* root) {
if (root == NULL)
return 0;
getSum(root, 0);
return ret;
}
void getSum(TreeNode *root, int sum)
{
if (root == NULL)
return;
sum = sum * 10 + root->val;
if (root->left == NULL && root->right == NULL)
{
ret += sum;
return;
}
getSum(root->left, sum);
getSum(root->right, sum);
}
};
- 被围绕的区域
**方法:(DFS深度优先遍历) O(n2)
逆向考虑问题,我们先统计出哪些区域不会被攻占,然后将其它区域都变成’X’即可。
1.开一个二维布尔数组,记录哪些区域被遍历过。
2.枚举所有边界上的’O’,从该位置做深度优先遍历,只遍历是’O’的位置,并将所有遍历到的位置都标记成true。
3.将所有未遍历到的位置变成’X’。
**
class Solution {
public:
vector<vector<bool>> st;
int n,m;
void solve(vector<vector<char>>& board) {
if(board.empty() || board[0].empty()) return ;
n = board.size();
m = board[0].size();
//初始化二维标记数组为false
for(int i=0;i<n;i++){
vector<bool> tmp;
for(int j=0;j<m;j++){
tmp.push_back(false);
}
st.push_back(tmp);
}
//搜寻边界为O的区域,并标记为true
for(int i=0;i<n;i++){
if(board[i][0] =='O' ) dfs(board,i,0);
if(board[i][m-1] =='O') dfs(board,i,m-1);
}
for(int i=0;i<m;i++){
if(board[0][i] == 'O') dfs(board,0,i);
if(board[n-1][i] == 'O') dfs(board,n-1,i);
}
//未标记的全为X
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!st[i][j]){
board[i][j] = 'X';
}
}
}
}
void dfs(vector<vector<char>>& board,int x,int y){
st[x][y] = true;//标记该点已经搜寻过
int dx[4]={1,0,0,-1};
int dy[4]={0,1,-1,0};
for(int i=0;i<4;i++){
int tx = x+dx[i];
int ty = y+dy[i];
//st[tx][ty] = true;
if(tx>=0 && tx<board.size() && ty>=0 && ty<board[0].size() && !st[tx][ty] && board[tx][ty] == 'O'){
dfs(board,tx,ty);
}
}
}
};
- 分割回文串
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
本题采用回溯法求解。其实还可以优化:采取一个dp实现存储回文状态,然后根据该状态来直接判断子串是否为回文串,即空间换时间
class Solution {
vector<vector<string>> res;
int size;
bool check(const string&s, int i, int j) {
if (j < i) return true;
if (s[i++] == s[j--]) return check(s, i, j);
else return false;
}
void backtrack(const string& s, int ps, vector<string>& temp) {
if (ps >= size) {
res.push_back(temp);
return;
}
for (int i = ps; i < size; ++i) {
if (check(s, ps, i)) {
temp.push_back(s.substr(ps, i-ps+1));
backtrack(s, i+1, temp);
temp.pop_back();
}
}
}
public:
vector<vector<string>> partition(const string& s) {
size = s.size();
if (size == 0) return res;
vector<string> temp;
backtrack(s, 0, temp);
return res;
}
};
- 分割回文串2
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
分割次数最少,则其子串分割次数也是最少,因此可以想到用动态规划求解:如果这是回文串,那么到这里为止的划分次数可以为这个回文串前面所需的次数+1
class Solution
{
public:
int minCut(string s)
{
int l = s.length();
vector<int> list(l + 1); // list[i]代表前i个字符需要划几次,特别地,list[0]=-1
for(int i = 0; i < l + 1; ++i)
{ // 初始化[-1, 0, 1, 2, 3...]
list[i] = i - 1;
}
for(int i = 0; i < l; ++i)
{ // 以每个字符为中心找最长回文子串
list[i + 1] = min(list[i + 1], list[i] + 1); // 初始化,最坏情况下就比左边的多划一次
if(i == l - 1)
{ // 最后一个了没必要找了
break;
}
// 先找偶数个的
int start = i, end = i + 1;
while(s[start] == s[end])
{
list[end + 1] = min(list[end + 1], list[start] + 1);
if(end == l - 1 || start == 0)
{
break;
}
--start, ++end;
}
// 再找奇数个的
start = i - 1, end = i + 1;
if(start < 0)
{
continue;
}
while(s[start] == s[end])
{
list[end+1] = min(list[end + 1], list[start] + 1);
if(end == l-1 || start == 0)
{
break;
}
--start, ++end;
}
// 如果整个串都是回文串,那么就中断
if(list[l] == 0)
{
return 0;
}
}
return list[l];
}
};
- 克隆图
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
本题题目看起来比较复杂,实际其实就是一个DFS的递归拷贝而已,换一个概念为复制一棵树是不是就好理解了
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution
{
public:
Node* used[101]; //创建一个节点(指针)数组记录每个拷贝过的节点
Node* cloneGraph(Node* node)
{
if(!node)
return node; //如果是空指针,则返回空
if(used[node->val])
return used[node->val]; //该节点已经拷贝,直接返回该节点的指针即可
Node* p = new Node(node->val); //创建拷贝节点
used[node->val] = p; //递归会遍历每一个原有节点,然后将拷贝后的指针放入used
vector<Node*> tp = node->neighbors;
for(int i = 0; i < tp.size(); i++) //将该节点的邻接节点放入拷贝节点邻接数组
p->neighbors.push_back(cloneGraph(tp[i]));//递归实现每一个节点的更新
return p; //返回拷贝后的节点
}
};