Onwaier's Blog

大步后退亦或停止不前,不如匍匐前进

0%

需要用到threshold来计算不同的无线传输模型的传输矩离,但使用gcc编译过程中出错。 首先进入ns文件夹下的indep-utils/propagation,我的路径是/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35/indep-utils/propagation 编辑命令为g++ -lm threshold.cc -o threshold

错误1 报错”threshold.cc:56:22: fatal error: iostream.h: 没有那个文件或目录”

找到#include<iostream.h>这一行,把#include<iostream.h>修改为#include<iostream>,并添加命令空间std,代码为using namespace std; 编译再次出现新的错误

错误2 报错”threshold.cc:211:30: error: ‘strcmp’ was not declared in this scope”

strcmp函数是关于字符串的操作,头文件为#include<string.h>,而文件中并未包含,加入#include<string.h>一行。 再次编译,没有报错,编译成功。 以Two Ray Ground,希望有效的传输距离为250m为例。 输入命令./threshold -m TwoRayGround 250 运行结果如图所示。 运行结果 执行命令ns test_2nodes.tcl出错,

错误3 报错”invalid command name “Agent/mUDP””

查询发现mUDP,mUdpSink,mTcpsink是NS2中没有的,是后来人写的。所以要使用此功能必须自行加入。下载mUDP, mUdpSink的文件,要下载的有下列几个文件 mudp.cc、mudp.h、mudpsink.cc、mudpsink.h。下载地址为:百度网盘,提取码为65j6个人网盘。 具体如何添加参照博客1博客2 下面以我的路径(ns在/home/onwaier)来说明配置过程 1. 在/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35目录下新建文件夹,添加刚才下载的文件mtcpsink.ccmtcpsink.hmudp.ccmudp.hmudpsink.ccmudpsink.h。 2. 修改/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35/common/packet.h,在struct hdr_cmn{}中添加以下代码

1
2
3
4
int frametype_; //added by smallko
double sendtime_; // added by smallko
unsigned int pkt_id_; // added by smallko
unsigned int frame_pkt_id_; //added by smallko
  1. 修改/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35/Makefile,找到OBJ_CC这一行, 在其下行添加代码measure/mtcpsink.o measure/mudp.o measure/mudpsink.o \。注意Makefile对于语法要求很高,不能有空行或多余的空格。
  2. 修改/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35/tcl/lib/ns-default.tcl,找到Delayer set debug_ false这一行,在其下一行添加Agent/mUDP set packetSize_ 1000
  3. 执行命令./configure --with-tcl-ver=8.5;make clean;make 出现新的错误。

错误4 报错”cannot call constructor mUdpAgent::UdpAgent’ directly [-fpermissive]“

  1. 在makefile中加入以下CCOPT = -Wall -Wno-write-strings -fpermissive
  2. 修改measure/mudp.cc 将代码
1
2
3
4
5
mUdpAgent::mUdpAgent() : id_(0), openfile(0)  
{
bind("packetSize_", &size_);
UdpAgent::UdpAgent();
}

改为

1
2
3
4
mUdpAgent::mUdpAgent() :UdpAgent(), id_(0), openfile(0)  
{
bind("packetSize_", &size_);
}

重新编译,运行通过。 但仍有上述问题invalid command name "Agent/mUDP" 尝试进入目录/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35,执行./install,然后进入目录/home/onwaier/ns-allinone-2.35_gcc482/ns-allinone-2.35/ns-2.35执行sudo make install。 再次执行命令ns test_2nodes.tcl,运行结果为: 执行结果

1266. 访问所有点的最小时间

题目描述

平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi]。请你计算访问所有这些点需要的最小时间(以秒为单位)。 你可以按照下面的规则在平面上移动: 每一秒沿水平或者竖直方向移动一个单位长度,或者跨过对角线(可以看作在一秒内向水平和竖直方向各移动一个单位长度)。 必须按照数组中出现的顺序来访问这些点。 示例 1: 输入:points = [[1,1],[3,4],[-1,0]] 输出:7 解释:一条最佳的访问路径是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0] 从 [1,1] 到 [3,4] 需要 3 秒 从 [3,4] 到 [-1,0] 需要 4 秒 一共需要 7 秒 示例 2: 输入:points = [[3,2],[-2,2]] 输出:5 提示: points.length == n 1 <= n <= 100 points[i].length == 2 -1000 <= points[i][0], points[i][1] <= 1000

题解1

相邻两点间访问时,如果横坐标相等或者纵坐标相等,直接沿竖直方向或水平方向移动一个单位长度。如果不相等,则先沿对角线移动使横坐标或纵坐标相等,然后再水平或垂直移动。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int minTimeToVisitAllPoints(vector<vector<int>>& points) {
int sum = 0, len = points.size();
for(int i = 1; i < len; ++i){
if(points[i][0] == points[i - 1][0]){//横坐标相等
sum += abs(points[i][1] - points[i - 1][1]);
}
else if(points[i][1] == points[i - 1][1]){//纵坐标相等
sum += abs(points[i][0] - points[i - 1][0]);
}
else{
if(abs(points[i][0] - points[i - 1][0]) < abs(points[i][1] - points[i - 1][1])){//纵
sum = sum + abs(points[i][1] - points[i - 1][1]);
}
else{
sum = sum + abs(points[i][0] - points[i - 1][0]);
}
}
}
return sum;
}
};

题解2

仔细观察代码1发现,相邻两点的访问时间等于横纵坐标差值的绝对值较大的一个。

代码2

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
int minTimeToVisitAllPoints(vector<vector<int>>& points) {
int sum = 0, len = points.size();
for(int i = 1; i < len; ++i){
sum = sum + max(abs(points[i][0] - points[i - 1][0]), abs(points[i][1] - points[i - 1][1]));
}
return sum;
}
};

1267. 统计参与通信的服务器

题目描述

