1、栈的基础使用
1、给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
这道题是众多公司面试题:
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
这道题是众多公司面试题:
class Solution { public: bool isValid(string s) { stack<char> stack; for(int i = 0; i< s.size(); i++){ if(s[i] == '(' || s[i] == '{' || s[i] == '[') stack.push(s[i]); else{ if(stack.size() == 0) return false; char c = stack.top(); stack.pop(); char match; if(s[i] == ')') match = '('; else if(s[i] == ']') match = '['; else match = '{'; if(c != match) return false; } } if(stack.size() != 0) return false; return true; } };2、逆波兰表达式(后缀表达式求值)
从左向右扫描,遇到数字压栈,遇到操作符,弹出栈顶的两个元素,先弹出的元素在右边,后弹出来的在左边,进行计算后,将结果压栈,再往后扫描,直到扫描结束,输出栈顶元素,即为最终结果
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> stack; int i = 0; while(i<tokens.size()){ if(tokens[i]!="+" && tokens[i]!="-" && tokens[i]!="*" && tokens[i]!="/"){ stack.push(stoi(tokens[i])); } else{ int x,y; x = stack.top(); stack.pop(); y = stack.top(); stack.pop(); if(tokens[i] == "+") stack.push(y+x); else if(tokens[i] == "-") stack.push(y-x); else if(tokens[i] == "*") stack.push(y*x); else stack.push(y/x); } i++; } int res = stack.top(); return res; } };
2、栈和递归的紧密关系
1、二叉树中的算法给定一个二叉树,返回它的 前序 遍历。
1.1 这里模仿系统中的栈使用非递归的方式来实现
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; struct Command{ string s;//定义的命令:go,print TreeNode * node; Command(string s,Treenode *node):s(s),node(node){}//构造函数 }; class Solution{ public: vector<int> preorderTraversal(TreeNode* root) { vector<int> res; if(root == NULL) return res; stack<Command> stack; stack.push(Command("go",root)); while(!stack.empty()){ Command command = stack.top(); stack.pop(); if(command.s == "print") res.push_back(command.node->val); else{ assert(command.s == "go"); if(command.node->right) stack.push(Command("go",command.node->right)); if(command.node->left) stack.push(Command("go",command.node->left)); stack.push(Command("print",command.node)); } } return res; } };1.2 递归
class Solution { public: vector<int> preorderTraversal(TreeNode* root) { vector<int> res; helper(root,res); return res; } void helper(TreeNode* root,vector<int>& res){ if(root == NULL) return; res.push_back(root->val); helper(root->left,res); helper(root->right,res); } };1.3 迭代
class Solution { public: vector<int> preorderTraversal(TreeNode* root) { stack<TreeNode*> sk; vector<int> res; while(root||sk.size()){ while(root){ sk.push(root); res.push_back(root->val); root=root->left; } root=sk.top(); sk.pop(); root=root->right; } return res; } };2、二叉树中序遍历
递归:
/** * 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> inorderTraversal(TreeNode* root) { vector<int> res; inorder(root,res); return res; } void inorder(TreeNode *root,vector<int>& res){ if(root == NULL) return; inorder(root->left,res); res.push_back(root->val); inorder(root->right,res); } };
非递归:
/** * 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> inorderTraversal(TreeNode* root) { vector<int> res; stack<TreeNode*> stack; while(root || stack.size()){ while(root){ stack.push(root); root = root->left; } if(stack.size()){ root = stack.top(); stack.pop(); res.push_back(root->val); root = root->right; } } return res; } };3、二叉树后序遍历
递归:
/** * 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> postorderTraversal(TreeNode* root) { vector<int> res; postorder(root,res); return res; } void postorder(TreeNode *root,vector<int>& res){ if(root ==NULL) return; postorder(root->left,res); postorder(root->right,res); res.push_back(root->val); } };
非递归:
/** * 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> postorderTraversal(TreeNode* root) { stack<TreeNode*> stack,result; vector<int> res; if(root == NULL) return res; stack.push(root); while(stack.size()){ root = stack.top(); stack.pop(); result.push(root); if(root->left) stack.push(root->left); if(root->right) stack.push(root->right); } while(result.size()){ root = result.top(); result.pop(); res.push_back(root->val); } return res; } };
3、队列
/** * 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<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; if(root == NULL) return res; queue<pair<TreeNode*,int>> q; q.push(make_pair(root,0)); while(!q.empty()){ TreeNode *node = q.front().first; int level = q.front().second; q.pop(); if(level == res.size()) res.push_back(vector<int>()); res[level].push_back(node->val); if(node->left) q.push(make_pair(node->left,level+1)); if(node->right) q.push(make_pair(node->right,level+1)); } return res; } };2、二叉树的锯齿形遍历
给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]
3.2 BFS(广度优先遍历)和图的最短路径
1、给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
可以使用动态规划的方法来实现
class Solution { public: int numSquares(int n) { vector<int> dp(n + 1); for (int i = 1; i <= n; i++) { dp[i] = i; for (int j = 1; j * j <= i; j++) dp[i] = min(dp[i], dp[i - j * j] + 1); } return dp[n]; } };
127.126
3.3 优先队列(底层实现:堆)
1、给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
1.首先扫描一边数组并统计频率,排序找到前k个出现频率最高的元素
2.维护一个含有k个元素的优先队列。如果遍历到的元素比队列中的最小频率的元素的频率高,则取出队列中最小频率的元素,将新的元素入队。最终,队列中剩下的,就是前k个出现频率最高的元素。
class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k) { map<int,int> freq;//第一个是元素,第二个是出现的频率 //统计每个元素出现的频率 for(int i =0;i<nums.size();i++) freq[nums[i]] ++; //扫描freq,维护当前出现频率最高的k个元素 //在优先队列中,按照频率排序,所以数据对是(频率,元素)的形式 priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>> > pq; for(map<int,int>::iterator iter = freq.begin(); iter != freq.end();iter ++){ if(pq.size() == k){ if(iter->second > pq.top().first){ pq.pop(); pq.push(make_pair(iter->second,iter->first)); } }else{ pq.push(make_pair(iter->second,iter->first)); } } vector<int> res; while(!pq.empty()){ res.push_back(pq.top().second); pq.pop(); } return res; } };
2、合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { //vector中存放的是每个链表的头结点 if(lists.size() == 0) return NULL; if(lists.size() == 1) return lists[0]; ListNode *p = lists[0]; for(int i =1;i<lists.size();i++) p = merge2Lists(p,lists[i]); return p; } ListNode* merge2Lists(ListNode* l1,ListNode* l2){ ListNode *head = new ListNode(0); ListNode *p = head; while(l1&&l2){ if(l1->val < l2->val){ p -> next = l1; l1 = l1->next; } else{ p -> next = l2; l2 = l2 -> next; } p = p->next; } while(l1){ p->next = l1; l1 = l1->next; p = p->next; } while(l2){ p->next = l2; l2 = l2->next; p = p->next; } return head->next; } };