[经验分享] 覃超线上直播课-模拟面试

本文为2020年07月09日 本文为覃超关于模拟面试的线上直播课的听课笔记。

正确对待面试

  • 作为和未来同事的一次合作
  • 并肩作战,解决问题
  • 减少压力(一定要积极的沟通和表达)

所以一定要积极的沟通和表达

注意:题目的熟练程度

技术面试

  • 项目经验:要点,深度
  • 基础知识:语言、数据库、并发、框架知识等
  • 算法和数据结构。(重点)60%~70%

面试题目

线程和进程

线程:没有独立的地址空间,与进程以及其他线程共享一个地址空间,它是系统任务调度的最小单元(轻量级进程);进程:有独立的地址空间。

多进程场景:监控进程系统(启动服务,然后监控服务是否退出,把服务重新启动);多线程的场景:高并发(nginx、tars框架)。

并发和并行:

并发:两个或者两个以上的事件在同一时间段内发生(是指微观的时间段,非常短,几乎感觉不出时间发生的时间差。)

并行:指两个或者两个以上的事件在同一时刻发生。并行意味着应用程序拆分成更小的子任务,这些任务可以并行处理,例如在多个CPU在同一时间。

同步和异步:

同步:下一个任务依赖上一个任务结束之后才能进行。

异步:等待某件事情的过程中继续做自己的事,不需要等待这一事件完成后再工作。

线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等 待另一线程的完成,从而可以让主线程干其它的事情。

并发和并行其实是异步线程实现的两种形式。并行其实是真正的异步,多核 CPU可以同时开启多条线程供多个任务同时执行,互不干扰。但是并发就不一 样了,是一个伪异步。在单核CUP中只能有一条线程,但是又想执行多个任务。 这个时候,只能在一条线程上不停的切换任务。

算法题目

题目一:20.Valid Parentheses

class Solution {
 public:
  bool isValid(string s) {
    if (s.empty()) {
      return true;
    }

    std::stack<char> stack;
    for (char i : s) {
      if (i == '}' || i == ']' || i == ')') {
        // 如果栈已经空了,放入反括号,一定不合法。
        // 另外,如果这里不做判断,下面的stack.top()将访问未知地址,导致异常
        if (stack.empty()) {
          return false;
        }
        if ( (i == '}' && stack.top() == '{')
            || (i == ')' && stack.top() == '(')
            || (i == ']' && stack.top() == '[')) {
          stack.pop();
        } else {
          return false;
        }
      } else {
        stack.push(i);
      }
    }
    return stack.empty();
  }
};

AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000383e02 bp 0x7ffe462fb990 sp 0x7ffe462fb880 T0)

特别注意:如果stack为空,则stack.top()将访问未知的地址,会引发异常(it is meaningless to have references to the top element when the stack is empty)。因此,进行stack.top()操作之前,必须确保stack不为空。

题目二:70.Climbing Stairs

class Solution {
 public:
  int climbStairs(int n) {
  	if (n < 3) {
  		return n;
  	}
  	int f1 = 1;
  	int f2 = 2;
  	int f3 = 3;
  	for (int i = 3; i <= n; i++) {
  		f3 = f1 + f2;
  		f1 = f2;
  		f2 = f3;
  	}
  	return f3;
  }
};

这道题特别经典,变形很多。一定要重视。

  • 如果n种走法?
  • 如果相邻走法不能重复?
  • 如何打印出所有走法?

题目三:50.Pow(x, n)

解法1:暴力求解

class Solution1 {
public:
    double myPow(double x, int n) {
        long long N = n; // 变成长整形,处理负数越界的常用方法
        if (N < 0) {
            N = -N;
            x = 1 / x;
        }
        
        double res = 1.0;
        for (int i = 0; i < N; i++) {
            res *= x;
        }
        return res;
    }
};

Time Limit Exceeded,无法AC

Line 21: Char 35: runtime error: negation of -2147483648 cannot be represented in type ‘int’; cast to an unsigned type to negate this value to itself (solution.cpp)

