CUFMH2Blog

LeetCode - 剑指Offer - Part4

Word count: 3.6k Reading time: 19 min
2020-09-05 calculating Share

选得幽居惬野情,终年无送亦无迎。

有时直上孤峰顶,月下披云啸一声。

剑指 Offer 31. 栈的压入与弹出序列*

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

提示:

0 <= pushed.length == popped.length <= 1000
0 <= pushed[i], popped[i] < 1000
pushed 是 popped 的排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof

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:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
// 模拟
// 84.25% 83.41%
int len=pushed.size();
stack<int> s;
int j=0;
for(int i=0;i<len;i++){
if(s.empty()) {s.push(pushed[j]); j++;}
if(popped[i]==s.top()) {s.pop();}
else {
while(popped[i]!=s.top()) {
if(j==len) return false;
s.push(pushed[j]);
j++;
}
s.pop();
}
}
return true;
}
};

剑指 Offer 32. 从上到下打印二叉树*

队列实现层次遍历即可

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

例如:
给定二叉树: [3,9,20,null,null,15,7],

​ 3

/ \
9 20
/ \
15 7
返回:

[3,9,20,15,7]

提示:

节点总数 <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof

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
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
// 88.87% 94.01%
vector<int> ans;
if(!root) return ans;
queue<TreeNode *> que;
que.push(root);
while(!que.empty()) {
TreeNode * t=que.front();
que.pop();
ans.push_back(t->val);
if(t->left) que.push(t->left);
if(t->right) que.push(t->right);
}
return ans;
}
};

每一层打印到一行

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:
vector<vector<int>> levelOrder(TreeNode* root) {
// 直接每行后面添加标记
// 90.81% 60.95%
vector<vector<int>> ans;
if(!root) return ans;
queue<TreeNode *> que;
que.push(root);
que.push(nullptr);
vector<int> temp;
while(!que.empty()) {
TreeNode * t=que.front();
if(!t) {
ans.push_back(temp);
temp.clear();
que.pop();
continue;
}
temp.push_back(t->val);
if(t->left) que.push(t->left);
if(t->right) que.push(t->right);
que.pop();
if(!que.front()) que.push(nullptr);
}
ans.pop_back();
return ans;
}
};

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

例如:
给定二叉树: [3,9,20,null,null,15,7],

3

/ \
9 20
/ \
15 7
返回其层次遍历结果:

[
[3],
[20,9],
[15,7]
]

提示:

节点总数 <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof

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
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
// 双端队列
// 不使用vector reverse
// 3-4-5-6-0|root-0|0-1-2
// 90.35% 84.53%
vector<vector<int>> ans;
if(!root) return ans;
deque<TreeNode *> que;
que.push_back(root);
que.push_back(nullptr);
vector<int> temp;
bool flag=0;
while(!que.empty()) {
if(!flag) {
TreeNode * t=que.front();
if(!t) {
ans.push_back(temp);
temp.clear();
que.pop_front();
flag=1-flag;
continue;
}
temp.push_back(t->val);
if(t->left) que.push_back(t->left);
if(t->right) que.push_back(t->right);
que.pop_front();
if(!que.front()) que.push_front(nullptr);
} else {
TreeNode * t=que.back();
if(!t) {
ans.push_back(temp);
temp.clear();
que.pop_back();
flag=1-flag;
continue;
}
temp.push_back(t->val);
if(t->right) que.push_front(t->right);
if(t->left) que.push_front(t->left);
que.pop_back();
if(!que.back()) que.push_back(nullptr);
}
}
ans.pop_back();
return ans;
}
};

剑指 Offer 33. 二叉搜索树的后序遍历序列*

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

示例 1:输入: [1,6,3,2,5]
输出: false

示例 2:输入: [1,3,2,6,5]
输出: true

提示:

数组长度 <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
// 100.00% 78.27%
bool verifyPostorder(vector<int>& postorder) {
int len=postorder.size();
if(len<=1) return true;
return fun(postorder, 0, len-1);
}

bool fun(vector<int>& postorder, int l, int r){
if(l>=r) return true;
int i;
for(i=l;i<r;i++) {if(postorder[i]>postorder[r]) break;}
for(int j=i+1;j<r;j++) {if(postorder[j]<postorder[r]) return false;}
return fun(postorder,l,i-1) && fun(postorder,i,r-1);
// 分为两部分,确保左子树都小于最后的根节点,右子树都大于根节点
// 再保证左子树右子树也为搜索树
}
};

