casyup.me@outlook.com

0%

Permutaions II

给定一个数组, 返回数组中所有不重复的序列

my solutions (108 -> 24ms)

一开始我的想法是, 按照 46 题的解法, 然后用 set 自动去重

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
static const int _ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);

return 0;
}();

class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& v) {
size = v.size();
if (size == 1) return {v};
else if (size == 0) return {};

permuteUnique_do(v, 0);

return { ret.begin(), ret.end() };
}

private:
void permuteUnique_do(vector<int>& curv, int begin) {
if (begin >= size) {
ret.insert(ret.begin(), curv);

return;
}

for (int i = begin; i < size; ++i) {
swap(curv[i], curv[begin]);
permuteUnique_do(curv, begin + 1);
swap(curv[i], curv[begin]);
}
}

private:
set<vector<int>> ret;
size_t size;
};

但是这得到了 108 ms 的答案, 我思索了一下, 速度的瓶颈在于:

我对多个数做了同样的操作, 但最后将他们其中的大部分都抛弃了

要结局这种问题, 需要从源头入手, 在操作之前, 就将这些数据跳过

不过我暂时没有什么好的想法, 看了一下最好的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static const int _ = []() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}();
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
do{
res.push_back(nums);
}while(next_permutation(nums.begin(), nums.end()));
res.erase(unique(res.begin(),res.end()),res.end());
return res;
}
};

emmm, 他又用了标准库

本来想着这道题就这样算了, 但是突然想到, 这好像和 31 题很类似…

然后我改良了一下算法

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
static const int _ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);

return 0;
}();

class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& v) {
size_t size = v.size();
if (size == 1) return {v};
else if (size == 0) return {};

sort( v.begin(), v.end() );
ret.push_back(v);

while ( nextPermutation(v) ) {
ret.push_back(v);
}

return ret;
}

private:
bool nextPermutation(vector<int>& nums) {
size_t size = nums.size();

for (int i = size - 1; i > 0; --i) {
if (nums[i] > nums[i - 1]) {
int i2 = nums[i - 1];
vector<int>::iterator it = nums.end();
while ( ++i2 < size ) {
if ( ( it = find(nums.begin() + i + 1, nums.end(), i2) ) != nums.end() )
break;
}

if ( it != nums.end() ) {
*it ^= nums[i - 1];
nums[i - 1] ^= *it;
*it ^= nums[i - 1];
} else {
nums[i] ^= nums[i - 1];
nums[i - 1] ^= nums[i];
nums[i] ^= nums[i - 1];
}

sort(nums.begin() + i, nums.end());
return true;
}
}

return false;
}

private:
vector<vector<int>> ret;
};

形状和 the best solution 类似, 但是过程我自己实现了, 换成了我在 31 题中的解法

最后, 我得到了仅次于最好答案的速度

(标准库还是没办法比得过的, 我什么时候能写出标准库一样漂亮的代码呢?)

(PS: 我是不是改抽一部分时间去分析一下标准库的东西?)

(一开始的 236 是我未将参数以引用传递)

(后面的 32 是我将 size 换成了类变量, 但是时间增加了, 看来对于经常访问的变量, 还是局部变量好)

(PS: 我不是分析过这些东西么, 为什么会犯这种错…)

Spiral Matrix II

没什么好说的, 旋转填充N宫格

my solution (4ms)

可以用一个 while + 4个for 来做, 这样是最直观, 最好理解的

但是不太想这样, 感觉太丑陋了… 所以想了一下办法在一个循环中实现它

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
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> ret( n, vector<int>(n, 0) );
vector<vector<int>> step { {1, 0}, {0, 1}, {-1, 0}, {0, -1} };
int type = 0, count = 0, xstep = 0, ystep = 0;;

for (int nums = 1, end = n * n; nums <= end; ) {
ret[ystep][xstep] = nums++;

xstep += step[type][0];
ystep += step[type][1];

count = ++count >= (n - 1) ? count - n + 1 : count;

if (count == 0) {
type = ++type % 4;
if (type == 0) {
n -= 2;
xstep += 1;
ystep += 1;
}
}
}

return ret;
}
};

前进有4个方向, 每次的位移是一定的, 而且是按照循环来做位移

所以用了取余和数组来确定每一次的位移是正确的

然后是将每个方向的步数看做同一个值, 所以求取步数在之前

比如 n = 3:

[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]

则是按照 1, 2 3, 4 … ( (4 * n - 1) )这样的概念来前进, 但是问题在于