这里有一幅服务器分布图,服务器的位置标识在 m * n 的整数矩阵网格 grid 中,1 表示单元格上有服务器,0 表示没有。 如果两台服务器位于同一行或者同一列,我们就认为它们之间可以进行通信。 请你统计并返回能够与至少一台其他服务器进行通信的服务器的数量。 示例 1: 输入:grid = [[1,0],[0,1]] 输出:0 解释:没有一台服务器能与其他服务器进行通信。 示例 2: 输入:grid = [[1,0],[1,1]] 输出:3 解释:所有这些服务器都至少可以与一台别的服务器进行通信。 示例 3: 输入:grid = [[1,1,0,0],[0,0,1,0],[0,0,1,0],[0,0,0,1]] 输出:4 解释:第一行的两台服务器互相通信,第三列的两台服务器互相通信,但右下角的服务器无法与其他服务器通信。 提示: m == grid.length n == grid[i].length 1 <= m <= 250 1 <= n <= 250 grid[i][j] == 0 or 1

题解1

判断每台服务器所在行或者列除了它自己外,有没有其它的服务器,有的话表示可以通信,计数+1。否则,无法通信。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
int countServers(vector<vector<int>>& grid) {
int rows = grid.size(), cols = grid[0].size(), cnt = 0;
for(int i = 0; i < rows; ++i){
for(int j = 0; j < cols; ++j){
if(grid[i][j]){
//行
bool flag = false;
for(int k = 0; k < cols; ++k){
if(k != j && grid[i][k]){
flag = true;
break;
}
}
//列
for(int k = 0; k < rows; ++k){
if(k != i && grid[k][j]){
flag = true;
break;
}
}
if(flag){
++cnt;
}
}
}
}
return cnt;
}
};

1268. 搜索推荐系统

题目描述

给你一个产品数组 products 和一个字符串 searchWord ,products 数组中每个产品都是一个字符串。 请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。 请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。 示例 1: 输入:products = [“mobile”,”mouse”,”moneypot”,”monitor”,”mousepad”], searchWord = “mouse” 输出:[ [“mobile”,”moneypot”,”monitor”], [“mobile”,”moneypot”,”monitor”], [“mouse”,”mousepad”], [“mouse”,”mousepad”], [“mouse”,”mousepad”] ] 解释:按字典序排序后的产品列表是 [“mobile”,”moneypot”,”monitor”,”mouse”,”mousepad”] 输入 m 和 mo,由于所有产品的前缀都相同,所以系统返回字典序最小的三个产品 [“mobile”,”moneypot”,”monitor”] 输入 mou, mous 和 mouse 后系统都返回 [“mouse”,”mousepad”] 示例 2: 输入:products = [“havana”], searchWord = “havana” 输出:[[“havana”],[“havana”],[“havana”],[“havana”],[“havana”],[“havana”]] 示例 3: 输入:products = [“bags”,”baggage”,”banner”,”box”,”cloths”], searchWord = “bags” 输出:[[“baggage”,”bags”,”banner”],[“baggage”,”bags”,”banner”],[“baggage”,”bags”],[“bags”]] 示例 4: 输入:products = [“havana”], searchWord = “tatiana” 输出:[[],[],[],[],[],[],[]] 提示: 1 <= products.length <= 1000 1 <= products[i].length <= 2 * 10^4 products[i] 中所有的字符都是小写英文字母。 1 <= searchWord.length <= 1000 searchWord 中所有字符都是小写英文字母。

题解1

首先需将products中的字符串按照字典序排序,然后依次寻找seachWord每个字母后与之匹配的product(最多三个)。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
vector<vector<string>> suggestedProducts(vector<string>& prod, string searchWord) {
sort(prod.begin(), prod.end());
vector<vector<string>>res;
vector<string>tmp;
for(int i = 0; i < searchWord.size(); ++i){//以每个字母前的子串进行匹配
string s = searchWord.substr(0, i + 1);
tmp.clear();
int cnt = 0;
for(int j = 0; j < prod.size(); ++j){
if(cnt < 3 && prod[j].substr(0, i + 1) == s){//匹配且最多只能有三个
tmp.push_back(prod[j]);
++cnt;
}
}
res.push_back(tmp);
}
return res;
}
};

1269. 停在原地的方案数

题目描述

有一个长度为 arrLen 的数组,开始有一个指针在索引 0 处。 每一步操作中,你可以将指针向左或向右移动 1 步,或者停在原地(指针不能被移动到数组范围外)。 给你两个整数 steps 和 arrLen ,请你计算并返回:在恰好执行 steps 次操作以后,指针仍然指向索引 0 处的方案数。 由于答案可能会很大,请返回方案数 模 10^9 + 7 后的结果。 示例 1: 输入:steps = 3, arrLen = 2 输出:4 解释:3 步后,总共有 4 种不同的方法可以停在索引 0 处。 向右,向左,不动 不动,向右,向左 向右,不动,向左 不动,不动,不动 示例 2: 输入:steps = 2, arrLen = 4 输出:2 解释:2 步后,总共有 2 种不同的方法可以停在索引 0 处。 向右,向左 不动,不动 示例 3: 输入:steps = 4, arrLen = 2 输出:8 提示: 1 <= steps <= 500 1 <= arrLen <= 10^6

题解1

如果进行dfs搜索,会超时。我们进行记忆化搜索,将已经计算过的存储下来,避免重复遍历与计算, 大大减少时间。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#define MOD (1000000007)

/*
记忆化搜索
dp[j][i] = dp[j - 1][i - 1] + dp[j - 1][j] + dp[j - 1][i + 1]
*/

