2020.2 刷题笔记
剑指offer https://www.nowcoder.com/ta/coding-interviews?page=1
61-67
请实现两个函数,分别用来序列化和反序列化二叉树。
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 !表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {
string str = "";
queue q;
q.push(root);
for (bool flag = true;flag;) {
flag = false;
int count = q.size();
for (int i = 0;i < count;++i) {
TreeNode* now = q.front();
q.pop();
if (now) {
if (now->left || now->right) flag = true;
q.push(now->left);
q.push(now->right);
str += to_string(now->val) + "!";
}
else {
q.push(NULL);
q.push(NULL);
str += "#!";
}
}
}
char* cstr = new char[(int)str.size() + 1];
strcpy(cstr, str.c_str());
return cstr;
}
TreeNode* Deserialize(char *str) {
string sstr = str;
delete[] str;
if (sstr == "#!") return NULL;
vector nodes;
int pos = sstr.find('!', 0), pre = 0;
for (;pos != string::npos;pre = pos + 1, pos = sstr.find('!', pre)) {
string tmp = sstr.substr(pre, pos - pre);
if (tmp != "#") {
TreeNode* node = new TreeNode(atoi(tmp.c_str()));
nodes.push_back(node);
int size = nodes.size();
if (size > 1) {
if (size % 2 == 0) nodes[size / 2 - 1]->left = nodes[size - 1];
else nodes[size / 2 - 1]->right = nodes[size - 1];
}
}
else {
TreeNode* node = new TreeNode(0);
nodes.push_back(node);
}
}
return nodes[0];
}
};
我用的是层序遍历
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k) {
TreeNode* kNode = NULL;
FindKthNode(pRoot, kNode, k);
return kNode;
}
void FindKthNode(TreeNode* now, TreeNode*&kNode, int&k) {
if (now == NULL || k <= 0) return;
FindKthNode(now->left, kNode, k);
if (--k == 0) kNode = now;
if (k > 0) FindKthNode(now->right, kNode, k);
}
};
中序遍历二叉搜索树得到的就是非递减的序列
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
class Solution {
public:
void Insert(int num) {
nums.push_back(num);
}
double GetMedian() {
sort(nums.begin(), nums.end());
int size = nums.size();
if (size % 2 == 1) return nums[size / 2];
else return (nums[size / 2 - 1] + nums[size / 2]) / 2.;
}
vector nums;
};
另解:大顶堆和小顶堆,与PAT甲级-Stack类似:https://www.nowcoder.com/discuss/422
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个:{[2,3,4],2,6,2,5,1},{2,[3,4,2],6,2,5,1},{2,3,[4,2,6],2,5,1},{2,3,4,[2,6,2],5,1},{2,3,4,2,[6,2,5],1},{2,3,4,2,6,[2,5,1]}。
一、暴力穷举:
class Solution {
public:
vector maxInWindows(const vector& num, unsigned int size) {
vector maxs;
if (!size) return maxs;
for (int i = 0;i + size <= num.size();++i) {
int MAX = 0x80000000;
for (int j = 0;j < size && j + i < num.size();++j) MAX = max(MAX, num[i + j]);
maxs.push_back(MAX);
}
return maxs;
}
};
二、multiset:
class Solution {
public:
vector maxInWindows(const vector& num, unsigned int size) {
vector maxs;
if (!size || size > num.size()) return maxs;
multiset> now;
for (int i = 0;i < size;++i) now.insert(num[i]);
maxs.push_back(*now.begin());
for (int i = size;i < num.size();++i) {
now.erase(now.find(num[i - size]));
now.insert(num[i]);
maxs.push_back(*now.begin());
}
return maxs;
}
};
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str) {
if (*matrix == '\0') return false;
if (*str == '\0') return true;
visit.resize(rows, vector(cols, false));
for (int i = 0;i < rows;++i) {
for (int j = 0;j < cols;++j) {
if (matrix[i * cols + j] == str[0]) {
visit[i][j] = true;
if (Has(i, j, 1, matrix, rows, cols, str)) return true;
visit[i][j] = false;
}
}
}
return false;
}
bool Has(int i, int j, int now, char* matrix, int rows, int cols, char* str) {
if (str[now] == '\0') return true;
const int di[] = {0, 1, 0, -1}, dj[] = {1, 0, -1, 0};
for (int k = 0;k < 4;++k) {
int ii = i + di[k], jj = j + dj[k];
if (ii < 0 || ii >= rows || jj < 0 || jj >= cols) continue;
if (!visit[ii][jj] && matrix[ii * cols + jj] == str[now]) {
visit[ii][jj] = true;
if (Has(ii, jj, now + 1, matrix, rows, cols, str)) return true;
visit[ii][jj] = false;
}
}
return false;
}
vector > visit;
};
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
class Solution {
public:
int movingCount(int threshold, int rows, int cols) {
vector > visit(rows, vector(cols, false));
const int di[] = {0, 1}, dj[] = {1, 0}; // 向右,向下
int MAX = 1;
threshold >= 0 ? visit[0][0] = true : --MAX;
for (int i = 0;i < rows;++i) {
for (int j = 0;j < cols;++j) {
if (!visit[i][j]) continue;
for (int k = 0;k < 2;++k) {
int ii = i + di[k], jj = j + dj[k], sum = 0;
if (ii < 0 || ii >= rows || jj < 0 || jj >= cols || visit[ii][jj]) continue;
for (int iii = ii;iii && sum <= threshold;iii /= 10) sum += iii % 10;
for (int jjj = jj;jjj && sum <= threshold;jjj /= 10) sum += jjj % 10;
if (sum <= threshold) {
visit[ii][jj] = true;
++MAX;
}
}
}
}
return MAX;
}
};
注意和上一题不同,这题可以重复进入格子
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入描述:输入一个数n,意义见题面。(2 <= n <= 60)
class Solution {
public:
int cutRope(int number) {
int MAX = 1;
for (int last = 1;last < number;++last) MAX = max(MAX, last * Cut(number - last));
return MAX;
}
int Cut(int number) {
int MAX = number;
for (int last = 1;last < number;++last) MAX = max(MAX, last * Cut(number - last));
return MAX;
}
};