2.1 面试官谈基础知识
2.2 编程语言
- 程序员写代码老是基于某一种编程语言,所以技术面试的时候直接或者间接都会涉及至少一种编程语言。在面试的过程当中,面试官要么直接问语言的语法,要么让应聘者用-~种编程语言写代码解决一个问题,经过写出的代码来判断应聘者对他使用的语言的掌握程度。如今流行的编程语言不少,不一样公司开发用的语言也不尽相同。作底层开发好比常常写驱动的人更习惯用C, Linux下有不少程序员用C++开发应用程序,基于Windows的C#项目已经愈来愈多,跨平台开发的程序员则可能更喜欢Java,随着苹果iPad、iPhone 的热销已经有不少程序员投向了Objective C的阵营,同时还有不少人喜欢用脚本语言如Perl、Python 开发短小精致的小应用软件。所以,不一样公司面试的时候对编程语言的要求也有所不一样。每一种编程语言均可以写出一本大部头的书籍,本书限于篇幅不可能面面俱到。本书中全部代码都用C/C++/C#实现,下 面简要介绍一些C++/C#常见的面试题。
面试题1:赋值运算符函数
- 题目:以下为类型CMyString的声明,请为该类型添加赋值运算符函数。
- 思路
-
将返回值类型声明为该类型的引用
-
把传入的参数类型声明为常量引用
-
释放实例自身已有的内存
-
判断传入的参数和当前的实例是否是同一个实例
- 不适用java
面试题2:实现Singleton模式
- 题目:设计一个类,咱们只能生成该类的一个实例。
- http://www.javashuo.com/article/p-fpcfqeab-gh.html
- 1. 懒汉模式
- public class SingletonDemo {
- private static SingletonDemo instance;
- private SingletonDemo(){
- }
- public static SingletonDemo getInstance(){
- if(instance==null){
- instance=new SingletonDemo();
- }
- return instance;
- }
- }
- 如上,经过提供一个静态的对象instance,利用private权限的构造方法和getInstance()方法来给予访问者一个单例。缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。之因此叫作懒汉模式,主要是由于此种方法能够很是明显的lazy loading。针对懒汉模式线程不安全的问题,咱们天然想到了,在getInstance()方法前加锁,因而就有了第二种实现。
- 2. 线程安全的懒汉模式
- public class SingletonDemo {
- private static SingletonDemo instance;
- private SingletonDemo(){
- }
- public static synchronized SingletonDemo getInstance(){
- if(instance==null){
- instance=new SingletonDemo();
- }
- return instance;
- }
- }
- 然而并发实际上是一种特殊状况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低。
- 3. 饿汉模式
- public class SingletonDemo {
- private static SingletonDemo instance=new SingletonDemo();
- private SingletonDemo(){
- }
- public static SingletonDemo getInstance(){
- return instance;
- }
- }
- 直接在运行这个类的时候进行一次loading,以后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。
- 4. 静态类内部加载
- public class SingletonDemo {
- private static class SingletonHolder{
- private static SingletonDemo instance=new SingletonDemo();
- }
- private SingletonDemo(){
- System.out.println("Singleton has loaded");
- }
- public static SingletonDemo getInstance(){
- return SingletonHolder.instance;
- }
- }
- 使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了相似懒汉模式的效果,而这种方法又是线程安全的。
- 本题考点:
-
- 考查对单例(Singleton) 模式的理解。
- 考查对Java的基础语法的理解,如静态构造函数等。
- 考查对多线程编程的理解。
2.3 数据结构
数据结构一直是技术面试的重点,大多数面试题都是围绕着数组、字符串、链表、树、栈及队列这几种常见的数据结构展开的,所以每个应聘者都要熟练掌握这几种数据结构。
数组和字符串是两种最基本的数据结构,它们用连续内存分别存储数字和字符。链表和树是面试中出现频率最高的数据结构。因为操做链表和树须要操做大量的指针,应聘者在解决相关问题的时候-要留意代码的鲁棒性,不然容易出现程序崩溃的问题。是一个与递归紧密相关的数据结构,一样队列也与广度优先遍历算法紧密相关。深入理解这两种数据结构能帮助咱们解决不少算法问题。
2.3.1 数组
面试题3:二维数组中的查找
- 测试用例:
-
- 二维数组中包含查找的数字(查找的数字是数组中的最大值和最小值,查找的数字介于数组中的最大值和最小值之间)。
- 二维数组中没有查找的数字(查找的数字大于数组中的最大值,查找的数字小于数组中的最小值,查找的数字在数组的最大值和最小值之间但数组中没有这个数字)。
- 特殊输入测试(输入空指针)。
- 本题考点:
-
- 考查应聘者对二维数组的理解及编程能力。二维数组在内存中占据连续的空间。在内存中从上到下存储各行元素,在同一-行中按照从左到右的顺序存储。所以咱们能够根据行号和列号计算出相对于数组首地址的偏移量,从而找到对应的元素。
- 考查应聘者分析问题的能力。当应聘者发现问题比较复杂时,能不能经过具体的例子找出其中的规律,是可否解决这个问题的关键所在。这个题目只要从一个具体的二维数组的右上角开始分析,就能找到查找的规律,从而找到解决问题的突破口。
2.3.2 字符串
面试题4:替换空格
- 题目:请实现一个函数,把字符串中的每一个空格替换成"%20"。例如输入“We are happy.",则输出“We%20are%20happy.”。
- 思路:从后往前复制,数组长度会增长,或使用StringBuilder、StringBuffer类 , 先遍历空格个数直接扩容
- 代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(replaceSpace(newStringBuffer("We Are Happy.")));
- }
- publicstaticString replaceSpace(StringBuffer str) {
- if(str== null)
- returnnull;
- StringBuilder sb= newStringBuilder();
- for(inti= 0; i< str.length(); i++) {
- if(String.valueOf(str.charAt(i)).equals(" ")) {
- sb.append("%20");
- } else{
- sb.append(str.charAt(i));
- }
- }
- returnString.valueOf(sb);
- }
- }
- 方法二
- "We Are Happy.".replace(" ", "%20")
- 测试用例:
-
- 输入的字符串中包含空格(空格位于字符串的最前面,空格位于字符串的最后面,空格位于字符串的中间,字符串中有连续多个空格)。
- 输入的字符串中没有空格。
- 特殊输入测试(字符串是个NULL指针、字符串是个空字符串、字符串只有一个空格字符、字符串中只有连续多个空格)。
- 本题考点:
-
- 考查对字符串的编程能力。
- 考查分析时间效率的能力。咱们要能清晰地分析出两种不一样方法的时间效率各是多少。
- 考查对内存覆盖是否有高度的警戒。在分析得知字符串会变长之后,咱们可以意识到潜在的问题,并主动和面试官沟通以寻找问题的解决方案。
- 考查思惟能力。在从前到后替换的思路被面试官否认以后,咱们能迅速想到从后往前替换的方法,这是解决此题的关键。
- 相关题目:
-
- 有两个排序的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2。请实现一个函数,把A2中的全部数字插入到A1中而且全部的数字是排序的。和前面的例题同样,不少人首先想到的办法是在A1中从头至尾复制数字,但这样就会出现屡次复制一个数字的状况。更好的办法是从尾到头比较A1和A2中的数字,并把较大的数字复制到A1的合适位置。
- 触类旁通:
- 合并两个数组(包括字符串)时,若是从前日后复制每一个数字(或字符)须要重复移动数字(或字符)屡次,那么咱们能够考虑从后往前复制,这样就能减小移动的次数,从而提升效率。
2.3.3 链表
咱们说链表是一种动态数据结构,是由于在建立链表时,无须知道链
表的长度。当插入一一个结点时,咱们只须要为新结点分配内存,而后调整指针的指向来确保新结点被连接到链表当中。内存分配不是在建立链表时.次性完成,而是每添加一个结点分配一 次内存。因为没有闲置的内存,链表的空间效率比数组高。
面试题5:从尾到头打印链表
- 题目:输入一个链表的头结点,从尾到头反过来打印出每一个结点的值。
- 面试小提示:在面试中若是咱们打算修改输入的数据,最好先问面试官是否是容许作修改。
-
思路:借助栈实现,或使用递归的方法。
- 代码实现
- publicArrayList<Integer> printListFromTailToHead(ListNodelistNode) {
- ArrayList<Integer> list= newArrayList<>();
- if(listNode== null)
- returnlist;
- Stack<ListNode> stack= newStack<>();
- while(listNode!= null) {
- stack.push(listNode);
- listNode= listNode.next;
- }
- while(!stack.isEmpty()) {
- list.add(stack.pop().val);
- }
- returnlist;
- }
- 简单演示 栈
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- ArrayList<Integer> arr= newArrayList<Integer>();
- arr.add(1);
- arr.add(2);
- arr.add(3);
- arr.add(4);
- arr.add(5);
- printListFromTailToHead(arr);
- }
- publicstaticvoidprintListFromTailToHead(ArrayList<Integer> list) {
- Stack<Integer> stack= newStack<Integer>();
- for(Integer integer: list) {
- stack.push(integer);
- }
- while(!stack.isEmpty()) {
- System.out.println(stack.pop());
- }
- }
- }
- 简单演示 递归
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- int[] arr= {1,2,3,4,5};
- printListFromTailToHead(arr,0);
- }
- publicstaticvoidprintListFromTailToHead(int[] arr,intnum) {
- if(arr.length> num){
- printListFromTailToHead(arr,num+1);
- System.out.println(arr[num]);
- }
- }
- }
- 测试用例:
- 功能测试(输入的链表有多个结点,输入的链表只有一个结点)。
- 特殊输入测试(输入的链表头结点指针为NULL)。
- 本题考点:
- 考查对单项链表的理解和编程能力。
- 考查对循环、递归和栈3个相互关联的概念的理解。
2.3.4 树
树是一种在实际编程中常常遇到的数据结构。它的逻辑很简单:除了根结点以外每一个结点只有一个父结点,根结点没有父结点;除了叶结点以外全部结点都有一个或多个子结点,叶结点没有子结点。父结点和子结点之间用指针连接。因为树的操做会涉及大量的指针,所以与树有关的面试题都不太容易。当面试官想考查应聘者在有复杂指针操做的状况下写代码的能力,他每每会想到用与树有关的面试题。
面试的时候提到的树,大部分都是二叉树。所谓二叉树是树的一种特殊结构,在二叉树中每一个结点最多只能有两个子结点。在二叉树中最重要的操做莫过于遍历,即按照某一-顺序访问树中的全部结点。一般树有以下几种遍历方式:
- 前序遍历:先访问根结点,再访问左子结点,最后访问右子结点。图2.5中的二叉树的前序遍历的顺序是十、六、四、八、1四、十二、16。
- 中序遍历:先访问左子结点,再访问根结点,最后访问右子结点。图2.5中的二叉树的中序遍历的顺序是四、六、八、十、十二、1四、16。
- 后序遍历:先访问左子结点,再访问右子结点,最后访问根结点。图2.5中的二叉树的后序遍历的顺序是四、八、六、十二、1六、1四、10。
面试题6:重建二叉树
- 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
-
思路:先找出根节点,而后利用递归方法构造二叉树
- 代码实现
- classTreeNode {
- intval;
- TreeNode left;
- TreeNode right;
- TreeNode(intx) {
- val= x;
- }
- }
- publicclassTestc {
- publicTreeNode reConstructBinaryTree(int[] pre, int[] in) {
- if(pre== null|| in== null) {
- returnnull;
- }
- if(pre.length== 0 || in.length== 0) {
- returnnull;
- }
- if(pre.length!= in.length) {
- returnnull;
- }
- TreeNode root= newTreeNode(pre[0]);
- for(inti= 0; i< pre.length; i++) {
- if(pre[0] == in[i]) {
- root.left= reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+ 1), Arrays.copyOfRange(in, 0, i));
- root.right= reConstructBinaryTree(Arrays.copyOfRange(pre, i+ 1, pre.length),
- Arrays.copyOfRange(in, i+ 1, in.length));
- }
- }
- returnroot;
- }
- }
- 测试用例:
- 普通二叉树(彻底二叉树,不彻底二叉树)。
- 特殊二叉树(全部结点都没有右子结点的二叉树,全部结点都没有左子结点的二叉树,只有一一个结点的二叉树)。
- 特殊输入测试(二叉树的根结点指针为NULL、输入的前序遍历序列和中序遍历序列不匹配)。
- 本题考点:
2.3.5 栈和队列
栈是一个很是常见的数据结构,它在计算机领域中被普遍应用,
好比操做系统会给每一个线程建立-一个栈用来存储函数调用时各个函数的参数、返回地址及临时变量等。栈的特色是后进先出,即最后被压入(push)栈的元素会第一个被弹出(pop)。 在面试题22“栈的压入、弹出序列”中,咱们再详细分析进栈和出栈序列的特色。
一般栈是一“个不考虑排序的数据结构,咱们须要O(n)时间才能找到栈中最大或者最小的元素。若是想要在O(1)时间内获得栈的最大或者最小值,咱们须要对栈作特殊的设计,详见面试题21“包含min函数的栈”。
队列是另一种很重要的数据结构。和栈不一样的是,队列的特色是先进先出,即第一个进入队列的元素将会第一个出来。 在2.3.4节介绍的树的宽度优先遍历算法中,咱们在遍历某一层树的结点时,把结点的子结点放到一个队列里,以备下一层结点的遍历。详细的代码参见面试题23“从上到下遍历二叉树”。
栈和队列虽然是特色针锋相对的两个数据结构,但有意思的是它们却相互联系。请看面试题7“用两个栈实现队列”,同时读者也能够考患如何用两个队列实现栈。
面试题7:用两个栈实现队列
2.4 算法和数据操做
和数据结构同样,考查算法的面试题也备受面试官的青睐,其中排序和查找是面试时考查算法的重点。在准备面试的时候,咱们应该重点掌握二分查找、归并排序和快速排序,作到能随时正确、完整地写出它们的代码。
有不少算法均可以用递归和循环两种不一样的方式实现。一般基于递归的实现方法代码会比较简洁,但性能不如基于循环的实现方法。在面
试的时候,咱们能够根据题目的特色,甚至能够和面试官讨论选择合适.的方法编程。
位运算能够当作是一- 类特殊的算法,它是把数字表示成二进制以后对0和1的操做。因为位运算的对象为二进制数字,因此不是很直观,但掌握它也不难,由于总共只有与、或、异或、左移和右移5种位运算。
2.4.1 查找和排序
查找和排序都是在程序设计中常常用到的算法。查找相对而言较为简
单,不外乎顺序查找、二分查找、哈希表查找和二叉排序树查找。在试
的时候,无论是用循环仍是用递归,面试官都期待应聘者可以信手拈来写出完整正确的二分查找代码,不然可能连继续面试的兴趣都没有。
- 面试小提示:若是面试题是要求在排序的数组( 或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数,咱们均可以尝试用二分查找算法。
面试题8:旋转数组的最小数字
- 题目:把一个数组最开始的若干个元素搬到数组的末尾,咱们称之为数组的旋转。 输入一个非递减排序的数组的一 个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的全部元素都大于0,若数组大小为0,请返回0
- 思路:
- 代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(minNumberInRotateArray(newint[] { 1, 0, 1, 1, 1 }));
- }
- publicstaticintminNumberInRotateArray(int[] array) {
- if(array== null|| array.length== 0)
- return0;
- intleft= 0;
- intright= array.length- 1;
- intmid= 0;
- while(array[left] >= array[right]) {
- if(right- left<= 1) {
- mid= right;
- break;
- }
- mid= (left+ right) / 2;
- if(array[left] == array[mid] && array[mid] == array[right]) {
- if(array[left+ 1] != array[right- 1]) {
- intreslut= array[left];
- for(inti= left+ 1; i<= right; i++) {
- if(reslut>array[i]) {
- reslut= array[i];
- }
- }
- returnreslut;
- }
- } else{
- if(array[left] <= array[mid]) {
- left= mid;
- } else{
- right= mid;
- }
- }
- }
- returnarray[mid];
- }
- }
- 测试用例:
- 功能测试(输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字)。
- 边界值测试(输入的数组是一一个升序排序的数组、只包含一一个数字的数组)。
- 特殊输入测试(输入NULL指针)。
- 本题考点:
- 考查对二分查找的理解。本题变换了二分查找的条件,输入的数组不是排序的,而是排序数组的一个旋转。这要求咱们对二分查找的过程有深入的理解。
- 考查沟通学习能力。本题面试官提出了一个新的概念:数组的旋转。咱们要在很短期内学习理解这个新概念。在面试过程当中若是面试官提出新的概念,咱们能够主动和面试官沟通,多问几个问题把概念弄清楚。
- 考查思惟的全面性。排序数组自己是数组旋转的一一个特例。另外,咱们要考虑到数组中有相同数字的特例。若是不能很好地处理这些特例,就很难写出让面试官满意的完美代码。
2.4.2 递归和循环
若是咱们须要重复地屡次计算相同的问题,一般能够选择用递归或者循环两种不一样的方法。递归是在一一个函数的内部调用这个函数自身。而循环则是经过设置计算的初始值及终止条件,在一个范围内重复运算。
面试小提示:一般基于递归实现的代码比基于循环实现的代码要简洁不少,更加容易实现。若是面试官没有特殊要求,应聘者能够优先采用递归的方法编程。
- 递归虽然有简洁的优势,但它同时也有显著的缺点。递归因为是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都须要在内存栈中分配空间以保存参数、返回地址及临时变量,并且往栈里压入数据和弹出数据都须要时间。这就不难理解.上述的例子中递归实现的效率不如循环。
- 另外,递归中有可能不少计算都是重复的,从而对性能带来很大的负面影响。递归的本质是把一一个问题分解成两个或者多个小问题。若是多个小问题存在相互重叠的部分,那么就存在重复的计算。在面试题9“斐波那契数列”及面试题43“n个骰子的点数”中咱们将详细地分析递归和循环的性能区别。
- 除了效率以外,递归还有可能引发更严重的问题:调用栈溢出。前面分析中提到须要为每一次函数调用在内存栈中分配空间,而每一个进程的栈的容量是有限的。当递归调用的层级太多时,就会超出栈的容量,从而致使调用栈溢出。在上述例子中,若是输入的参数比较小,如10,它们都能返回结果55。但若是输入的参数很大,如5000,那么递归代码在运行的时候就会出错,但运行循环的代码能获得正确的结果12502500。
面试题9:斐波那契数列
- 题目一:写一个函数,输入n,求斐波那契( Fibonacci )数列的第n项斐波那契数列的定义以下:
- 思路:递归实现效率低,循环实现。
- 循环代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(fibonacci(9));
- }
- publicstaticlongfibonacci(intn) {
- longresult= 0;
- longpreOne= 1;
- longpreTwo= 0;
- if(n== 0) {
- returnpreTwo;
- }
- if(n== 1) {
- returnpreOne;
- }
- for(inti= 2; i<= n; i++) {
- result= preOne+ preTwo;
- preTwo= preOne;
- preOne= result;
- }
- returnresult;
- }
- }
- 递归代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(fibonacci(9));
- }
- publicstaticlongfibonacci(intn) {
- if(n< 0) {
- return0;
- }
- if(n== 1) {
- return1;
- }
- returnfibonacci(n- 1) + fibonacci(n- 2);
- }
- }
- 题目二:一只青蛙一次能够跳上1级台阶,也能够跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法
- 把问题一 0 1 改为 1 2
- 假设,一级台阶,有f(1)种方法,二级有f(2)种,以此类推,n级有f(n)种方法。
- 能够看出,f(1)=1;f(2)=2。
- 那么,假设n级台阶,那么第一步就有两种状况,跳一步,跟跳两步。
- 状况一:跳一步,那么接下去的就是f(n-1);
- 状况二:跳两步,那么接下去的就是f(n-2)。
- 测试用例:
- 功能测试(如输入三、五、10 等)。
- 边界值测试(如输入0、一、2)。
- 性能测试(输入较大的数字,如40、50、100 等)。
- 本题考点:
- 考查对递归、循环的理解及编码能力。
- 考查对时间复杂度的分析能力。
- 若是面试官采用的是青蛙跳台阶的问题,那同时还在考查应聘者的数学建模能力。
2.4.3 位运算
五种运算:与、或、异或、左移、右移
面试题10:二进制中的1的个数
- 题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1。所以若是输入9,该函数输出2。
- 思路:
- 1.可能引发死循环的解法 判断尾数 右移。面试官接下来可能要问的第二个问题就是:上面的函数若是输入一个负数,好比0x80000000,运行的时候会发生什么状况?把负数0x80000000右移一位的时候 并非简单地把最高位的1移到第二位变成0x4000000而是0xC000000。这是由于移位前是个负数,仍然要保证移位后是个负数,所以移位后的最高位会设为1。若是一直作右移运算,最终这个数字就会变成0xFFFFFFFF而陷入死循环。
- 2.为了不死循环,咱们能够不右移输入的数字i。首先把i和1作与运算,判断i的最低位是否是为1。接着把1左移一-位获得2,再和i作与运算,就能判断i的次低位是否是.....这样反复左移,每次都能判断i的其中一位是否是1。
- 代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(NumberOf(5));
- }
- publicstaticintNumberOf(intn) {
- intcount= 0;
- intflag= 1;
- while(flag!= 0){
- if((n& flag) != 0){
- count++;
- }
- flag= flag<< 1;
- }
- returncount;
- }
- }
- 思路二:把一个整数减去1,再和原整数作与运算,会把该整数最右边一个1变成0。那么一一个整数的二进制表示中有多少个1,就能够进行多少次这样的操做。
- 代码实现
- publicclassTestc {
- publicstaticvoidmain(String[] args) {
- System.out.println(NumberOf(7));
- }
- publicstaticintNumberOf(intn) {
- intcount= 0;
- while(n> 0){
- if(n!= 0)
- n= n&(n-1);
- count++;
- }
- returncount;
- }
- }
- 测试用例:
- 正数(包括边界值一、0x7FFFFFFF)。
- 负数(包括边界值0x80000000、0xFFFFFFFF)。
- 本题考点:
- 考查对二进制及位运算的理解。
- 考查分析、调试代码的能力。若是应聘者在面试过程当中采用的是第
- 一种思路,当面试官提示他输入负数将会出现问题时,面试官会期待他能在心中运行代码,本身找出运行出现死循环的缘由。这要求应聘者有必定的调试功底。
- 相关题目:
- 用一条语句判断一“个整数是否是2的整数次方。一个整数若是是2的整数次方,那么它的二进制表示中有且只有一位是1,而其余全部位都是0。根据前面的分析,把这个整数减去1以后再和它本身作与运算,这个整数中惟一的1就会变成0。
- 输入两个整数m和n,计算须要改变m的二进制表示中的多少位才能获得n。好比10的二进制表示为1010, 13 的二进制表示为1101,须要改变1010中的3位才能获得1101。咱们能够分为两步解决这个问题:第一步求这两个 数的异或,第二步统计异或结果中1的位数。
- 触类旁通:
- 把一个整数减去1以后再和原来的整数作位与运算,获得的结果至关因而把整数的二进制表示中的最右边一个1变成0.不少二进制的问题均可以用这个思路解决。
2.5 本章小结
本章着重介绍应聘者在面试以前应该认真准备的基础知识。为了应对编程面试,应聘者须要从编程语言、数据结构和算法3方面作好准备。
面试官一般采用概念题、代码分析题及编程题这3种常见题型来考查应聘者对某一编程语言 的掌握程度。本章的2.2节讨论了C++/C#语言这3种题型的常见面试题。
数据结构题目一直是面试官考查的重点。数组和字符串是两种最基本的数据结构。链表应该是面试题中使用频率最高的一种数据结构。若是面试官想加大面试的难度,他颇有可能会选用与树(尤为是二叉树)相关的第2章面试须要的基础知识
面试题。因为栈与递归调用密切相关,队列在图(包括树)的宽度优先遍历中须要用到,所以应聘者也须要掌握这两种数据结构。
算法是面试官喜欢考查的另一个重点。查找(特别是二分查找)和排序(特别是快速排序和归并排序)是面试中最常常考查的算法,应聘者必定要熟练掌握。另外,应聘者还要掌握分析时间复杂度的方法,理解即便是同一思路,基于循环和递归的不一样实现它们的时间复杂度可能大不相同。
位运算是针对二进制数字的运算规律。只要应聘者熟练掌握了二进制的与、或、异或运算及左移、右移操做,就能解决与位运算相关的面试题。