map<pair<int, int>, int>mark;
int dfs(int pos, int steps, int n){
if(steps == 0){
return pos == 0?1:0;
}
if(mark.find(make_pair(pos, steps - 1))!= mark.end()){
mark[make_pair(pos, steps)] = mark[make_pair(pos, steps - 1)] % MOD;
}
else{
mark[make_pair(pos, steps)] = dfs(pos, steps - 1, n) % MOD;
}
if(pos - 1 >= 0){//左移
if(mark.find(make_pair(pos - 1, steps - 1)) != mark.end()){
mark[make_pair(pos, steps)] = (mark[make_pair(pos, steps)] + mark[make_pair(pos - 1, steps - 1)]) % MOD;
}
else{
mark[make_pair(pos, steps)] = (mark[make_pair(pos, steps)] + dfs(pos - 1, steps - 1, n)) % MOD;
}
}
if(pos + 1 < n){
if(mark.find(make_pair(pos + 1, steps - 1)) != mark.end()){
mark[make_pair(pos, steps)] = (mark[make_pair(pos, steps)] + mark[make_pair(pos + 1, steps - 1)]) % MOD;
}
else{
mark[make_pair(pos, steps)] = (mark[make_pair(pos, steps)] + dfs(pos + 1, steps - 1, n)) % MOD;
}
}
return mark[make_pair(pos, steps)];
}
class Solution {
public:

int numWays(int steps, int arrLen) {
mark.clear();
return dfs(0, steps, arrLen);
}
};

题解2

动态规划 $dp(pos, steps) = dp(pos- 1, steps - 1) + dp(pos. steps - 1) + dp(pos + 1, steps - 1)$ 其中dp(pos, steps)表示从0出发,最后经过steps步回到pos的方案数,而对于每一步有三种方案:向左移,停在原地,向右移。由此得到转移方程。

代码2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define MOD (1000000007)

class Solution {
public:
int numWays(int steps, int arrLen) {
vector<vector<int>>dp(505, vector<int>(505, 0));
dp[0][0] = 1;
for(int i = 1; i <= steps; ++i){
for(int j = 0; j <= min(arrLen - 1, i); ++j){
if(j - 1 >= 0){
dp[j][i] = (dp[j][i] + dp[j - 1][i - 1]) % MOD;
}
if(j + 1 <= i){
dp[j][i] = (dp[j][i] + dp[j + 1][i - 1]) % MOD;
}
dp[j][i] = (dp[j][i] + dp[j][i - 1]) % MOD;
}
}
return dp[0][steps];
}
};

1260. 二维网格迁移

题目描述

给你一个 n 行 m 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。 每次「迁移」操作将会引发下述活动: 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]。 位于 grid[i][m - 1] 的元素将会移动到 grid[i + 1][0]。 位于 grid[n - 1][m - 1] 的元素将会移动到 grid[0][0]。 请你返回 k 次迁移操作后最终得到的 二维网格。 示例 1: 输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1 输出:[[9,1,2],[3,4,5],[6,7,8]] 示例 2: 输入:grid = [[3,8,1,9],[19,7,2,5],[4,6,11,10],[12,0,21,13]], k = 4 输出:[[12,0,21,13],[3,8,1,9],[19,7,2,5],[4,6,11,10]] 示例 3: 输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 9 输出:[[1,2,3],[4,5,6],[7,8,9]] 提示: 1 <= grid.length <= 50 1 <= grid[i].length <= 50 -1000 <= grid[i][j] <= 1000 0 <= k <= 100

题解1

之前做过一个一维数组元素平移,最后面的移到前面。比赛时首先想到的是先把二维数组转成一维数组,然后平移元素。注意平移周期为数组长度。可以先对平移长度取模。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
时间复杂度为o(m * n)
空间复杂度为o(m * n)
*/
class Solution {
public:
vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
vector<int>tmp;
int rows = grid.size(), cols = grid[0].size();
for(int i = 0; i < grid.size(); ++i){//将二维数组转成一维数组
for(int j = 0; j < grid[i].size(); ++j){
tmp.push_back(grid[i][j]);
}
}
int len = tmp.size();
k = k % len;
for(int i = 0; i < len; ++i){//对元素进行平移,不过这里是逆向思维
grid[i / cols][i % cols] = tmp[(i - k + len) % len];
}
return grid;
}
};

1261. 在受污染的二叉树中查找元素

题目描述

给出一个满足下述规则的二叉树: root.val == 0 如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1 如果 treeNode.val == x 且 treeNode.right != null,那么 treeNode.right.val == 2 * x + 2 现在这个二叉树受到「污染」,所有的 treeNode.val 都变成了 -1。 请你先还原二叉树,然后实现 FindElements 类: FindElements(TreeNode* root) 用受污染的二叉树初始化对象,你需要先把它还原。 bool find(int target) 判断目标值 target 是否存在于还原后的二叉树中并返回结果。 示例 1: 输入: [“FindElements”,”find”,”find”] [[[-1,null,-1]],[1],[2]] 输出: [null,false,true] 解释: FindElements findElements = new FindElements([-1,null,-1]); findElements.find(1); // return False findElements.find(2); // return True 示例 2: 输入: [“FindElements”,”find”,”find”,”find”] [[[-1,-1,-1,-1,-1]],[1],[3],[5]] 输出: [null,true,true,false] 解释: FindElements findElements = new FindElements([-1,-1,-1,-1,-1]); findElements.find(1); // return True findElements.find(3); // return True findElements.find(5); // return False 示例 3: 输入: [“FindElements”,”find”,”find”,”find”,”find”] [[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]] 输出: [null,true,false,false,true] 解释: FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]); findElements.find(2); // return True findElements.find(3); // return False findElements.find(4); // return False findElements.find(5); // return True 提示: TreeNode.val == -1 二叉树的高度不超过 20 节点的总数在 [1, 10^4] 之间 调用 find() 的总次数在 [1, 10^4] 之间 0 <= target <= 10^6

题解1

