• 对面试题(剑指offer)产生的一些思考。


    零散的思绪。另外,推荐《剑指offer》。本文初期大部分思考都从剑指引发。

    面试题不单单只是用来面试。其中有很多编程的经验可以学习。就如同我们当年的考试:)

    1:鲁棒性的一个方面:边界条件和异常处理。

      1,鲁棒性:robust的中文音译。本身就是健全性。

      2,面试中,经常出现一些简单的代码。考验的就是一个程序员对边界条件和异常处理的考虑。

        例如:字符串转化为整数。本身题目非常简单。但,我们应该能够考虑到异常输入有:英文/未知符号,负数,空指针,超大数值。

        另外,需要明确题目。如果面试官的要求是:字符串转化为数字。需要问清楚,整数,还是浮点?。。。当然,也可能弄巧成拙,本来面试官只是想整数,但你既然问了,那就成全你吧:(

        如果是浮点,我们可能还要考虑,精度缺失的问题。我们就要考虑double的精度。

      3,对于链表,边界中最不容易考虑的应该是:链表项数。

        例如:查找倒数K项,但实际链表项数 < K。

    2:对数组操作的一些胡思乱想。

      1,C++数组最麻烦的一点是下标超出。我们当然可以用vector解决。但面试中,常常用这种事情来玩你。。。

      2,我对数组的遍历,经常是for循环+数组个数。数组各数通常使用宏定义。但,这并不能解决超出问题。比如:把14打成15这种低级错误。所以,是否能够有使用一种方式解决此问题?下面是我对此想法的一个尝试。

    /**********************************************************
     * file array_t.cpp
     * rief 
     * 
    ote 
     * 
     * version 
     * author zheng39562@163.com
    ***********************************************************/
    #include <iostream>
    
    using namespace std;
    
    int main( int argc, char **argv )
    {
        int array[] = { 0, 1, 2, 3, 4};
    
        int iNumOfarray = sizeof( array ) / sizeof( array[0] );
        cout << iNumOfarray << endl;
        for( int i=0; i<iNumOfarray; i++)
        {
            cout << array[i] << endl;
        }
    }

        1)此代码中,尝试用sizeof来解决循环问题。但我无法确定,此方法是否会有其他瑕疵。

        2)当然,更好的方法是使用vector。此代码仅是一个简单的讨论性问题。如果有人对此有什么想法。希望能够联系我(评论或邮件)。

        3)另外,此方法不使用将数组指针作为形参的函数,因为通过函数传递的数组,会在函数中自动退化成为普通的指针类型。

     3:对于C++常量字符串的一个疑惑。

    /**********************************************************
     * file string_t.cpp
     * rief 
     * 
    ote 
     * 
     * version 
     * author zheng39562@163.com
    **********************************************************/
    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int main( int argc, char **argv )
    {
        char str1[] = "Hello world.";
        char str2[] = "Hello world.";
    
        char *str3 = {"Hello world."};
        char *str4 = {"Hello world."};
    
        if( str1 == str2 )
            cout << "1==2" << endl;
        else
            cout << "1!=2" << endl;
    
        if( str3 == str4 )
            cout << "3==4" << endl;
        else
            cout << "3!=4" << endl;
    
        exit( 0 );
    }

        1)这段代码。最终执行的结果是:1!=2 3==4。剑指(指代剑指offer此书。本文不再重复)中指出,C++在常量字符串上进行了一定优化——str3/4指向同一个地址。我比较好奇的是,C++如何优化此问题。

        2)计算机如何将str3和str4识别成同一个字符串?

        3)此问题,可能涉及到编译,执行和代码内存分配等问题。个人暂时无法解答。留于此,作为记录。

    4:对于C++字符串的一些补充。

      1,一个空字符串中依然包含一个''。已经经过代码证实。

    5:移动数据的一种方法:当从前往后移动数据,可能需要重复多次时;最好反置进行移动。因为从前往后移动时,很多数据会经历多次向后移动的经历。这点,对于字符串和数组操作尤其有效。

    6:对于指针,必须判断其是否为NULL。在剑指中2.3.3链表的添加节点函数中,并没有对指向指针的指针进行判断。也许是因为经验性常识,但我个人认为此代码并不完美。因为指向指针的指针,也同样需要判断。

      1,存在一个疑问:对空指针取地址,得到的结果是否和OS具体实现有关?

    7:对于剑指链表的一个另类思路的尝试。

      1,起因:剑指中,尝试使用AddtoTail一个函数完成添加操作。它可以完成添加功能,但同时也完成了链表的创建。代码如下(实现代码省略):

    void AddToTail( ListNode** pHead, int value )
    {
        // contents....
        if ( *pHead == NULL )
        // contents...
    }

        1)使用指向指针的指针的原因是:当往一个空链表中插入头节点时,如果使用链表头作为指针,会导致我们丢失链表头。(具体解释,可以查看剑指offer——或者email本人)

        2)而我的问题是,如果pHead本身就是NULL,这个函数会怎么样?剑指中,对于空指针异常警惕,所以对于此,我存有疑惑。是否是C++语言体系中,已经能够保证NULL指针可以指向NULL?

        3)如果如上猜测,为何在removeNode函数中,又对phead本身进行了判断?

        4)模拟性质的对此函数进行调用,发现似乎需要在外面先创建一个ListNode**的指针。用new?或者还是可以直接NULL。感觉此函数非常模糊不清。如果使用new,感觉多此一举。

      2,对此函数,有两个想法。

    1 first mode
    ListNode* AddToTail( ListNode* pHead, int value )  // 如果传入指针为空,则直接返回一个头指针。使用返回值来获取头指针。
    2 second mode
    ListNode* CreateList( int value );
    void AddToTail( ListNode* pHead, int value );  // 相信都看的懂 :)
    // 个人倾向此方法。虽然使用一个函数解决看起来更简洁。但我更喜欢将行为拆解。

        1)个人无法认同剑指的链表函数,即使NULL真的指向NULL。即使使用该函数不会造成错误——但从逻辑上,很不美好:(

      3,另外,挺喜欢剑指面试4中的环形链表。

    8:碰见过的面试中,大部分公司都喜欢考递归。但,需要注意,现实中使用递归需要考虑栈溢出的问题。除非比较确定不会溢出,否则现实中还是少用递归吧。(当然,面试时还是使用递归比较好)

    9:面试题6的细节记录:

      1,使用前序和中序数组来构建二叉树。大致思想不再分析,但需要注意异常输入。例如:前序中本身存在错误数据。此时,使用你的函数,是否能够抛出一个错误?

      2,虽然再异常和边界中已经提及其重要性,但个人编程习惯很难一时改变。在面试时,需要可以关注此点。

      3,二叉树是否可以通过前序+后序还原二叉树?(个人已经尝试,无法解决。)

    10:注意算法(特别是排序)的额外空间消耗,平均/最差时间复杂度。

      1,基本的数据结构和常用排序算法是必须具备的知识。

    11:面试题8代码中,没有考虑数组出错的情况。

      1,对于排序数组的查找和搜索,需要注意重复数字的情况。

    12:使用位运算时,需要注意负整数的情况。(通常面试不会变态到求浮点数。)负整数使用补码形式保存数据。

    13:很多链表问题都可以使用两个指针解决。。。如果不行,大概可以用3个?(玩笑。。。)

    14:面试题28是一个相对复杂的递归方式——因为它的递归出口比较奇怪。

      1,此题思路不难,但递归代码卡了10分钟。因为在大脑中模拟递归走向时出现问题。

      2,使用和理解递归:建议将递归函数代码内的递归函数看成已经解决的结果,而不要尝试展开它。对于部分递归,模拟展开没有问题,但另一部分递归,并不适合展开。

      3,递归有两种模式:

        1)拥有比较简单的出口,并且结果可以带入上层递归函数中。把递归本身当作结果时,很容易联想到结果。对于此类递归,大脑模拟展开问题不大。

        2)如面试28,递归代码中的递归函数本身就代表多种可能性,并且递归出口并非0,1,2,3等等简单解决式。当我尝试大脑模拟打印时,发现可能性多种多样,无法例举。可能反向导致思路受阻。

      4,递归的两个重点:

        1)递归规则。

        2)寻找递归出口。需要注意的是,递归出口有时并非只有一个(虽然一个出口也可以完成递归,但可能会有隐性减分)。

      5,面试28的扩展题。(有时间将重开一章,说明遇见的问题和出现错误)

    15:吐槽面试题37.好好的归并排序变种成完全想不到的奇葩题。。。

    16:逻辑运算符: '!' 的返回值为TRUE OR FALSE。

    17:粗略看完剑指OFFER后的感想。

    对于剑指:

    阅读完后,第一想法,是将其中的所有面试题进行整理。得到其算法和数据结构的本质。因为面试题可以有各类变种,但需求的算法和数据结构都是不会改变的。阅读剑指,与其说是阅读,理解和记忆面试题,不如说,其总结了常用的面试算法和结构。

    18:以下无经过权威证实:

    用三天的时间看玩了剑指offer。不得不说,本书写的非常好(听说被翻译成英文出版了。不知真假)。

    但剑指中,实际上还有很多很多缺陷的地方。倒不是书的问题。而是知识点实在太多。剑指偏向于算法以及代码细节的处理(如异常等)。但对具体语言就有很大欠缺,需要其他面试题补充。

    面试不单是拿OFFER。一个合格的公司,它拿出的面试题本身应该有一定的倾向性。例如,我希望得到服务器相关的岗位。但对于此行业,我只知道非常粗浅的概念知识。解答面试的同时,同样需要关注面试题的中包含的知识倾向。

  • 相关阅读:
    Gantt/BTS 生产计划电子看板甘特图
    C# DotNetty TCP对接 松下扣料机
    Vue 和 Zebra 打印机连接直接打印条码
    JavaFx 通信ITC数字广播 SAPI 使用NeoSpeech Liang包生成语音
    Docker RabbitMQ 镜像集群
    游戏匹配实现
    Metro 界面设计案例
    JavaFX 集成 Sqlite 和 Hibernate 开发爬虫应用
    Discuz论坛 自动加好友留言程序
    JavaFX Metro UI 和 开发库
  • 原文地址:https://www.cnblogs.com/zheng39562/p/4281478.html
Copyright © 2020-2023  润新知