- 图片平滑器
包含整数的二维矩阵 M 表示一个图片的灰度。你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个,则尽可能多的利用它们。
直接遍历比起前缀和等做法实测效率更高
class Solution {
public:
vector<vector<int>> imageSmoother(vector<vector<int>>& M) {
int m = M.size(), n = M.front().size();
vector<vector<int>> result(m, vector<int>(n));
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
int num = 0, sum = 0;
for (int _i = max(0, i-1); _i <= min(m-1, i+1); ++_i)
{
for (int _j = max(0, j-1); _j <= min(n-1, j+1); ++_j)
{
sum += M[_i][_j];
++num;
}
}
result[i][j] = sum / num;
}
}
return result;
}
};
- 二叉树最大宽度
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
其实就是层序遍历加了一个索引计算
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int widthOfBinaryTree(TreeNode* root) {
if(root == NULL) return 0;
queue<pair<TreeNode*, int>> q; //pair的第二个位置记录当前是第几个节点
q.push({
root, 1});
int width = 0;
while(!q.empty())
{
int count = q.size();
int start = q.front().second, index; //start是本层起点, index是本层当前遍历到的节点的索引
while(count--)
{
TreeNode *tmp = q.front().first;
index = q.front().second;
q.pop();
if(tmp->left) q.push({
tmp->left , index * 2 - start * 2}); //防止索引位置太大溢出
if(tmp->right) q.push({
tmp->right, index * 2 + 1 - start * 2});
}
width = max(width, index - start + 1);
}
return width;
}
};
- 奇怪的打印机
有台奇怪的打印机有以下两个特殊要求:
打印机每次只能打印同一个字符序列。
每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。
给定一个只包含小写英文字母的字符串,你的任务是计算这个打印机打印它需要的最少次数。
动态规划求解:dp[i][j]表示从第i个字符到第j个字符所需的最小打印次数。
递推式如下:dp[i][j] = min(dp[i][j], dp[i][j-1] + 1, dp[i][m-1] + dp[m][j]);, 其中第m个字符与第j个字符相同。
class Solution {
public:
int strangePrinter(string s) {
if (s.size() == 0) return 0;
//去除连续相同·字符
int index = 0;
string str;
while(index < s.size()) {
char ch = s[index++];
str += ch;
while(index < s.size() && s[index] == ch) {
index++;
}
}
int n = str.size();
//dp[i][j]表示从第i个字符到第j个字符(包括两端)需要的打印次数
vector<vector<int>> dp(n+1, vector<int>(n+1, -1));
return helper(1,n,dp,str);
}
int helper(int l, int r, vector<vector<int>>& dp, string& str) {
if (dp[l][r] != -1) return dp[l][r];
if (l > r) return dp[l][r] = 0;
int res = helper(l, r - 1, dp, str) + 1;
for (int m = l; m < r; ++m) {
if (str[m-1] == str[r-1]) {
res = min(res, helper(l, m - 1, dp, str) + helper(m, r - 1, dp, str));
}
}
return dp[l][r] = res;
}
};
- 非递减数列
给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
很简单的遍历,唯一的坑是需要判定是否可以修改成非递减序列
class Solution {
public:
bool checkPossibility(vector<int> &nums) {
int n = nums.size(), cnt = 0;
for (int i = 0; i < n - 1; ++i) {
int x = nums[i], y = nums[i + 1];
if (x > y) {
cnt++;
if (cnt > 1) {
return false;
}
if (i > 0 && y < nums[i - 1]) {
nums[i + 1] = x;
}
}
}
return true;
}
};
- 优美的排列2
给你两个整数 n 和 k ,请你构造一个答案列表 answer ,该列表应当包含从 1 到 n 的 n 个不同正整数,并同时满足下述条件:
假设该列表是 answer = [a1, a2, a3, … , an] ,那么列表 [|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] 中应该有且仅有 k 个不同整数。
返回列表 answer 。如果存在多种答案,只需返回其中 任意一种 。
先按照这种最小最大数相邻的方法排列,每排一个,k自减1,当k减到1的时候,后面的排列方法只要按照升序的方法排列,就不会产生不同的差的绝对值,这种算法的时间复杂度是O(n)。而且巧妙的是如果k是奇数则从前面最小数开始,否则k是偶数是从后面最大数开始的。
class Solution {
public:
vector<int> constructArray(int n, int k) {
assert(n > 0 && k > 0);
vector<int> res;
int i = 1;
int j = n;
while (i <= j) {
if (k > 1) {
// 按照交替i,j一小一大的方法生成前K-1个数
if (k % 2 != 0) {
res.push_back(i);
i++;
} else {
res.push_back(j);
j--;
}
k--;
} else {
// 按照生序的方法生成剩余排列
res.push_back(i);
i++;
}
}
return res;
}
};
- 乘法表中第K小的数
思路:二分法
1、找第k小,第k大一类的问题,一般情况下都是用二分来解决,那么二分法就需要找到二分的数据点以及比较方式
2、当前乘法表中最小值肯定是1,最大值为m*n,也就是我们这张表的最小和最大值,数据点找到
3、比较方式,要找到第k小,那么我们在拿到mid数据点的时候,去看当前mid的位置,如果mid位置大于k,说明mid值太大,需要再次进行二分求解 4、问题来了,这个位置怎么找呢,假设mid位于指定行i,那么mid/i也就是mid元素的列的位置,如果列的位置大于列最大长度n,那么说明该行所有数据都小于mid,累加n即可如果不是呢,说明找到了正确的行,只需要mid/i列的位置也就是这一行小于mid的元素个数
5、边界条件的处理是二分法中比较重要的,我们要找第k小,那么可以理解为假设当mid处于某个值时,满足了条件,我们需要锁定大值,也就是count>=k,去调整l的值不断逼近当l的值逼近到某个数据,使得条件再次满足时,就是循环条件结束的时候,这样可以保证l的值一定是在原列表中的,因为+1或-1都会导致条件不满足当然我们的>=条件也是不允许的,因为满足之后l值就被锁定了
class Solution {
public:
int findKthNumber(int m, int n, int k) {
int l = 1;
int h = m * n;
while(l < h) {
int mid = l + (h - l) / 2;
// 查找比mid小的元素个数
int count = 0;
for (int i = 1; i <= m; i++) {
count += (mid / i > n ? n : mid / i);
}
if (count >= k) {
h = mid;
} else {
l = mid + 1;
}
}
return l;
}
};
- 修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
分别处理根,左边,右边即可
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root) return nullptr;
//处理头结点,让root移动到[low,high]范围内,注意是左闭右闭
while(root->val < low || root->val > high)
{
if(root->val < low) root = root->right;//小于low往右走
else root = root->left; //大于high往左走
if(root == nullptr) break; //结束循环的条件
}
TreeNode* cur = root;
//此时root已经在[low,high]范围内,处理左孩子元素小于low的情况
while(cur != nullptr)
{
while(cur->left && cur->left->val < low)
{
cur->left = cur->left->right;
}
cur = cur->left;
}
cur = root;
//此时root已经在[low,high]范围内,处理右孩子大于high的情况
while(cur != nullptr)
{
while(cur->right && cur->right->val > high)
{
cur->right = cur->right->left;
}
cur = cur->right;
}
return root;
}
};