题目大意是已经给出二叉树的结构,但是结点值不正确,首先根据给定的规则,对二叉树节点重新赋值。然后写出find函数。 对于二叉树的构建及find函数常见的方法是递归的方式,但是后来我发现find的传入参数不是没有root。实现有点麻烦,后来用的是队列实现的。类似于BFS

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class FindElements {
public:
TreeNode *res;
FindElements(TreeNode* root) {
res = root;
if(res == NULL){
return;
}
queue<TreeNode*>que;
TreeNode* frt;
res->val = 0;
que.push(res);
while(!que.empty()){//BFS为节点赋值
frt = que.front();
que.pop();
if(frt->left != NULL){
frt->left->val = frt->val * 2 + 1;
que.push(frt->left);
}
if(frt->right != NULL){
frt->right->val = frt->val * 2 + 2;
que.push(frt->right);
}
}
}
bool find(int target) {
if(res == NULL){
return false;
}
else{
queue<TreeNode*>que;
TreeNode* frt;
que.push(res);
while(!que.empty()){//BFS遍历节点,来寻找节点
frt = que.front();
que.pop();
if(frt->val == target){
return true;
}
if(frt->left != NULL){
que.push(frt->left);
}
if(frt->right != NULL){
que.push(frt->right);
}
}
return false;
}
}
};

/**
* Your FindElements object will be instantiated and called as such:
* FindElements* obj = new FindElements(root);
* bool param_1 = obj->find(target);
*/

1262. 可被三整除的最大和

题目描述

给你一个整数数组 nums,请你找出并返回能被三整除的元素最大和。 示例 1: 输入:nums = [3,6,5,1,8] 输出:18 解释:选出数字 3, 6, 1 和 8,它们的和是 18(可被 3 整除的最大和)。 示例 2: 输入:nums = [4] 输出:0 解释:4 不能被 3 整除,所以无法选出数字,返回 0。 示例 3: 输入:nums = [1,2,3,4,4] 输出:12 解释:选出数字 1, 3, 4 以及 4,它们的和是 12(可被 3 整除的最大和)。 提示: 1 <= nums.length <= 4 * 10^4 1 <= nums[i] <= 10^4

题解1

这个题比赛的时候,思路没理清,导致写出来的代码bug多,赛后想了感觉很简单。 1. 所有元素排序 2. 求所有元素之和对3的余数res,期间各存下余数为1和余数为2的最小的2个数(4个数) 3. res = 0的话,被三整除的最大和为元素和。res=1的话需要去除1个余数为1的数或2个余数为2的数,需要比较一个,选择和大的一个。res=2的话需要去除2个余数为1的数或1个余数为2的数,同样比较取和较大的一种方案

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
int maxSumDivThree(vector<int>& nums) {
sort(nums.begin(), nums.end());

int sum = 0 , mod = 0, len = nums.size(), res;
vector<int>v1, v2;
for(int i = 0; i < len; ++i){
sum = sum + nums[i];
mod = (mod + nums[i]) % 3;
if(v1.size() < 2 && nums[i] % 3 == 1){//存储至多2个最小的余数为1的
v1.push_back(nums[i]);
}
else if(v2.size() < 2 && nums[i] % 3 == 2){//存储至多2个最小的余数为2的
v2.push_back(nums[i]);
}
}
if(mod == 0){//刚好余数为0
res = sum;
}
else if(mod == 1){//需去除1个余数为1的或2个余数为2的
int num1 = sum - (v1.size() > 0?v1[0]:sum);
int num2 = sum - (v2.size() > 1?v2[0] + v2[1]:sum);
res = max(num1, num2);
}
else{//需去除2个余数为1的或者1个余数为2的
int num1 = sum - (v1.size() > 1?v1[0] + v1[1]:sum);
int num2 = sum - (v2.size() > 0?v2[0]:sum);
res = max(num1, num2);
}
return res;
}
};

1263. 推箱子

题目描述

「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。 游戏地图用大小为 n * m 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。 现在你将作为玩家参与游戏,按规则将箱子 ‘B’ 移动到目标位置 ‘T’ : 玩家用字符 ‘S’ 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。 地板用字符 ‘.’ 表示,意味着可以自由行走。 墙用字符 ‘#’ 表示,意味着障碍物,不能通行。 箱子仅有一个,用字符 ‘B’ 表示。相应地,网格上有一个目标位置 ‘T’。 玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。 玩家无法越过箱子。 返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1。 示例 1: 输入:grid = [[“#”,”#”,”#”,”#”,”#”,”#”], [“#”,”T”,”#”,”#”,”#”,”#”], [“#”,”.”,”.”,”B”,”.”,”#”], [“#”,”.”,”#”,”#”,”.”,”#”], [“#”,”.”,”.”,”.”,”S”,”#”], [“#”,”#”,”#”,”#”,”#”,”#”]] 输出:3 解释:我们只需要返回推箱子的次数。 示例 2: 输入:grid = [[“#”,”#”,”#”,”#”,”#”,”#”], [“#”,”T”,”#”,”#”,”#”,”#”], [“#”,”.”,”.”,”B”,”.”,”#”], [“#”,”#”,”#”,”#”,”.”,”#”], [“#”,”.”,”.”,”.”,”S”,”#”], [“#”,”#”,”#”,”#”,”#”,”#”]] 输出:-1 示例 3: 输入:grid = [[“#”,”#”,”#”,”#”,”#”,”#”], [“#”,”T”,”.”,”.”,”#”,”#”], [“#”,”.”,”#”,”B”,”.”,”#”], [“#”,”.”,”.”,”.”,”.”,”#”], [“#”,”.”,”.”,”.”,”S”,”#”], [“#”,”#”,”#”,”#”,”#”,”#”]] 输出:5 解释:向下、向左、向左、向上再向上。 示例 4: 输入:grid = [[“#”,”#”,”#”,”#”,”#”,”#”,”#”], [“#”,”S”,”#”,”.”,”B”,”T”,”#”], [“#”,”#”,”#”,”#”,”#”,”#”,”#”]] 输出:-1 提示: 1 <= grid.length <= 20 1 <= grid[i].length <= 20 grid 仅包含字符 ‘.’, ‘#’, ‘S’ , ‘T’, 以及 ‘B’。 grid 中 ‘S’, ‘B’ 和 ‘T’ 各只能出现一个。