剑指 Offer 34. 二叉树中和为某一值的路径*

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

提示:

节点总数 <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof

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
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
// 97.70% 37.58%
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> pathSum(TreeNode* root, int sum) {
dfs(root, sum);
return ans;
}
void dfs(TreeNode* &root, int sum){
if(!root) return;
path.push_back(root->val);
if(!root->left && !root->right && root->val==sum) {
ans.push_back(path);
path.pop_back();
return;
}
dfs(root->left, sum-(root->val));
dfs(root->right, sum-(root->val));
path.pop_back();
}
};

剑指 Offer 35. 复杂链表的复制*

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof

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
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;

Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
// 1.在每个节点复制一个相同节点,再进行分割
// 96.73% 57.60%
if(!head) return nullptr;
Node *p=head, *q, *ans, *a;
// 复制节点
while(p) {
q=p->next;
Node *t=new Node(p->val);
p->next=t;
t->next=q;
p=q;
}
p=head;
// 复制随机指针
while(p) {
if(p->random) {
p->next->random=p->random->next;
}
p=p->next->next;
}
p=head;
ans=head->next;
a=head->next;
// 分割
while(p) {
q=p->next->next;
if(q) {
p->next=q;
a->next=q->next;
} else {
p->next=nullptr;
a->next=nullptr;
}
a=a->next;
p=q;
}
return ans;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
Node* copyRandomList(Node* head) {
// 2.hashmap
if(head==NULL) return head;
unordered_map<Node*,Node*>mp;
Node *t=head;
while(t!=NULL){
mp[t]=new Node(t->val);
t=t->next;
}
t=head;
while(t!=NULL){
if(t->next){
mp[t]->next=mp[t->next];
}
if(t->random){
mp[t]->random=mp[t->random];
}
t=t->next;
}
return mp[head];
}
};

剑指 Offer 36. 二叉搜索树与双向链表*

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

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
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;

Node() {}

Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}

Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* treeToDoublyList(Node* node) {
// 原地算法 - 递归
// 95.30% 59.04%
if(!node) return nullptr;
if(!node->left && !node->right) {
node->left=node;
node->right=node;
return node;
} else if(!node->left) {
Node *t=treeToDoublyList(node->right);
node->right=t;
node->left=t->left;
t->left->right=node;
t->left=node;
return node;
} else if(!node->right) {
Node *t=treeToDoublyList(node->left);
node->right=t;
node->left=t->left;
t->left->right=node;
t->left=node;
return t;
}
node->right=treeToDoublyList(node->right);
Node* t1=node->right->left;
Node* t2=treeToDoublyList(node->left);
node->left=t2->left;
node->right->left=node;
node->left->right=node;
t1->right=t2;
t2->left=t1;
cout << "4:" << t2->val << endl;
return t2;
}
};

剑指 Offer 37. 序列化二叉树*

请实现两个函数,分别用来序列化和反序列化二叉树。

你可以将以下二叉树:

​ 1 - (2) | (3-(4)|(5))

序列化为 “[1,2,3,null,null,4,5]”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof

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
82
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
// 72.60% 90.44%
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string ans="";
if(!root) return ans;
int count_null=0;
queue<TreeNode *> que;
TreeNode * t=nullptr;
que.push(root);
while(!que.empty()){
t=que.front();
que.pop();
if(!t) {
ans+= "null ";
count_null++;
} else {
que.push(t->left);
que.push(t->right);
ans+= to_string(t->val) + " ";
count_null=0;
}
}
ans = ans.substr(0, ans.size()-5*count_null);
return ans;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
// 可能是多位数或者负数
int len=data.size();
if(!len) return nullptr;

int i=0, j=0, k;
while(j+1<len && data[j+1]!=' ') { j++; }
string s=data.substr(i, j-i+1);
k=atoi(s.c_str());
TreeNode *p=new TreeNode(k);
i=j+2;

queue<TreeNode *> que;
que.push(p);

bool isLeft=1;
while(i<len) {
TreeNode *q=que.front(), *t;
if(data[i]=='n') {
i+=5;
t=nullptr;
} else {
j=i;
while(j+1<len && data[j+1]!=' ') { j++; }
string s=data.substr(i, j-i+1);
k=atoi(s.c_str());
i=j+2;
t=new TreeNode(k);
que.push(t);
}
if(isLeft) {
q->left=t;
} else{
q->right=t;
que.pop();
}
isLeft = 1-isLeft;
}
return p;
}
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

