牛客面经手撕算法常见题

目录

3sum

3Sum Closest

有向图是否有环

判断链表是否有环

二叉树最大深度

二叉树路径和是否存在给定的值

二叉树路径和为给定的值的所有路径(dfs)

二分查找实现

二叉树先序遍历非递归

二叉树中序遍历

使用栈

二叉树中序遍历不用栈

二叉树后序遍历

汉诺塔问题


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);
        }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值