题解1

参照题解 如果只有箱子没有人,只是将箱子移到目标位置,就是常见的BFS问题。而这个题有两个对象,箱子和人,要保证箱子能被推到目标位置。加入队列的信息除了箱子的坐标还有人的坐标,随着人的移动来判断人是否能走到箱子的位置,能表示箱子移动,步数加1;否则只是人的位置发生移动,此时步数并未增加。此外,标记对象也发生改变,以前只是箱子这个二元组,现在变成了人和箱子这个四元组。因为人移动带来的步数并不一定增长,这里需要用到优先队列,每次选取步数最小的来移动。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
struct cmp{
bool operator()(vector<int>v1, vector<int>v2){//定义优先级别
return v1[0] > v2[0];
}
};
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//定义移动方向
class Solution {
public:
int minPushBox(vector<vector<char>>& grid) {
set<vector<int> >mark;//用set来标记
int rows = grid.size(), cols = grid[0].size();
int x1, x2, y1, y2, x3, y3, next_x, next_y, dist;
vector<int>v;
priority_queue<vector<int>, vector<vector<int>>, cmp>que;//定义优先队列
for(int i = 0; i < rows; ++i){
for(int j = 0; j < cols; ++j){
if(grid[i][j] == 'S'){//玩家
x1 = i;
y1 = j;
}
else if(grid[i][j] == 'B'){//箱子
x2 = i;
y2 = j;
}
else if(grid[i][j] == 'T'){//目标
x3 = i;
y3 = j;
}
}
}
que.push({0, x1, y1, x2, y2});//初始化
mark.insert({x1, y1, x2, y2});
while(!que.empty()){
v = que.top();
que.pop();
if(v[3] == x3 && v[4] == y3){
return v[0];
}
for(int i = 0; i < 4; ++i){
x1 = v[1] + dir[i][0];
y1 = v[2] + dir[i][1];
dist = v[0];
if(x1 < 0 x1 >= rows y1 < 0 y1 >= cols){
continue;
}
if(grid[x1][y1] != '#'){
if(x1 == v[3] && y1 == v[4]){
x2 = v[3] + dir[i][0];
y2 = v[4] + dir[i][1];
if(x2 >= 0 && x2 < rows && y2 >= 0 && y2 < cols && grid[x2][y2] != '#'){
if(mark.find({x1, y1, x2, y2}) == mark.end()){//未访问过
mark.insert({x1, y1, x2, y2});
que.push({dist + 1, x1, y1, x2, y2});
}
}
}
else{
if(mark.find({x1, y1, v[3], v[4]}) == mark.end()){//未访问过
mark.insert({x1, y1, v[3], v[4]});
que.push({dist, x1, y1, v[3], v[4]});
}
}
}
}
}
return -1;
}
};

1256. 加密数字

题目描述

给你一个非负整数 num ,返回它的「加密字符串」。 加密的过程是把一个整数用某个未知函数进行转化,你需要从下表推测出该转化函数: 题目描述1 示例 1: 输入:num = 23 输出:”1000” 示例 2: 输入:num = 107 输出:”101100” 提示: 0 <= num <= 10^9

题解1

我发现的规律1-2 编码位数为1位从0 - 1,3-6编码位数为2位从00 - 11。7-14编码位数为3位从000 - 111。所以从这个规律中我需要知道2个信息编码的位数及在编码中位置(即第几个)。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
string encode(int num) {
long long tmp = 0, sum = 0, res;
int bit = 0;
string s;
if(num == 0){//0的话为空串
return "";
}
while(sum <= num){//sum = 2 ^ (bit) -1 寻找2^bit - 1 > num
if(bit == 0){
tmp = 1;
}
else
tmp = tmp << 1;
bit++;
sum += tmp;
}
res = num - (sum - tmp);//num在bit-1位中编码的位置
for(int i = 0; i < bit - 1; ++i){
s.push_back(res % 2 + '0');
res = res >> 1;
}
reverse(s.begin(), s.end());//逆序
return s;
}
};

题解2

参考圈子中的讨论,其实一个数num的编码为num+1的二进制数的低bit-1位(除去最高位)。 比如num = 6的编码为11相当于7(111)的后两位。 其实反过来题解1可以从侧面证实这种解法的正确性,最后得到的res与num存在以下关系 $latex num = res + 2^{bit - 1} - 1$变化成$latex num + 1 = res + 2^{bit - 1}$ res显然是num + 1的后bit-1位

代码2

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
string encode(int num) {
string s;
int res = num + 1;
while(res){
s.push_back((res & 1) + '0');
res = res >> 1;
}
reverse(s.begin(), s.end());//逆序
return s.substr(1);//只输出 低bit-1位
}
};

1257. 最小公共区域

题目描述

给你一些区域列表 regions ,每个列表的第一个区域都包含这个列表内所有其他区域。 很自然地,如果区域 X 包含区域 Y ,那么区域 X 比区域 Y 大。 给定两个区域 region1 和 region2 ,找到同时包含这两个区域的 最小 区域。 如果区域列表中 r1 包含 r2 和 r3 ,那么数据保证 r2 不会包含 r3 。 数据同样保证最小公共区域一定存在。 示例 1: 输入: regions = [[“Earth”,”North America”,”South America”], [“North America”,”United States”,”Canada”], [“United States”,”New York”,”Boston”], [“Canada”,”Ontario”,”Quebec”], [“South America”,”Brazil”]], region1 = “Quebec”, region2 = “New York” 输出:”North America” 提示: 2 <= regions.length <= 10^4 region1 != region2 所有字符串只包含英文字母和空格,且最多只有 20 个字母。

题解1