如果将计算步数的操作放到了最后, 那么 3 就会被错误计算, 所以保存了一下最后一次越界前的值

(我隐约感觉这里应该能够优化, 毕竟这样写出来的代码不易理解)

结果没什么好说的

the best solution (4ms)

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
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int> > result(n,vector<int>(n));

if(n==0)
{
return result;
}
if(n==1)
{
result[0][0] = n;
return result;
}

int top = 0;
int bottom = n-1;
int left = 0;
int right = n-1;

long index = 1;

while(top<=bottom)
{
for(int i=left;i<=right;i++)
{
result[top][i]=index;
index++;
}

for(int i=top+1;i<bottom;i++)
{
result[i][right]=index;
index++;
}

if(top<bottom)
{
for(int i=right;i>=left;i--)
{
result[bottom][i]=index;
index++;
}
}

if(left<right)
{
for(int i=bottom-1;i>top;i--)
{
result[i][left]=index;
index++;
}
}

left++;
right--;
top++;
bottom--;
}

return result;
}
};

我不喜欢丑陋的 while for for for for :( , 就不分析了

Merge Intervals

给定一个区间的集合, 合并所有重复的区间

my solution (12ms)

可以通过重新排序的形式来一步步合并, 比如:

start: 1 2 8 15

end: 3 6 10 18

其中 start 和 end 是经排序后的数组

从 1 开始, 如果接下来的开始下标小于当前的结束下标 (2 < 3)

那么, 就可以合并, 继续对比下一个. (8 < 6) 不成立, 那么第一个区间就出现了, 1-6

接下来从 8 开始, 继续以上步骤, 直至数组尾部, 这样算法的复杂度就是 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
static const int _ = []() {
ios::sync_with_stdio(false);
cin.tie(NULL);

return 0;
}();

class Solution {
public:
vector<Interval> merge(vector<Interval>& intervals) {
vector<int> start;
vector<int> end;
vector<Interval> ret;

for (auto& it : intervals) {
start.push_back(it.start);
end.push_back(it.end);
}

sort(start.begin(), start.end());
sort(end.begin(), end.end());

size_t size = start.size();
for (int i = 0; i < size; ) {
int ti = i;

for (int j = i++; ;) {
if (j >= size || i >= size) { // 这里应该可以少判断 j 的值
ret.push_back( {start[ti], end.back()} );
break;
}
else if (start[i] <= end[j]) {
++i, ++j;
}
else {
ret.push_back( {start[ti], end[j]} );
break;
}
}

}

return ret;
}
};

代码如上, 应该还有可以优化的地方 (比如说外围 for 循环, 他仅仅就是一个保留 i 的作用)

结果还不错

the best solution (8ms)

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
static const int _ = [](){
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}();

class Solution {
public:
std::vector<Interval> merge(std::vector<Interval>& Intervals) {
const int IntervalNum = Intervals.size();
if (IntervalNum < 2)
return Intervals;

std::sort(Intervals.begin(), Intervals.end(),
[](const Interval &A, const Interval &B) {
return A.start < B.start;
});

std::vector<Interval> Merged {Intervals[0]};
for (int i = 1; i < IntervalNum; ++i) {
const Interval &Curr = Intervals[i];
Interval &LastMerged = Merged.back();
// 完全被内含的区间会被跳过
if (Curr.end <= LastMerged.end)
continue;
// 更新区间的最大值
if (Curr.start <= LastMerged.end)
LastMerged.end = Curr.end;
else // 下一个区间完全不在当前区间内, 不能合并. 压入当前区间, 继续执行
Merged.push_back(Curr);
}

return Merged;
}
};

他的排序是直接在 Intervals 进行, 并且只比对 start (也是, 我好像做了多余的比对)

想法大致是一样的, 但是细节不够好

Unique Paths

找到不重复的能到达目标的路径数

my solution (8ms)

典型的可以用递归解决的问题

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
class Solution {
public:
int uniquePaths(int m, int n) {
_m = m;
_n = n;
_cx = 0;

uniquePaths_do(1, 1);

return _cx;
}

private:
void uniquePaths_do(int m, int n) {
if (m == _m && n == _n) {
++_cx;
return;
}

if (m + 1 <= _m)
uniquePaths_do(m + 1, n);
if (n + 1 <= _n)
uniquePaths_do(m, n + 1);
}

int _m;
int _n;
int _cx;
};

结果超时了, 想了一下:

            11
     21            12
  31   22        22   13
41 32 32 23    32 23 23 14

这样子的话, 大多数的迭代都会重复

所以更改了一下代码:

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
class Solution {
public:
int uniquePaths(int m, int n) {
_m = m;
_n = n;

return uniquePaths_do(1, 1);
}

private:
int uniquePaths_do(int m, int n) {
auto p = make_pair(m, n);

if (road[p] != 0) {
return road[p];
} else if (m == _m && n == _n) {
return 1;
}

int l = 0, r = 0;
if (m + 1 <= _m)
l = uniquePaths_do(m + 1, n);
if (n + 1 <= _n)
r = uniquePaths_do(m, n + 1);

if (road[p] == 0) {
road[p] = l + r;
}

return l + r;
}

int _m;
int _n;
map <pair<int, int>, int> road;
};

通过保存的形式来去掉一些可以重用的计算, 虽然过了, 但是效果不理想

emm… 我没有再从树中能总结出什么好的规律…

the best solution

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> path(m, vector<int>(n, 1));
for(int i = m - 2; i >= 0; i--){
for(int j = n - 2; j >= 0; j--){
path[i][j] = path[i + 1][j] + path[i][j + 1];
}
}
return path[0][0];
}
};

