目录
3sum
思路:注意去重
时间复杂度:O(nlogn)
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> threeSum(vector<int>& nums) {
if(nums.size() < 3) return res;
sort(nums.begin(),nums.end());
int i = 0;
while(i < nums.size()){
twoSum(nums,-nums[i],i+1);
// 去重
while(i+1 < nums.size() && nums[i+1] == nums[i]) i++;
i++;
}
return res;
}
void twoSum(vector<int>& nums,int target,int begin){
int i = begin,j = nums.size()-1,sum;
while(i < nums.size() && j >= begin && i < j){
sum = nums[i]+nums[j];
if(sum < target) i++;
else if(sum > target) j--;
else{
vector<int> out = {-target,nums[i],nums[j]};
res.push_back(out);
// 去重
while(i+1 < nums.size() && nums[i+1] == nums[i]) i++;
i++;
while(j-1 >= begin && nums[j-1] == nums[j]) j--;
j--;
}
}
}
};
3Sum Closest
Given an array nums
of n integers and an integer target
, find three integers in nums
such that the sum is closest to target
. Return the sum of the three integers. You may assume that each input would have exactly one solution.
Example:
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
思路:
逐一产生3sum然后比较即可
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int closest = nums[0]+nums[1]+nums[2];
for(int i = 0;i < nums.size();i++){
int j = i+1,k = nums.size()-1,sum;
while(j < k){
sum = nums[i]+nums[j]+nums[k];
if(sum < target) j++;
else if(sum > target) k--;
else return target;
if(abs(target-sum) < abs(target-closest)) closest = sum;
}
while(i+1 < nums.size() && nums[i+1] == nums[i]) i++;
}
return closest;
}
};
有向图是否有环
关于拓扑:https://blog.csdn.net/yanbao4070/article/details/81014566
if __name__ == "__main__":
v = [[0, 0, 0, 0, 1], [1, 0, 0, 0, 0], [0, 0, 0, 1, 1], [0, 0, 0, 0, 0], [0, 1, 0, 0, 0]]
in_v = {}
cnt = 0
visited = set()
circle = []
# 计算每个点的入度
for i in range(len(v)):
for j in range(len(v)):
if v[i][j] == 1:
if j not in in_v.keys():
in_v[j] = 0
in_v[j] += 1
# 拓扑
# 入度为0的点,加入到visited中
change = True
while change:
change = False
for node in range(len(v)):
if node not in visited and (node not in in_v.keys() or in_v[node] == 0):
change = True
visited.add(node)
for j in range(len(v)):
if v[node][j] == 1:
in_v[j] -= 1
# 没有被visited的,即为环
for node in range(len(v)):
if node not in visited:
circle.append(node)
print(circle)
判断链表是否有环
思路:快慢指针经典题(所有类型的分析:https://www.cnblogs.com/hiddenfox/p/3408931.html)
(没细看,也可以看看:http://www.cnblogs.com/wuyuegb2312/p/3183214.html)
设两个指针,一个每次走一步的慢指针和一个每次走两步的快指针,如果链表里有环的话,两个指针最终肯定会相遇。(为什么有环的情况下二者一定会相遇呢?因为fast先进入环,在slow进入之后,如果把slow看作在前面,fast在后面每次循环都向slow靠近1,所以一定会相遇,而不会出现fast直接跳过slow的情况)
struct ListNode{
int val;
ListNode *next;
ListNode(int x){
val = x;
next = NULL;
}
};
bool is_linked(ListNode *head){
if(!head || !head->next) return false;
ListNode *slow = head,*fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(fast == slow) return true;
}
return false;
}
二叉树最大深度
递归即可
/**
* 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:
int maxDepth(TreeNode* root) {
if(!root) return 0;
return 1+max(maxDepth(root->left),maxDepth(root->right));
}
};
二叉树路径和是否存在给定的值
/**
* 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:
bool hasPathSum(TreeNode* root, int sum) {
if(!root) return false;
if(!root->left && !root->right) return root->val == sum;
return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
}
};
二叉树路径和为给定的值的所有路径(dfs)
struct tree_node{
int val;
tree_node *left,*right;
tree_node(int x){
val = x;
left = NULL;
right = NULL;
}
};
class solution{
public:
vector<vector<int>> res;
vector<vector<int>> get_path(tree_node *root,int sum){
if(!root) return res;
vector<int> out;
dfs(root,sum,out);
return res;
}
void dfs(tree_node *root,int sum,vector<int> out){
if(!root) return;
if(!root->left && !root->right && root->val == sum){
out.push_back(root->val);
res.push_back(out);
} else{
out.push_back(root->val);
dfs(root,sum-root->val,out);
}
}
};
二分查找实现
#include <vector>
using namespace std;
int binary_search(vector<int> &nums,int target){
int l = 0,r = int(nums.size())-1,mid;
// 这里是小于等于
while(l <= r){
mid = (l+r)/2;
// 这里是有+1,-1的,而不是纯粹的mid
if(target < nums[mid]) r = mid-1;
else if(target > nums[mid]) l = mid+1;
else return mid;
}
return 0;
}
int main(){
vector<int> nums = {2,3,4,6,7,8,9};
int res = binary_search(nums,5);
cout << res << endl;
}
二叉树时间复杂度分析
二叉树先序遍历非递归
使用栈,先右后左进栈即可
/**
* 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> preorderTraversal(TreeNode* root){
vector<int> res;
if(!root) return res;
stack<TreeNode*> s{{root}};
TreeNode *cur;
while(!s.empty()){
cur = s.top();s.pop();
res.push_back(cur->val);
// 先右后左
if(cur->right) s.push(cur->right);
if(cur->left) s.push(cur->left);
}
return 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;
if(!root) return res;
stack<TreeNode*> s;
TreeNode *cur = root;
while(cur || !s.empty()){
if(cur){
s.push(cur);
// 先序是在这里,中序是在下面
res.push_back(cur->val);
cur = cur->left;
}
else{
cur = s.top(); s.pop();
cur = cur->right;
}
}
return 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;
if(!root) return res;
stack<TreeNode*> s;
TreeNode *cur = root;
while(cur || !s.empty()){
// 存在当前节点,就入栈,注意这里是if判断
if(cur){
s.push(cur);
cur = cur->left;
}
// 否则从栈里取,去访问,并且转向右孩子
else{
cur = s.top(); s.pop();
res.push_back(cur->val);
cur = cur->right;
}
}
return res;
}
};
二叉树中序遍历不用栈
由于我们既不能用递归,又不能用栈,那我们如何保证访问顺序是中序遍历的左-根-右呢。原来我们需要构建一个线索二叉树,我们需要将所有为空的右子节点指向中序遍历的下一个节点,这样我们中序遍历完左子结点后,就能顺利的回到其根节点继续遍历了。这里的重点是,对于根节点,它的中序遍历的前置节点是:根节点左子树的最右节点
如上图为一个线索二叉树,我们先到根节点1,(1)然后第一次为节点1的左子树的最右节点4设置右孩子,将右孩子指向1,然后我们继续向左前进,直到我们来到4,4没有左孩子,所有先被遍历到,然后我们通过4的右孩子,(2)到达1,此时检查1的左子树的最右节点的右孩子非空,且与1相同,并且将4的右孩子再次置为NULL,不改变树原本的结构。然后再去对1进行访问(达成中序遍历的目的)
具体算法如下:
1. 初始化指针cur指向root
2. 当cur不为空时
- 如果cur没有左子结点
a) 打印出cur的值(即遍历到)
b) 将cur指针指向其右子节点
-反之,如果cur有左子结点
将pre指针指向cur的左子树中的最右子节点(即中序遍历时,根节点的前一个节点)
* (1)若pre不存在右子节点(即还未设置后继节点)
a) 将其右子节点指回cur
b) cur指向其左子节点
* (2)反之,pre存在右子节点,与cur相同
a) 将pre的右子节点置空
b) 打印cur的值
c) 将cur指针指向其右子节点
/**
* 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;
if(!root) return res;
TreeNode* cur = root,*pre;
while(cur){
if(!cur->left){
res.push_back(cur->val);
cur = cur->right;
}
else{
pre = cur->left;
while(pre->right && pre->right != cur) pre = pre->right;
if(pre->right != cur){
pre->right = cur;
cur = cur->left;
}
else{
// 注意这里将右孩子置空,否则会有非法访问(我也没想通为啥)
pre->right = NULL;
res.push_back(cur->val);
cur = cur->right;
}
}
}
return 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> postorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> s{{root}};
TreeNode *cur;
while(!s.empty()){
cur = s.top(); s.pop();
res.insert(res.begin(),cur->val);
if(cur->left) s.push(cur->left);
if(cur->right) s.push(cur->right);
}
return 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<vector<int>> printFromTopToBottom(TreeNode* root) {
vector<vector<int>> res;
if(!root) return res;
int depth = getDepth(root);
// 使用深度进行层次遍历递归
for(int i = 1;i <= depth;i++){
vector<int> out;
levelSearch(root,out,i);
res.push_back(out);
}
return res;
}
int getDepth(TreeNode* root){
if(!root) return 0;
return max(getDepth(root->left),getDepth(root->right)) + 1;
}
void levelSearch(TreeNode* root,vector<int> &out,int i){
if(i == 1 || !root){
if(root) out.push_back(root->val);
return;
}
levelSearch(root->left,out,i-1);
levelSearch(root->right,out,i-1);
}
};
汉诺塔问题
有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
(1)然后移动n号盘到z轴上,即
move(x,n,z);
(2)把x杆上的n个金盘利用y杆全部移到z杆
hanoi(n,x,z,y);
void hanoi(int n,char x,char y,char z){
if(n == 1)
move(x,1,z);
else{
hanoi(n-1,x,z,y);
move(x,n,z);
hanoi(n-1,y,x,z);
}
}