剑指 Offer 38. 字符串的排列*

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = “abc”
输出:[“abc”,”acb”,”bac”,”bca”,”cab”,”cba”]

限制:

1 <= s 的长度 <= 8

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof

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:
// 97.01% 85.73%
vector<string> permutation(string s) {
vector<string> ans;
fun(ans,s,0); // dfs
return ans;
}
void fun(vector<string> &ans, string &s, int left) {
if(left==s.size()) { // 终止条件
ans.push_back(s);
return;
}
for(int i=left; i<s.size(); i++) { // 剪枝
bool flag=1;
for(int j=left; j<i; j++){
if(s[j]==s[i]) { flag=0; }
}
if(flag) {
swap(s[left],s[i]);
fun(ans, s, left+1);
swap(s[left],s[i]);
} else{
continue;
}
}
}
};

剑指 Offer 39. 数组中出现次数过半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:

1 <= 数组长度 <= 50000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof

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
class Solution {
public:
int majorityElement(vector<int>& nums) {
// 1.排序法
// 时间O(nlogn) 空间O(1)
// 21.95% 41.79%
// -----------------
// sort(nums.begin(), nums.end());
// return nums[nums.size()/2];

// 2.hashmap
// 时间O(n) 空间O(n/2)
// 26.75% 5.20%
// -----------------
// unordered_map<int,int> m;
// int len = nums.size();
// for(int i = 0; i < len; i++){
// m[nums[i]]++;
// if(m[nums[i]] > len/2)
// return nums[i];
// }
// return 0;

// 3.摩尔投票法
// 时间O(n) 空间O(1)
// 63.12% 83.12%
// -----------------
int count=0;
int len = nums.size(), num;
for(int i=0; i<len; i++) {
if(!count) {
num=nums[i];
count++;
} else if(num==nums[i]) {
count++;
} else {
count--;
}
}
return num;
}
};

剑指 Offer 40. 最小的k个数*

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof

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
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
// 1.排序
// 66.84% 66.64%
// O(nlogn) O(logn)
// ---------------------
// vector<int> ans;
// sort(arr.begin(), arr.end());
// for (int i=0;i<k;i++) ans[i] = arr[i];
// return ans;

// 2.堆 - priority_queue
// 31.25% 25.18%
// O(nlogk) O(k)
// ---------------------
// vector<int> ans;
// if(!k) return ans;
// priority_queue<int> heap;
// for(int i=0;i<k;i++) heap.push(arr[i]);
// for(int i=k;i<arr.size();i++) {
// if(arr[i]<heap.top()) {
// heap.pop();
// heap.push(arr[i]);
// }
// }
// for(int i=0;i<k;i++) {
// ans.push_back(heap.top());
// heap.pop();
// }
// return ans;

// 3.快排
// 46.10% 61.40%
// O(n)~O(n^2) O(logn)~O(n)
// ---------------------
vector<int> ans;
quick_sort(arr,0,arr.size()-1,k);
for(int i=0;i<k;i++) ans.push_back(arr[i]);
return ans;
}

void quick_sort(vector<int>& arr, int l, int r, int k) {
if(l>=r) return;
int i=l-1, j=r+1, num=arr[(l+r)/2];
while(i<j) {
i++;j--;
while(arr[i]<num) {i++;}
while(arr[j]>num) {j--;}
if(i<j) swap(arr[i], arr[j]);
cout << i << j << endl;
}
if(j-l+1==k) return;
else if(j-l+1>k) quick_sort(arr, l, j, k);
else quick_sort(arr, j+1, r, k-(j-l+1));
return;
}
};

CATALOG
  1. 1. 剑指 Offer 31. 栈的压入与弹出序列*
  2. 2. 剑指 Offer 32. 从上到下打印二叉树*
  3. 3. 剑指 Offer 33. 二叉搜索树的后序遍历序列*
  4. 4. 剑指 Offer 34. 二叉树中和为某一值的路径*
  5. 5. 剑指 Offer 35. 复杂链表的复制*
  6. 6. 剑指 Offer 36. 二叉搜索树与双向链表*
  7. 7. 剑指 Offer 37. 序列化二叉树*
  8. 8. 剑指 Offer 38. 字符串的排列*
  9. 9. 剑指 Offer 39. 数组中出现次数过半的数字
  10. 10. 剑指 Offer 40. 最小的k个数*