根据regions建立并查集 然后将region1与region2的所有父结点(包括它们自已)放到v1与v2中。 寻找v1与v2的第一个相交元素即为所示。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
map<string, string>maps;
string findSmallestRegion(vector<vector<string>>& regions, string region1, string region2) {
vector<string>v1, v2;
for(int i = 0; i < regions.size(); ++i){//建立并查集
string fa = regions[i][0];
for(int j = 1; j < regions[i].size(); ++j){
maps[regions[i][j]] = fa;
}
}
//将region1与region2的所有父结点对应放到v1与v2中。
//然后寻找v1与v2第1个相交的元素
v1.push_back(region1);
v2.push_back(region2);
string tmp = region1;
while(maps.find(tmp) != maps.end()){
tmp = maps[tmp];
v1.push_back(tmp);
}
tmp = region2;
while(maps.find(tmp) != maps.end()){
tmp = maps[tmp];
v2.push_back(tmp);
}
for(int i = 0; i < v1.size(); ++i){
if(find(v2.begin(), v2.end(), v1[i]) != v2.end()){
return v1[i];
}
}
return "";
}
};

1258. 近义词句子

题目描述

给你一个近义词表 synonyms 和一个句子 text , synonyms 表中是一些近义词对 ,你可以将句子 text 中每个单词用它的近义词来替换。 请你找出所有用近义词替换后的句子,按 字典序排序 后返回。 示例 1: 输入: synonyms = [[“happy”,”joy”],[“sad”,”sorrow”],[“joy”,”cheerful”]], text = “I am happy today but was sad yesterday” 输出: [“I am cheerful today but was sad yesterday”, “I am cheerful today but was sorrow yesterday”, “I am happy today but was sad yesterday”, “I am happy today but was sorrow yesterday”, “I am joy today but was sad yesterday”, “I am joy today but was sorrow yesterday”] 提示: 0 <= synonyms.length <= 10 synonyms[i].length == 2 synonyms[0] != synonyms[1] 所有单词仅包含英文字母,且长度最多为 10 。 text 最多包含 10 个单词,且单词间用单个空格分隔开。

题解1

题意比较简单,但是比较麻烦。 首先根据近义词对,将所有近义的放到同一个set中 然后将句子text转成单词数组,遍历单词,对每一个单词,在近义词中查找看是否存在近义词,如果存在,则将近义词组中的所有单词添加到v中。 最后通过DFS来生成所有近义的句子。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
vector<string>res, words;
string tmp;
vector<vector<string>>v(15);
void generate(int idx, int n){//DFS生成句子
if(idx >= n){
tmp.clear();
for(int i = 0; i < n; ++i){
if(i == 0){
tmp.append(words[i]);
}
else{
tmp.append(" ");
tmp.append(words[i]);
}
}
res.push_back(tmp);
return;
}
for(auto str:v[idx]){
words.push_back(str);
generate(idx + 1, n);
words.pop_back();
}
}

class Solution {
public:
vector<string> generateSentences(vector<vector<string>>& sy, string text) {
set<string>sets[15];
for(int i = 0; i < 15; ++i){//全局变量需要重置,不然执行结果与本地不相同
v[i].clear();
}
res.clear();
words.clear();
int idx = 0, cnt = -1;
vector<string>sen;
for(int i = 0; i < sy.size(); ++i){//根据同义词组,将同义的放到同一个set中
if(i == 0){
++cnt;
idx =cnt;
}
else{
idx = -1;
for(int j = 0; j <= cnt; ++j){
if(sets[j].find(sy[i][0]) != sets[j].end() sets[j].find(sy[i][1]) != sets[j].end()){
idx = j;
}
}
if(idx == -1){
++cnt;
idx = cnt;
}
}
for(auto str:sy[i]){
sets[idx].insert(str);
}
}
istringstream s(text);
string str;
while(s >> str){//句子拆成单词
sen.push_back(str);
}
for(int i = 0; i < sen.size(); ++i){
bool flag = false;
for(int j = 0; j <= cnt; ++j){
if(sets[j].find(sen[i]) != sets[j].end()){//如果同义词组中找到该词,则将该同义词组中所有元素放到v[i]中
flag = true;
for(auto iter :sets[j]){
v[i].push_back(iter);
}
break;
}
}
if(!flag){
v[i].push_back(sen[i]);
}
}
generate(0, sen.size());//DFS生成句子
return res;
}
};

1259. 不相交的握手

题目描述

偶数 个人站成一个圆,总人数为 num_people 。每个人与除自己外的一个人握手,所以总共会有 num_people / 2 次握手。 将握手的人之间连线,请你返回连线不会相交的握手方案数。 由于结果可能会很大,请你返回答案 模 10^9+7 后的结果。 示例 1: 输入:num_people = 2 输出:1 示例 2: 输入:num_people = 4 输出:2 解释:总共有两种方案,第一种方案是 [(1,2),(3,4)] ,第二种方案是 [(2,3),(4,1)] 。 示例 3: 输入:num_people = 6 输出:5 示例 4: 输入:num_people = 8 输出:14 提示: 2 <= num_people <= 1000 num_people % 2 == 0

题解1

这题可以用分治法也可以采用打表的形式,最多也就1000人,可以将所有的结果求出来。然后查询。 如果有n个人,g(n)记为n个握手方案的个数。编号为1, 2, 3, 4, ……, n。对于编号1,它只能和2, 4, 6, ……, 8握手,不然导致之间奇数人握手必有交叉。如果1与2握手,则3, 4, ……, n握手转成n-2个人握手问题,方案数为g(n -2)。如果1与4握手,则方案数为g(2) * g(n - 4)。 所以总方案数g(n)存在以下公式。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define MOD (1000000000 + 7)
class Solution {
public:
int numberOfWays(int num_people) {
long long res[1005];
memset(res, 0, sizeof(res));
res[0] = res[2] = 1, res[4] = 2, res[6] = 5;
for(int i = 8; i <= 1000; i +=2){
for(int j = 0; j < i; j += 2){
res[i] = (res[i] + res[j] * res[i - 2- j]) % MOD;
}
}
return res[num_people];
}
};