简洁又高效, 真是个漂亮的小东西

思路是这样的:

1 1 1
3 2 1

1 1 1
3 2 1
6 3 1

上述分别是 3, 2 和 3, 3 的情况, 假设 rebot 在左下角, star 在右上角(本质上不会有什么改变, 只是换了方向)

从 star 开始往后退, 每一个节点的次数都是他的 上节点次数 + 右节点次数 直到循环结束

最后的左下角就是 rebot 的次数

这样的算法复杂度是 O(n)

820 Short Encoding of Words

my solution = =

一开始先试着用 O(n2) 的方法, 之后用 O(n), 但是时间依旧很长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
sort(words.begin(), words.end(), [](string &str1, string &str2) {
return str1.size() > str2.size();
});

string str;
for (int i = 0; i < words.size(); i++) {
if ( str.find(words[i] + '#') == string::npos ) {
str.append(words[i] + '#');
}
}

return str.size();
}
};

并且这个代码之前还踩了个坑 : https://www.boost.org/sgi/stl/StrictWeakOrdering.html

best solution

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
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
for (string& s : words) {
reverse(s.begin(), s.end());
}
sort(words.begin(), words.end());

int length = 0;
int i = 0;
while (true) {
if ((i + 1) >= words.size()) {
length += words[i].length() + 1;
break;
}

if (words[i+1].compare(0, words[i].length(), words[i]) != 0) {
length += words[i].length() + 1;
}

i++;
}
return length;
}
};

em… 逻辑挺简单的

反序字符串, 字典序排序, 后再直接 compare

Combinations

找到 n 中所有 长度为 k 的不重复的组合

my solution (60ms, top 99.97%)

通过问题代换, 可以转换成一种很好理解并易于编写的问题

将问题看做 找出范围 n 中所有满足 p(x) 的整数 x

(和 31. Next Permutation 类似, 我是从中得到的启发)

其中 x 一开始为 ∑k << (n - k); [k=1], 即 n = 4, x = 1234

n = 4, k = 2
1 2 3 4
[1,2],
[1,3],
[1,4],
[2,3],
[2,4],
[3,4],

n = 4, k = 3
1 2 3 4
[1,2,3],
[1,2,4],
[1,3,4],
[2,3,4]

他们都按照一定规律, 满足下一个数比上一个数大

这个规则 p(x) 用语言描述, 就是:

从后往前找一个当前下标小于当前下标的最大值的数
如果找到了, 当前下标存储的值自增, 后续下标以当前下标为基准, 顺序 +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
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
if (k > n) return {};

_n = n, _k = k;
for (int i = 1; i <= k; ++i) {
_cur.push_back(i);
}

vector<vector<int>> ret;
ret.emplace_back(_cur);

while (true) {
getNextNums();

if (_cur.size() != 0)
ret.emplace_back(_cur);
else
break;
}

return ret;
}

public:
void getNextNums() {
for (int i = _k - 1; i >= 0; --i) {
if ( _cur[i] < _n - _k + i + 1 ) {
_cur[i] += 1;

for (int j = i + 1, s = _cur[i] + 1; j < _k; ++j)
_cur[j] = s++;

return;
}
}

_cur.clear();
}

