目录
字符串相关
题目1:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc” OJ地址
图示如下。
方法一:传统的方法,依次将字符串的每个字符左移。
题目解析: 写去实现一个单次左旋的函数,其实就是一个字符串前移的函数,但是要提前将第一个字符保存起来。
注意:对于一个字符串而言,将这个字符串左旋字符串的长度次之后,其实这个字符串仍然是其本身,所以当左旋的次数大于字符串本身的长度时,就要对旋转的次数取模之后再进行左移。
编码如下。
class Solution {
public:
//字符串左旋一次
static void _reverse(string& str) {
char ch = str[0];
int i = 0;
while (i < str.size() - 1) {
str[i] = str[i + 1];
i++;
}
str[i] = ch;
}
string LeftRotateString(string str, int n) {
if (str.size() == 0 || n == 0) {
return str;
}
//对字符串的字符按照规则进行逆置
//注意逆置前要先对这个逆置的次数取模,因为左移字符串本身的长度之后,这个字符串还是本身
int num=n%str.size();
while (num > 0) {
_reverse(str);
num--;
}
return str;
}
};
这个方法有一个弊端,就是每一次的左旋都伴随着整个字符串的移动,时间复杂度过高,所以我们因此产生了第二种方法。
方法二:字符串逆置。
题目解析:左旋 n 次,可以先将前 n 个字符逆置,然后再将剩余的字符逆置,然后再将整体字符进行逆置。
编码如下。
class Solution {
public:
//逆置字符串
static void _reverse(string& str, int start, int end) {
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
string LeftRotateString(string str, int n) {
if (str.size() == 0 || n == 0) {
return str;
}
//对字符串的字符按照规则进行逆置
//注意逆置前要先对这个逆置的次数取模,因为左移字符串本身的长度之后,这个字符串还是本身
int num = n % str.size();
_reverse(str, 0, num - 1);
_reverse(str, num, str.size() - 1);
_reverse(str, 0, str.size() - 1);
return str;
}
};
题目2:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? OJ地址
图示如下。
题目解析: 这个题目其实和上一个题目类似,也是一个字符串反转的问题。通过空格将字符串分为了多个子字符串,我们应该先将每个子字符串进行反转,最终将整个字符串进行反转,就会得到最终的结果,所以我们一定要先找 ' ' 空格这个字符。
代码如下。
class Solution {
public:
static void _reverse(string& str, int start, int end) {
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
string ReverseSentence(string str) {
if (str.size() == 0) {
return str;
}
int begin = 0;
int end = begin;
int i = 0;
while (i < str.size()) {
while (i < str.size() && str[i] == ' ') {
i++;
}
begin = i;
while (i < str.size() && str[i] != ' ') {
i++;
}
end = i - 1 ;
if (begin < str.size() && end < str.size()) {
_reverse(str, begin, end);
}
}
//最后对整体字符串进行逆置
_reverse(str, 0, str.size() - 1);
return str;
}
};
二叉树相关
题目3:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三 行按照从左到右的顺序打印,其他行以此类推。OJ地址
图示如下。
题目解析:这道题目其实和二叉树的层序遍历息息相关,但是又区别于之前的层序遍历。因为之前的层序遍历我们只是将二叉树节点存储在了队列中,但是如果是之字形遍历,我们就不能单单的使用队列来存储节点,而是要使用栈和队列一起存储,队列起了辅助存储的作用,栈起到了之字形遍历的核心功能。 解决这个题目最重要的核心就是,当前层的遍历顺序,就是下一层的入队列和压栈的顺序。
编码如下。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > result;
if (pRoot == nullptr) {
return result;
}
queue<TreeNode*> q;
stack<TreeNode*> s;
//mode为1就是从左往右遍历,否则就是从右往左遍历
int mode = 1;
vector<int>v;
s.push(pRoot);
//访问栈顶的元素
while (!s.empty()) {
while (!s.empty()) {
TreeNode* node = s.top();
v.push_back(node->val);
s.pop();
//定义从左往右遍历,还是从右往左遍历
TreeNode* first = (mode == 1 ) ? node->left : node->right;
TreeNode* second = (mode == 1 ) ? node->right : node->left;
if (first) {
q.push(first);
}
if (second) {
q.push(second);
}
}
result.push_back(v);
v.clear();
mode = (mode == 1) ? 2 : 1;
//此时将队列中的元素入栈
while (!q.empty()) {
s.push(q.front());
q.pop();
}
}
return result;
}
};
题目4:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。OJ地址
图示如下。
题目解析: 其实搜索二叉树是有一个特点的,就是搜索二叉树的中序遍历序列是一个升序序列,所以我们可以先求出搜索二叉树的中序遍历序列,然后根据搜索二叉树的中序遍历序列,求出第 k 小个节点。
解决这个题目有两种方法,但是两种方法的区别其实仅仅就在于中序遍历方式一个是递归,一个是非递归。
方法一:递归法。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
static void LDR(TreeNode* proot, vector<int>& mid) {
if (proot == nullptr) {
return;
}
LDR(proot->left, mid);
mid.push_back(proot->val);
LDR(proot->right, mid);
}
int KthNode(TreeNode* proot, int k) {
if (proot == nullptr || k == 0) {
return -1;
}
vector<int> mid;
LDR(proot, mid);
if (mid.size() < k) {
return -1;
}
return mid[k - 1];
}
};
方法二:非递归。
编码如下。
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
int KthNode(TreeNode* proot, int k) {
if (proot == nullptr || k == 0) {
return -1;
}
TreeNode* node = proot;
stack<TreeNode*>s;
while (!s.empty() || node != nullptr) {
while (node) {
s.push(node);
node = node->left;
}
TreeNode* newnode = s.top();
s.pop();
k--;
if (k == 0) {
return newnode->val;
}
node = newnode->right;
}
return -1;
}
};
以上便是本期的所有内容。
本期内容到此结束^_^