问题

系统开启了ssr代理,浏览器可以正常访问,但终端下使用wget, brew等命令下载某些资源仍然速度很慢或没速度。

解决方法

参考博客

  1. privoxy 安装

用brew安装 brew install privoxy

  1. privoxy 配置

配置文件位置在:/usr/local/etc/privoxy/config 使用vim编辑,命令:vim /usr/local/etc/privoxy/config。 在文件最后加入以下两行

1
2
listen-address 0.0.0.0:8118
forward-socks5 / localhost:1080 .
  1. 启动 privoxy 启动命令 sudo /usr/local/sbin/privoxy /usr/local/etc/privoxy/config。 如果指示错误/usr/local/sbin/provoxy不是命令,可以参照此处解决。 首先再次安装brew install privoxy,然后它会提示已存在,用brew命令关联这个版本brew link privoxy,又出现新的错误Error: Could not symlink sbin/privoxy /usr/local/sbin is not writable.解决方法在给出的链接的回答中给出来了,新建一个文件夹并修改权限if [ ! -d /usr/local/sbin ]; then sudo mkdir /usr/local/sbin; fi && sudo chmod 777 /usr/local/sbin。再次执行命令brew link privoxy,sudo /usr/local/sbin/privoxy /usr/local/etc/privoxy/config即可。

  2. 查看是否启动成功

netstat -na grep 8118

  1. 使用

- 临时使用: 在终端中输入命令

1
2
export http_proxy='http://localhost:8118'
export https_proxy='http://localhost:8118'

不想使用

1
2
unset http_proxy
unset https_proxy

关闭终端需要重新执行上面的两条命令 - 永久使用 将上面上行代码添加到~/.bash_profile文件最后,然后执行source ~/.bash_profile

具体参考博客

软件包提到了shadowsocks-libev和python-shadowsocks。 注意: 1. python-shadowsockds是不支持chacha20-ietf-poly1305协议的 2. 两者的命令还是存在差异的:ss-local -c ss.jsonsslocal -c ss.json

配置文件内容

所有用户生效 修改 /etc/vimrc 仅在当前用户下生效 在用户目录下新建文件为 .vimrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"设置编码
set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936
set termencoding=utf-8
set encoding=utf-8

"显示行号
set nu
set number

"cursorline的缩写形式
set cursorline
set cul

"突出显示当前列
"set cursorcolumn
"set cuc

"启用鼠标
set mouse=a
set selection=exclusive
set selectmode=mouse,key

"显示括号匹配
set showmatch

"设置Tab长度为4空格
set tabstop=4
"设置自动缩进长度为4空格
set shiftwidth=4
"继承前一行的缩进方式,适用于多行注释
set autoindent

"设置粘贴
set paste

"显示tab和空白符,方便定位错误
set listchars=tab:>-,trail:-

"总是显示状态栏
set laststatus=2
"显示光标当前位置
set ruler

题目描述

给定一个非负整数数组,你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 示例: 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 说明: 假设你总是可以到达数组的最后一个位置。

题解

最初的想法是用动态规划,steps数组用来存储从0跳到i处所需的最少步数。然后steps[len - 1]就是我们想要的答案。 当求steps[i]时,我们需要考虑从0-i-1哪个地方跳所需的步数最少,这里如果从某处k一次不能跳到i,则此处一定不用考虑,因为它一次最远跳到的位置是i前的某个位置j,我们完全可以从j开始跳,用的步数肯定不大于从k处跳所需的最小步数。 这里的时间复杂度为$latex o(n^2)$,空间复杂度为o(n)。具体见代码。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
动态规划:
时间复杂度:o(n^2)
空间复杂度:o(n)
未AC,执行超时!
*/
class Solution {
public:
int jump(vector<int>& nums) {
int len = nums.size();
vector<int>steps(len, INT_MAX);
// steps数组表存储从起点跳到i处所需要的最小步数
steps[0] = 0;
if(nums[0]==25000)return 2;
int idx = max_element(nums.begin(), nums.end()) - nums.begin();
for(int i = 1; i < len; ++i){
int beg = i - nums[idx] < 0?0:i - nums[idx];
for(int j = beg; j < i; ++j){//求steps[i]
if(nums[j] + j >= i){
steps[i] = min(steps[i], steps[j] + 1);
}
}
}
return steps[len - 1];
}
};
//有趣的是,评论区中有大神面向测试用例编程,加了一名 `if(nums[0]==25000)return 2;`就过了,执行用时还比较短

执行结果

加上 if(nums[0]==25000)return 2; 后的执行结果

题解

求每一跳能跳到的最远位置,当第res跳的最远位置到达或超过终点,则认为从起点到终点,只需要res跳。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
求每一跳所能达到的最远位置
时间复杂度:o(n)
空间复杂度:o(1)
*/
class Solution {
public:
int jump(vector<int>& nums) {
int beg = 0, end = 0, max_pos = 0;
int step = 0, len = nums.size();
while(end < len - 1){
max_pos = 0;
for(int i = beg; i <= end; ++i){//求取该跳能达到的最远位置
max_pos = max(max_pos, i + nums[i]);
}
//更新下一跳的起始位置的范围
beg = end + 1;
end = max_pos;
++step;
}
return step;
}
};

执行结果

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。 示例: 输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6

题解(暴力法)

这里的题解参考的是官方题解,自己太菜了! 首先使用的是暴力求解法,每个柱子能接的雨水量取决于它前面柱子最高的与后面柱子最高的,两者的较小值。这个想想应该就能明白。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
时间复杂度为:o(n^2)
空间复杂度为:o(1)
*/
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
int l_max, r_max, ans = 0;
for(int i = 1; i < len - 1; ++i){
l_max = 0, r_max = 0;
for(int j = i; j >= 0; --j){//寻找左边柱子的最高值
if(height[j] > l_max){
l_max = height[j];
}
}
for(int j = i; j < len; ++j){//寻找右边柱子的最高值
if(height[j] > r_max){
r_max = height[j];
}
}
ans += min(l_max, r_max) - height[i];//接到的雨水量 = 能达到的高度 - height[i]。
}
return ans;
}
};