vector<int> _cur;
int _n, _k;
};

combine 和 getNextNums 的逻辑都相当简单, 写起来很容易

best solution (56ms)

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
class Solution {
public:
void combine(vector<vector<int>> &result, vector<int> &item, int beg, int end, int index) {
int k = item.size() - index;
if (k == 0) {
result.push_back(item);
return;
}

for (int i = beg; i <= end - k + 1; ++i) {
item[index] = i;
combine(result, item, i + 1, end, index + 1);
}
}

vector<vector<int>> combine(int n, int k) {
vector<vector<int>> result;
if (n <= 0 || k <= 0) {
return result;
}

vector<int> item(k);
item[0] = 1;
combine(result, item, 1, n, 0);

return result;
}
};

emmm, 好奇怪啊, 逻辑上来说想的都是一样的, 为什么他少 4ms 呢… ( 或许是因为通过 this 指针获取变量的锅? )

他用递归的形式来实现, 每次递归中会从当前起始索引循环到结束索引

当数组的长度到达 k 的时候, 就返回

值得一提的是, 他用索引的形式避免了那个会被多次修改的 item 以值形式传递

(我放弃使用递归的一部分原因就是因为暂时想不到什么方法避免那个临时的数组, 原来还可以这样…)

Reverse Linked List II

给定一个单向链表, 按照距离 m, n 旋转单向链表

my solution (4ms, 100%)

其实这个题没什么好写的, 主要是这个题之前做过.

当时的代码量很大不说, 最后还没做出来…

old solution

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
class Solution {
public:
int moth(ListNode* head, int m, int n) {
if (m == n)
return 1; // don't need replace

return 0;
}

void replace_far() {
pmptrv->next = pn;
pm->next = pnnext;
pnptrv->next = pm;
pn->next = pmnext;
}

void replace_close() {
pmptrv ? pmptrv->next = pn : nullptr;
pm->next = pnnext;
pn->next = pm;
}

void setpoint(ListNode* pcursor, int m, int n) {
for (int i = 1; ; ++i) {
if (i == m - 1)
pmptrv = pcursor;
else if (i == m)
pm = pcursor;
else if (i == m + 1) {
pmnext = pcursor;
if (i == n - 1)
pnptrv = pcursor;
if (i == n)
pn = pcursor;
}
else if (i == n - 1)
pnptrv = pcursor;
else if (i == n)
pn = pcursor;
else if (i == n + 1)
pnnext = pcursor;

pcursor = pcursor->next;
if (!pcursor)
break;
}
}

ListNode* reverseBetween(ListNode* head, int m, int n) {
if (moth(head, m, n))
return head;

// ListNode* pcursor = head;
setpoint(head, m, n);

if (n - m >= 2)
replace_far();
else if (n - m == 1)
replace_close();

if (m == 1)
return pn;

return head;
}

private:
ListNode* pm = nullptr;
ListNode* pn = nullptr;
ListNode* pmptrv = nullptr;
ListNode* pmnext = nullptr;
ListNode* pnptrv = nullptr;
ListNode* pnnext = nullptr;
};

… 天知道我 4 个月前脑子里面装的是什么, 会写出这样的代码.

关键是错了十几次, 然后还是没做出来…

new solution

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
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if (m == n || head == nullptr)
return head;

ListNode temp(-1);
temp.next = head;
ListNode* p1 = &temp;
ListNode* sta;
int ax = 0;
while (true) {
if (++ax >= m) {
sta = p1;
p1 = p1->next;
break;
}

p1 = p1->next;
}

ListNode* prev = p1;
ListNode* cur = p1->next;
ListNode* end = p1;
while (ax++ < n) {
ListNode* sav = cur->next;
cur->next = prev;
prev = cur;
cur = sav;
}

sta->next = prev;
end->next = cur;

return temp.next;
}
};

我采用了多放置一个头结点的方式来处理头结点也会被旋转的情况

先找到 m 的位置, 然后顺序旋转至 n 的位置, 最后将头尾衔接一下

最后的几次是我的尝试, 一开始的时候是 8ms, 但是我观察了 best solution, 发现区别不大

遂又尝试了一下, 然后是 4ms, 但是有时候不稳定, 总之在 4ms - 8ms 之间跳 (= =…)

Binary Tree Inorder Traversal

这个问题用递归非常好解决, 但是作者要求使用迭代

这是一个以迭代实现子树遍历的做法, 值得做一下笔记