注意:int类型的范围n∈[−2147483648,2147483647],如果n=−2147483648,执行-n就会出现越界,所以转为long来操作就安全了。将 x 存入 long 变量 y ,后面用 y 操作即可。

解法2:快速幂-递归+分治法(自顶向下)

  • 关键字:比O(n)更好的解法
  • 模式识别:分治法实现计算量减半
  • 时间复杂度:O(logn)
  • 空间复杂度:O(logn) (利用递归,存在递归栈)
class Solution2 {
  public:
  	double myPowHelper(double x, long long n) {
      if (n == 0) {
        return 1;
      }
    
      double half = myPowHelper(x, n/2);
      if (n % 2 != 0) {
        return half * half * x;
      } else {
        return half * half;
      }
    }
  	
  	double myPow(double x, int n) {
      if (n == 0 || x == 1) {
        return 1;
      }
      long long N = n;
      if (n < 0) {
        return 1 / myPowHelper(x, -N);
      } else {
        return myPowHelper(x, N);
      }
    }
};

改写为位运算

class Solution2 {
  public:
  	double myPowHelper(double x, long long n) {
      if (n == 0) {
        return 1;
      }
    
      double half = myPowHelper(x, n >> 1);
      if (n & 1 != 0) {
        return half * half * x;
      } else {
        return half * half;
      }
    }
  	
  	double myPow(double x, int n) {
      if (n == 0 || x == 1) {
        return 1;
      }
      long long N = n;
      if (n < 0) {
        return 1 / myPowHelper(x, -N);
      } else {
        return myPowHelper(x, N);
      }
    }
};

解法3:快速幂-循环递推(自底向上)

class Solution {
public:
    double myPow(double x, int n) {
        long N = n;
        if (N < 0) {
            N = -N;
        } 
      
        double res = 1.0;
        for (long i = N; i != 0; i >>= 1) {
            if (i & 1) {
                res *= x; // 不同二进制位的权重进行累加(当且仅当1才可累加)
            } 
            x *= x;	// 对应二进制位的权重(对于二进制而言,高位是相邻低位的2倍)
        }
        return n > 0 ? res : 1/res;
    }
};

改写为while循环

class Solution {
public:
    double myPow(double x, int n) {
        long N = n;
        if (N < 0) {
            N = -N;
        } 
      
        double res = 1.0;
        while (N) {
            if (N & 1) {
                res *= x;
            } 
            x *= x;
          	N >>= 1;
        }
        return n > 0 ? res : 1/res;
    }
};

题目四:112. Path Sum

class Solution {
 public:
  bool hasPathSum(TreeNode* root, int sum) {
    if (!root) {
      return false;
    }

    // recursion terminator
    if (!root->left && !root->right) {
      return sum == root->val;
    }
    return hasPathSum(root->left, sum - root->val) ||
    hasPathSum(root->right, sum - root->val);

  }
};

变形一:不需要到叶子节点,可以在任意节点结束。

class SolutionVariation1 {
 public:
  bool hasPathSum(TreeNode* root, int sum) {
    if (!root) {
      return false;
    }

    // recursion terminator
    if (sum == root->val) {
      return true;
    }
    
    // return sum == root->val;
    
    return hasPathSum(root->left, sum - root->val) ||
    hasPathSum(root->right, sum - root->val);

  }
};

变形二:不需要到叶子节点,可以在任意节点结束。并且也不需要从根节点开始,可以从任意节点开始。

// 思路:把所有的节点都看做根节点进行遍历
// 看不懂,不理解
class SolutionVariation2 {
 public:

  bool hasPathSum(TreeNode* root, int sum) {
    if (!root) {
      return false;
    }

    // recursion terminator
    return hasSubPathSum(root, sum)
      || hasPathSum(root->left, sum)
      || hasPathSum(root->right, sum);
  }

  bool hasSubPathSum(TreeNode* root, int sum) {
    if (!root) {
      return false;
    }

    if (root->val == sum) {
      return true;
    }
    return hasSubPathSum(root->left, sum - root->val) ||
        hasSubPathSum(root->right, sum - root->val);
  }

};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值