目录
1--栈的压入、弹出序列(31)
直观思路:用两个指针 i 和 j 指向压入和弹出的 vector,终止条件是:所有元素都压入了辅助栈(i > len),且辅助栈当前的栈顶元素与弹出的元素 popped[j] 不相等;
#include <iostream>
#include <vector>
#include <stack>
class Solution {
public:
bool validateStackSequences(std::vector<int>& pushed, std::vector<int>& popped) {
int len = pushed.size();
if (len == 0) return true;
int i = 0, j = 0; // i 指向 pushed 的元素,j 指向 popped 的元素
while(i < len || !st.empty()){
if (i >= len && st.top() != popped[j]){ // 栈顶元素不等于当前要pop的元素,且已push完所有元素
return false;
}
if (!st.empty() && st.top() == popped[j]){ // 如果栈顶元素等于当前要pop的元素,pop出栈顶元素,j下移
st.pop();
j++;
}
else if(i < len && pushed[i] != popped[j]){ // i 指向的元素不等于 j 指向的元素,将 i 压入堆栈中
st.push(pushed[i]);
i++;
}
else if(i < len && pushed[i] == popped[j]){ // i 指向的元素等于 j 指向的元素,i,j移向下一位
i++;
j++;
}
}
return true;
}
private:
std::stack<int> st;
};
int main(int argc, char *argv[]){
std::vector<int> push = {2, 1, 0};
std::vector<int> pop = {1, 2, 0};
Solution S1;
bool res = S1.validateStackSequences(push, pop);
if(res) std::cout << "true" << std::endl;
else std::cout << "false" << std::endl;
return 0;
}
简便思路:
模拟栈的压入顺序,依次压入 pushed 里的所有元素,用一个指针 j 指向需要弹出的元素,当符合弹出要求时就弹出对应的元素;
当辅助栈为空时,表明需要弹出的元素都顺利弹出,返回 true;
#include <iostream>
#include <vector>
#include <stack>
class Solution {
public:
bool validateStackSequences(std::vector<int>& pushed, std::vector<int>& popped) {
std::stack<int> st;
int n = pushed.size();
for (int i = 0, j = 0; i < n; i++) {
st.emplace(pushed[i]);
while (!st.empty() && st.top() == popped[j]) { // 符合弹出要求,直接 pop 出栈顶元素
st.pop();
j++;
}
}
return st.empty(); // 所有元素都顺利pop出,返回true;
}
};
int main(int argc, char *argv[]){
std::vector<int> push = {2, 1, 0};
std::vector<int> pop = {1, 2, 0};
Solution S1;
bool res = S1.validateStackSequences(push, pop);
if(res) std::cout << "true" << std::endl;
else std::cout << "false" << std::endl;
return 0;
}
2--从上到下打印二叉树(32)
主要思路:层次遍历打印二叉树结点
#include <iostream>
#include <vector>
#include <queue>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
std::vector<int> levelOrder(TreeNode* root) {
if (root == NULL) return Res;
Q1.push(root);
while(!Q1.empty()){
temp = Q1.front();
Q1.pop();
Res.push_back(temp->val);
if(temp->left != NULL){
Q1.push(temp->left);
}
if(temp->right != NULL){
Q1.push(temp->right);
}
}
return Res;
}
private:
std::queue<TreeNode *> Q1;
std::vector<int> Res;
TreeNode *temp;
};
int main(int argc, char *argv[]){
TreeNode *Node1 = new TreeNode(3);
TreeNode *Node2 = new TreeNode(9);
TreeNode *Node3 = new TreeNode(20);
TreeNode *Node4 = new TreeNode(15);
TreeNode *Node5 = new TreeNode(7);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Solution s1;
std::vector<int> res = s1.levelOrder(Node1);
for(int item : res){
std::cout << item << " ";
}
return 0;
}
3--从上到下打印二叉树II(32)
主要思路:
与上题类似,借助于层次遍历,不同的是为了打印每一层的结点,需要循环当前层的结点数次,每一次当前队列的结点数实质上等于当前层的结点数,因此只需要循环队列的长度次,并记录对应的结点值即可;
#include <iostream>
#include <vector>
#include <queue>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
std::vector<std::vector<int>> levelOrder(TreeNode* root) {
if(root == NULL) return Res;
Q1.push(root);
while(!Q1.empty()){
int num = Q1.size(); // 当前层的结点数
for(int i = 0; i < num; i++){
temp_node = Q1.front();
Q1.pop();
temp.push_back(temp_node->val);
if(temp_node->left != NULL) Q1.push(temp_node->left);
if(temp_node->right != NULL) Q1.push(temp_node->right);
}
Res.push_back(temp);
temp.clear();
}
return Res;
}
private:
std::vector<std::vector<int>> Res;
std::queue<TreeNode *> Q1;
std::vector<int> temp;
TreeNode * temp_node;
};
int main(int argc, char *argv[]){
TreeNode *Node1 = new TreeNode(3);
TreeNode *Node2 = new TreeNode(9);
TreeNode *Node3 = new TreeNode(20);
TreeNode *Node4 = new TreeNode(15);
TreeNode *Node5 = new TreeNode(7);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Solution s1;
std::vector<std::vector<int>> res = s1.levelOrder(Node1);
for(int i = 0; i < res.size(); i++){
for(int item : res[i]){
std::cout << item << " ";
}
std::cout << std::endl;
}
return 0;
}
4--从上到下打印二叉树III(32)
主要思路:
借助于双端队列,区分奇数偶数行,奇数行从双端队列的左边(front)取结点,并从右子结点开始不断往头插入;偶数行从双端队列的右边(back)取结点,并从左子结点开始不断往后插入;
核心问题是区分奇数偶数行,如何取结点和插入子节点到队列中;
#include <iostream>
#include <vector>
#include <deque>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
std::vector<std::vector<int>> levelOrder(TreeNode* root) {
if (root == NULL) return Res;
bool flag = true; // 奇数行 true, 偶数行 false;
Q.push_back(root);
while(!Q.empty()){
std::vector<int> tmp;
int num = Q.size();
for(int i = 0; i < num; i++){
if(flag){
tmp_node = Q.front();
Q.pop_front();
tmp.push_back(tmp_node->val);
if(tmp_node->left != NULL) Q.push_back(tmp_node->left);
if(tmp_node->right != NULL) Q.push_back(tmp_node->right);
}
else{
tmp_node = Q.back();
Q.pop_back();
tmp.push_back(tmp_node->val);
if(tmp_node->right != NULL) Q.push_front(tmp_node->right);
if(tmp_node->left != NULL) Q.push_front(tmp_node->left);
}
}
flag = !flag;
Res.push_back(tmp);
}
return Res;
}
private:
std::deque<TreeNode *> Q;
std::vector<std::vector<int>> Res;
TreeNode * tmp_node;
};
int main(int argc, char *argv[]){
TreeNode *Node1 = new TreeNode(3);
TreeNode *Node2 = new TreeNode(9);
TreeNode *Node3 = new TreeNode(20);
TreeNode *Node4 = new TreeNode(15);
TreeNode *Node5 = new TreeNode(7);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Solution s1;
std::vector<std::vector<int>> res = s1.levelOrder(Node1);
for(int i = 0; i < res.size(); i++){
for(int item : res[i]){
std::cout << item << " ";
}
std::cout << std::endl;
}
return 0;
}
5--二叉搜索树的后序遍历序列(33)
主要思路:
后序遍历的定义:左→右→根;
二叉搜索树的定义:左子树的结点均小于根节点,右子树的结点均大于根结点;左子树和右子树均为二叉搜索树;
利用递归算法,判断左子树和右子树是否为二叉搜索树,回溯条件为树只剩下一个结点,这时(i >= j),返回 true;
#include <iostream>
#include <vector>
class Solution {
public:
bool verifyPostorder(std::vector<int>& postorder) {
return recur(postorder, 0, postorder.size() - 1);
}
bool recur(std::vector<int>& postorder, int i, int j){
if(i >= j) return true;
int p = i;
while(postorder[p] < postorder[j]) p++; // 直到遍历完左子树所有结点(左子树结点都小于根结点)
int m = p; // 右子树序列的第一个结点
while(postorder[p] > postorder[j]) p++; // 判断右子树的结点是否都大于根结点
// 右子树的结点都大于根节点时,p指向根节点,即 p == j 为 true
// 递归判断左子树是否为二叉搜索树,右子树是否为二叉搜索树
return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
}
};
int main(int argc, char *argv[]){
std::vector<int> test = {1, 3, 2, 6, 5};
Solution S1;
bool res = S1.verifyPostorder(test);
if (res) std::cout << "true" << std::endl;
else std::cout << "false" << std::endl;
return 0;
}
6--二叉树中和为某一值的路径(34)
主要思路:
基于深度优先搜索,不断搜索下一层,并更新对应的目标值;
#include <iostream>
#include <string>
#include <vector>
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:
std::vector<std::vector<int>> pathSum(TreeNode* root, int target) {
dfs(root, target);
return Res;
}
void dfs(TreeNode* root, int target){
if (root == NULL) return;
path.push_back(root->val);
target = target - root->val;
if(root->left == NULL && root->right == NULL && target == 0){
Res.push_back(path);
}
dfs(root->left, target);
dfs(root->right, target);
path.pop_back();
}
private:
std::vector<std::vector<int>> Res;
std::vector<int> path;
};
int main(int argc, char *argv[]){
// root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
TreeNode *Node1 = new TreeNode(5);
TreeNode *Node2 = new TreeNode(4);
TreeNode *Node3 = new TreeNode(8);
TreeNode *Node4 = new TreeNode(11);
TreeNode *Node5 = new TreeNode(13);
TreeNode *Node6 = new TreeNode(4);
TreeNode *Node7 = new TreeNode(7);
TreeNode *Node8 = new TreeNode(2);
TreeNode *Node9 = new TreeNode(5);
TreeNode *Node10 = new TreeNode(1);
Node1->left = Node2;
Node1->right = Node3;
Node2->left = Node4;
Node3->left = Node5;
Node3->right = Node6;
Node4->left = Node7;
Node4->right = Node8;
Node6->left = Node9;
Node6->right = Node10;
int targetSum = 22;
Solution S1;
std::vector<std::vector<int>> Res = S1.pathSum(Node1, targetSum);
for(auto item : Res){
for(int value : item){
std::cout << value << " ";
}
std::cout << std::endl;
}
return 0;
}
7--复杂链表的复制(35)
主要思路:
复制普通链表时,只需在遍历每一个结点时创建新的结点;但在复制复杂链表时,由于 random 指针的存在,会出现以下问题:即一个结点 random 指针指向的结点还未被创建;
可以通过哈希表的形式将原结点和新结点进行匹配成对,创建完所有新结点后再更新 next 指针和 random 指针的关系;
#include <iostream>
#include <map>
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) {
Node* tmp = head;
while(tmp != NULL){
Node* newNode = new Node(tmp->val); // 创建新结点
M.insert({tmp, newNode}); // 原结点与新结点匹配成对,原结点为 key,新结点为value
tmp = tmp->next;
}
// 创建 next 指向和random指向
tmp = head;
while(tmp != NULL){
M[tmp]->next = M[tmp->next];
M[tmp]->random = M[tmp->random];
tmp = tmp->next;
}
return M[head];
}
private:
std::map<Node*, Node*> M; // 创建哈希表
};
int main(int argc, char *argv[]){
Node* Node1 = new Node(7);
Node* Node2 = new Node(13);
Node* Node3 = new Node(11);
Node* Node4 = new Node(10);
Node* Node5 = new Node(1);
Node1->next = Node2;
Node2->next = Node3;
Node3->next = Node4;
Node4->next = Node5;
Node2->random = Node1;
Node3->random = Node5;
Node4->random = Node3;
Node5->random = Node1;
Solution S1;
Node* Copy_Node = S1.copyRandomList(Node1);
Node* tmp = Copy_Node;
while(tmp != NULL){
std::cout << tmp->val << " ";
tmp = tmp->next;
}
return 0;
}
8--二叉搜索树与双向链表(36)
主要思路:
中序遍历二叉搜索树,遍历过程中构建前后结点的关系;
#include <iostream>
#include <map>
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* root) {
if (root == NULL) return root;
dfs(root);
// 经过中序遍历后,最小的结点还未指向最大的结点,最大的结点也还未指向最小的结点
// 此时,head指向最小的结点,pre指向最大的结点
head->left = pre;
pre->right = head;
return head;
}
void dfs(Node* cur){
if(cur == NULL) return;
// 递归左子树
dfs(cur->left);
if(pre == NULL){ // 前继结点为空,这时cur为最小的结点,用头指针指向
head = cur;
}
else{ // 前继结点不为空
pre->right = cur; // 前继结点右指针指向 cur
cur->left = pre; // cur结点左指针指向pre
}
pre = cur; // 更新前继结点,递归右子树
dfs(cur->right);
}
private:
Node* head = NULL;
Node* pre = NULL; // 前继结点
};
int main(int argc, char *argv[]){
Node* Node1 = new Node(4);
Node* Node2 = new Node(2);
Node* Node3 = new Node(5);
Node* Node4 = new Node(1);
Node* Node5 = new Node(3);
Node1->left = Node2;
Node1->right = Node3;
Node2->left = Node4;
Node2->right = Node5;
Solution S1;
Node* Res = S1.treeToDoublyList(Node1);
Node* tmp = Res;
for(int i = 0; i < 10; i++){
std::cout << tmp->val << " ";
tmp = tmp->right;
}
return 0;
}
9--序列化二叉树(37)
主要思路:视频讲解参考
序列化时存储层次遍历的结果;反序列化时根据层次遍历的结果来重构二叉树;
#include <iostream>
#include <vector>
#include <string>
#include <queue>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Codec {
public:
std::string serialize(TreeNode* root) {
std::string Res;
if(root == NULL) return Res;
std::queue<TreeNode*> Q;
Q.push(root);
while(!Q.empty()){
TreeNode* tmp = Q.front();
Q.pop();
if(tmp != NULL){
Q.push(tmp->left);
Q.push(tmp->right);
Res.append(std::to_string(tmp->val) + ',');
}
else{
Res.append("NULL,");
}
}
return Res;
}
TreeNode* deserialize(std::string data) {
if(data.empty()) return NULL;
std::vector<std::string> StringList;
std::string tmp_string;
// 将字符串拆分存储到StringList中
for(char c : data){
if(c != ','){
tmp_string.push_back(c);
}
else{
StringList.push_back(tmp_string);
tmp_string.clear();
}
}
std::queue<TreeNode*> Q;
TreeNode* head = new TreeNode(std::stoi(StringList[0])); // 第一个字符串的值是根节点
Q.push(head);
int i = 1;
while(!Q.empty()){
TreeNode* tmp = Q.front();
Q.pop();
if(StringList[i] != "NULL"){ // 重构左子结点
TreeNode* left = new TreeNode(std::stoi(StringList[i]));
Q.push(left);
tmp->left = left;
}
// 省略 else 因为默认就是NULL
i++;
if(StringList[i] != "NULL"){ // 重构右子结点
TreeNode* right = new TreeNode(std::stoi(StringList[i]));
Q.push(right);
tmp->right = right;
}
// 省略 else 因为默认就是NULL
i++;
}
return head;
}
};
int main(int argc, char *argv[]){
TreeNode* Node1 = new TreeNode(1);
TreeNode* Node2 = new TreeNode(2);
TreeNode* Node3 = new TreeNode(3);
TreeNode* Node4 = new TreeNode(4);
TreeNode* Node5 = new TreeNode(5);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Codec S1;
// 序列化
std::string test = S1.serialize(Node1);
std::cout << test << std::endl;
// 反序列化
TreeNode* root = S1.deserialize(test);
std::queue<TreeNode* > Q;
Q.push(root);
std::cout << root->val << " ";
while(!Q.empty()){
TreeNode* tmp = Q.front();
Q.pop();
if(tmp->left != NULL){
Q.push(tmp->left);
std::cout << tmp->left->val << " ";
}
else std::cout << "NULL ";
if(tmp->right != NULL){
Q.push(tmp->right);
std::cout << tmp->right->val << " ";
}
else std::cout << "NULL ";
}
return 0;
}
10--字符串的排列(38)
主要思路:
考察去重的全排列,因为字符串的字符可能会重复;全排列讲解参考视频:全排列讲解
为了让相同字符相邻,首先对字符串进行排序;
通过一个 visited 数组记录哪个字符已被访问,每次重头访问字符,遇到已访问的字符则跳过;
通过剪枝同一树层的相同字符来减少搜索次数;
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
class Solution {
public:
std::vector<std::string> permutation(std::string s) {
visited.resize(s.size());
std::sort(s.begin(), s.end()); // 对字符进行排序,保证相同字符相邻
backtrack(s, path, 0);
return Res;
}
void backtrack(std::string s, std::string &path, int index){
if(index == s.size()){
Res.push_back(path);
return;
}
for(int i = 0; i < s.size(); i++){
if(i > 0 && s[i-1] == s[i] && visited[i-1] == false) continue; // 同一树层,需要剪枝
if(visited[i] == true) continue; // 该字符已被访问过
path.push_back(s[i]);
visited[i] = true;
backtrack(s, path, index+1);
// 回溯
path.pop_back();
visited[i] = false;
}
}
private:
std::vector<std::string> Res; // 返回结果
std::string path; // 记录字符串
std::vector<bool> visited; // 记录已访问的结点
};
int main(int argc, char *argv[]){
std::string s = "abc";
Solution S1;
std::vector<std::string> Res = S1.permutation(s);
for(std::string item : Res){
std::cout << item << std::endl;
}
return 0;
}
11--数组中出现次数超过一半的数字(39)
主要思路:
利用哈希表,数字作为 key,数字的值作为 value,记录最大的 value 即可,返回对应的 key;
#include <iostream>
#include <vector>
#include <map>
class Solution {
public:
int majorityElement(std::vector<int>& nums) {
for(int num : nums){
hash[num] += 1;
if(hash[num] > max){
max = hash[num];
Res = num;
}
}
return Res;
}
private:
std::map<int, int> hash;
int max = 0;
int Res = -1;
};
int main(int argc, char *argv[]){
std::vector<int> test = {1, 2, 3, 2, 2, 2, 5, 4, 2};
Solution S1;
int Res = S1.majorityElement(test);
std::cout << Res << std::endl;
return 0;
}
12--最小的 k 个数(40)
主要思路:
维护一个堆,堆中元素一开始从大到小排列,堆中存储 k 个数;
之后遍历剩余的数字,每次与堆中最大的数值进行比较,并交换;
最后返回堆中的 k 个数即可;
#include <iostream>
#include <vector>
#include <queue>
class Solution {
public:
std::vector<int> getLeastNumbers(std::vector<int>& arr, int k) {
if (k == 0) return Res;
for(int i = 0; i < k; i++){ // 堆一开始存储 k 个数
Stack.push(arr[i]);
}
for(int i = k; i < arr.size(); i++){ // 从第 k 个数开始比较,更新堆中的元素
if(arr[i] < Stack.top()){
Stack.pop();
Stack.push(arr[i]);
}
}
// 比较完毕后将堆中的元素存储在结果数组中
for(int i = 0; i < k; i++){
Res.push_back(Stack.top());
Stack.pop();
}
return Res;
}
private:
std::priority_queue<int> Stack; // 维护的堆,priority queue默认数值大的优先级高,即数值大的在队头
std::vector<int> Res;
};
int main(int argc, char *argv[]){
std::vector<int> test = {3,2,1};
int k = 2;
Solution S1;
std::vector<int> Res = S1.getLeastNumbers(test, k);
for(int item : Res){
std::cout << item << " ";
}
return 0;
}