my solution

一开始我没看到 follow up, 然后用递归解决了

之后看到的时候没什么好的思路

(有部分是受了答案的影响, 因为这时候我已经解决了, 可以看别人的答案了)

(不得不说答案真是害人啊… 如果我独立思考的话, 花一部分时间也能想通的)

后面借鉴了答案的思想, 自己实现了, 所以这里就不贴出来了

best solution (4ms)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
TreeNode *curr;
stack<TreeNode*> st;
curr = root;
vector<int> res;

while ( !st.empty() || curr ){
while ( curr ) { st.push(curr); curr = curr->left; }
curr = st.top();
st.pop();
res.push_back(curr->val);
curr=curr->right;
}
return res;
}
};

简单来说

  1. 遍历所有左子树
  2. 获得栈顶, 存值, 然后以其右子树继续遍历

重复以上步骤, 很简单吧 (但是我当时为什么没有想出来呢 ? …)

以同样的逻辑, 可以实现 leftorder, rightorder

(如果没记错的话, 应该是这么叫的, 而且如果没估计错的话, 应该可以)

Populating Next Right Pointers in Each Node

给了一个完美的二叉树, 填充其next节点

my solution (60ms, 90.37%)

这道题我给出了两种方案, 递归和迭代

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
static const int _ = [] () {
ios::sync_with_stdio(false);
cin.tie(nullptr);

return 0;
} ();

class Solution {
public:
// Node* connect(Node* root) {
// if (!root)
// return root;
//
// vector<Node*> lev;
// lev.push_back(root);
//
// while ( !lev.empty() ) {
// vector<Node*> nlev;
// int size = lev.size();
//
// int i = 0, i2 = 1;
// for (; i2 < size; ++i, ++i2) {
// lev[i]->next = lev[i2];
// if ( lev[i]->left ) {
// nlev.push_back( lev[i]->left );
// nlev.push_back( lev[i]->right );
// }
// }
// lev[i]->next = nullptr;
// if ( lev[i]->left ) {
// nlev.push_back( lev[i]->left );
// nlev.push_back( lev[i]->right );
// }
//
// swap(lev, nlev);
// }
//
// return root;
// }

Node* connect(Node* root) {
if (!root) return root;

connect_two(root->left, root->right);

return root;
}

void connect_two(Node* left, Node* right) {
if (!left) return;

left->next = right;
if (left->left)
connect_two(left->left, left->right);
connect_two(left->right, right->left);
connect_two(right->left, right->right);
}
};

对于迭代, 我将每一级的元素都保留, 然后用作下一次迭代

递归的话, 因为如果只是单节点传参, 始终会面临有缝隙的情况, 所以用双节点传参

递归的代码更加简洁, 我推荐使用递归

emmmm… 速度上还好, 但是内存上就很不能接受了

(我很好奇, 代码量已经很少了, 而且递归中也不会产生多余的操作)

(难道是在 perfect binary tree 上能有什么绚丽的操作么?)

best solution

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
void connect(TreeLinkNode *root) {
queue<TreeLinkNode*> q;
if (!root){
return;
}

q.push(root);

while (!q.empty()){
int size = q.size();
while (size--){
TreeLinkNode* c = q.front();
q.pop();

if (size == 0){
c->next = NULL;
}
else{
c->next = q.front();
}

if (c->left){
q.push(c->left);
}
if (c->right){
q.push(c->right);
}
}
}
return;
}

哦哦哦, 用的是迭代的方式, 只不过他用了队列

而且用了size, 这样也就不需要一个新的容器了, 很不错 -v-

不过既然题说了是 perfect binary tree, 而且next默认为空, 那么可以稍微改良一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Node* connect(Node* root) {
if (!root) return root;

queue<Node*> q;
q.push(root);
while( !q.empty() ) {
int size = q.size();
while (size--) {
Node* cur = q.front();
q.pop();

if (size)
cur->next = q.front();

if (cur->left) {
q.push(cur->left);
q.push(cur->right);
}
}
}

return root;
}

细节在于:

  1. 如果是空节点, 少了一个空 queue 的分配时间
  2. 不用再去赋值 nullptr 了
  3. 少了一个 cur->right 的判断, 因为题目说了是 perfect binary tree

PS: 我最后用这个答案再试了一次, 发现结果和我用递归的结果相差无几…

可能这是增加了测试用例或者改了题的结果? 毕竟 TreeLinkNode 变为了 Node