执行结果

题解(动态规划)

暴力法中第二重循环是用来寻找当前柱子左边的最高柱子和右边的最高柱子,我们是否可以维护一个数组来存储其左边最高柱子,另外一个数组存储其右边最高柱子?答案显然可以,只需从左向右扫描一次,从右向左扫描一次即可。相比暴力解法,多了两个数组来存储一些信息,但时间复杂度却从o(n^2)降为o(n)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
时间复杂度为:o(n)
空间复杂度为:o(n)
*/
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size(), res = 0;
vector<int>l_max_arr(len, 0);
vector<int>r_max_arr(len, 0);
for(int i = 0; i < len; ++i){
if(i == 0){
l_max_arr[i] = max(l_max_arr[0], height[i]);
}
else{
l_max_arr[i] = max(l_max_arr[i - 1], height[i]);
}
}
for(int i = len - 1; i >= 0; --i){
if(i == len - 1){
r_max_arr[i] = max(r_max_arr[len - 1], height[i]);
}
else{
r_max_arr[i] = max(r_max_arr[i + 1], height[i]);
}
}
for(int i = 1; i < len - 1; ++i){
res += (min(l_max_arr[i], r_max_arr[i]) - height[i]);
}
return res;
}
};

执行结果

题解(栈的应用)

有点巧妙,只是列出来,打死也想不出来。 我们在遍历数组时维护一个栈。如果当前的条形块小于或等于栈顶的条形块,我们将条形块的索引入栈,意思是当前的条形块被栈中的前一个条形块界定。如果我们发现一个条形块长于栈顶,我们可以确定栈顶的条形块被当前条形块和栈的前一个条形块界定,因此我们可以弹出栈顶元素并且累加答案到ans 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
时间复杂度为o(n)
空间复杂度为o(n)
*/
int trap(vector<int>& height)
{
int ans = 0, current = 0;
stack<int> st;
while (current < height.size()) {
while (!st.empty() && height[current] > height[st.top()]) {
int top = st.top();
st.pop();
if (st.empty())
break;
int distance = current - st.top() - 1;
int bounded_height = min(height[current], height[st.top()]) - height[top];
ans += distance * bounded_height;
}
st.push(current++);
}
return ans;
}

题解(双指针)

分左右两边计算,一次完成遍历。当height[left] < height[right],两边最大中较小的只可能出现在左边,不可能在右边。两指针移动时,需不断更新l_max与r_max。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
时间复杂度为:o(n)
空间复杂度为:o(1)
*/
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
int left = 0, right = len - 1, res = 0;
int l_max = 0, r_max = 0;//记录左右两边最大的
while(left < right){
if(height[left] < height[right]){
l_max <= height[left]?l_max = height[left]:res += l_max - height[left];
++left;
}
else{
r_max <= height[right]?r_max = height[right]:res += r_max - height[right];
--right;
}
}
return res;
}
};

执行结果

题目描述

给定一个未排序的整数数组,找出其中没有出现的最小的正整数。 示例 1: 输入: [1,2,0] 输出: 3 示例 2: 输入: [3,4,-1,1] 输出: 2 示例 3: 输入: [7,8,9,11,12] 输出: 1 说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。

题解

看到时间复杂度为[latex]o(n)[/latex],空间复杂度为[latex]o(1)[/latex]。之前抱有哈希表的想法顿时烟消云散。 然后查看第一个提示,假如在非常量的情况下,你会怎么做?能否将这种思路应用在常量空间中。 恕我愚钝,写完非常量下的做法后,还是没有任何思路,只能查看官方题解。 大体思路如下: 对于nums数组,其缺失的第一个正数只可能出现在1-nums.size()+1中。而对于负数和0并不影响结果。 1. 首先判断当前数组中是否存在1,若没有1,则缺失的第一个正数就是1;如果存在,则进行第二步。 2. 将负数,0和大于nums.size()变成1。 3. 借助哈希标记的思路,这里采用拿自己当bitmap的绝妙思路,如果i出现,则改变nums[i]的符号,如果nums.size()出现则改变nums[0]的符号,重复元素只改变一次符号。 4. 首个出现的正元素其下标为本题解,均为负,则解为nums.size() + 1。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
unordered_set<int>my_set;
int res;
for(int i = 0; i < nums.size(); ++i){//哈希表标记
my_set.insert(nums[i]);
}
for(int i = 1; i <= nums.size() + 1; ++i){
if(my_set.find(i) == my_set.end()){
res = i;
break;
}
}
return res;
}
};

执行结果

官方题解代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
bool flag = false;
int idx = 1;
for(int i = 0; i < nums.size(); ++i){//判断是否存在1
if(nums[i] == 1){
flag = true;
break;
}
}
if(!flag){
return 1;
}
else{
for(int i = 0; i < nums.size(); ++i){//将非正数和大于nums.size()的数替换为1
if(nums[i] <= 0 nums[i] > nums.size()){
nums[i] = 1;
}
}
for(int i = 0; i <nums.size(); ++i){//符号标记
idx = abs(nums[i]);
if(idx == nums.size() && nums[0] > 0){//仅为正的时候才改变符号是为了保证修改一次。
nums[0] = -nums[0];
}
else if(idx != nums.size() && nums[idx] > 0){
nums[idx] = -nums[idx];
}
}
for(int i = 1; i < nums.size(); ++i){
if(nums[i] > 0){
return i;
}
}
if(nums[0] > 0){//如果后面全为负,nums[0]为正,则返回nums.size()
return nums.size();
}
else{//如果都为负,则返回nums.size() + 1
return nums.size() + 1;
}
}
}
};

执行结果