小弱石的求职季笔记(二)

比较知名的互联网公司就只投了网易和欢聚,其它的都完美错过......

于是从欢聚那失败归来后,赶紧印了数份简历,每天跑来跑去听宣讲会。

厦门这边正好就来了几家搞游戏的,于是就去看了...


三、厦门吉比特,游戏研发(一面挂,后来问要不要先去实习看看,如果能力可以会转正,没去)

刚看到这个公司的时候,感觉从来没见过...然后看了介绍,居然是以前做出《问道》的那家公司。

吉比特今年开出的待遇非常好,技术类月薪12k~18k,福利在厦门也是比较好的,笔试的时候整个大教室都坐满了,竞争挺激烈的。

笔试的话大部分都是c/c++的基础题,还有一些算法题,也参杂一了些Java和操作系统的问题,平时有做过题目的话基本上就OK。

然后顺利进入一面,面试官挺好的,比较有耐心,也不会专门往***钻的地方提问。

开始时先让自我介绍,然后大概问了一点基本情况学习成绩什么的,然后就开始问笔试时的一些题。


1. 第一个是最大子序列和问题:

a. 当时不记得动态规划怎么写了,就说了O(n^2)的解法:

int subMaxSum_1(int input[], int size)
{//对数组每一位,计算其向后累加和,累加过程中记录最大值,最终结果即为所求最大值
 //O(n^2)

    int subMax = 0;

    for(int i = 0; i < size; i++)
    {
        int sum = 0;
        for(int j = i; j < size; j++)
        {
            sum += input[j];
            if(sum > subMax)
            {
                subMax = sum;
            }
        }
    }

    return (subMax > 0) ? subMax : 0 ;
}


b. 回去后把动态规划(O(n))的方法撸了出来,加深印象:

int subMaxSum_2(int input[], int size)
{//利用动态规划方法
 //nAll[i] = max(input[i], nEnd[i], nAll[i-1])
 //nAll为0~i的最大的一段数组和,nEnd是包含input[i]在内的最大一段数组和
 //O(n)

    int subMax = 0;
    int nEnd = input[0];
    int nAll = input[0];

    for(int i = 1; i < size; i++)
    {
        nEnd = max(nEnd + input[i], input[i]);
        nAll = max(nEnd, nAll);
    }

    return nAll;
}

2. 第二个问题是最长相同子序列问题,当时好久没看算法,把这个给忘了,感觉要想出来可能还得思考一段时间,于是就说不太会,换个问题。回去后赶紧把这个问题撸出来,以防以后再问:

int subSequenceMax(char *str_1, char *str_2)
{ //动态规划方法,O(n^2)
    int len_1 = strlen(str_1);
    int len_2 = strlen(str_2);

    int **maxLen;
    maxLen = (int**) malloc(sizeof(int*) * (len_1 + 1)); 
    for(int i = 0; i < len_1; i++)
    { //申请空间,需要注意用for循环分别申请
        maxLen[i] = (int*) malloc(sizeof(int) * (len_2 + 1));
    }

    for(int i = 0; i <= len_1; i++)
    {
        for(int j = 0; j <= len_2; j++)
        {
            if(i == 0 || j == 0)
            { //对矩阵进行初始化
                maxLen[i][j] = 0;
                continue;
            }

            if(str_1[i - 1] == str_2[j - 1])
            { //如果检查到相同字符,则加1
                maxLen[i][j] = maxLen[i - 1][j - 1] + 1;
            }
            else
            { //如果是不同的字符,矩阵内对应的值为前一情况中的最大值
                maxLen[i][j] = max(maxLen[i - 1][j], maxLen[i][j - 1]);
            }
        }
    }

    for(int i = 0; i <= len_1; i++)
    {
        for(int j = 0; j <= len_2; j++)
        {
            cout << maxLen[i][j] << " " ;
        }
        cout << endl;
    }

    int result = maxLen[len_1][len_2];


    return result;
}
主要是推出来递推的表达式就搞定了~

3. 换了个问题,就开始让我手写算法了,求1~100的素数,这题看似容易,实际上还有些要稍微深入讨论的问题...

a. 首先想到的就是,遍历and对每个数检查到其开根号的位置:

void method_1(int n, int m)
{
    for(int i = n; i <= m;)
    {
        bool flag = true;

        for(int j = 2; j * j <= i; j++)
        { //这里原本写的是 j <= sqrt(i) + 1, 面试官说,实际上sqrt开销比较大,让我看看怎么更快处理这个地方
		  //当时就想着 i 开根号有什么更高效的方法,就没想出来,结果竟是 j * j 来替换,头脑没转过弯来啊hh,以后得记住喽
            if(i % j == 0)
            {
                flag = false;
                break;
            }
        }
        //if(flag) cout << i << " ";
        if(i % 2 == 0) i++;
        else i += 2;
    }

    cout << endl;
}
这里面试官还问,为什么检查到开根号位置就行了,当时本来是有思路说出来的,结果突然想到16,开根号是4,但是又有2 * 8 = 16,突然就短路了......

实际上,如果一个数x = m * n 如果m和n都大于sqrt(x) 则m * n大于x,故肯定有一个因子小于sqrt(x),于是只需要检查到sqrt(x)就够了。


b. 然后面试官说,对于每个非素数都有一个性质,就是它定能分解成素数的乘积,于是利用这个性质再写这个算法,当时由于直接再原来写的算法上补写,写得比较乱,不过基本上是写对的,实际代码应如下:

void method_2(int n, int m)
{//利用非素数可以分解为素数乘积的性质,对每个数先采用已知大于1的素数进行测试
 //若已知数已测完还未达到开方条件,则再依次加1进行判断
    int *result = new int[m]; //记录已找到的素数,用于之后的检查
    int count = 0;

    for(int i = 1; i <= m; i += 2)
    {
        bool flag = true;
        int j = 2;

        for(int z = 1; z < count; z++)
        {
            j = result[z];

            if(j * j > i)
            {
                break;
            }

            if(i % j == 0)
            {
                flag = false;
                j = i;
                break;
            }
        }

        for(; j * j <= i; j++)
        { 
            if(i % j == 0)
            {
                flag = false;
                break;
            }
        }

        if(flag) result[count++] = i;
    }

    for(int i = count - 1; i >= 0; i--)
    {
        if(result[i] < n)
        {
            break;
        }
        else
        {
            cout << result[i] << " ";
        }
    }

    cout << endl;

    delete result;
}

代码实际跑了一下,检查1~10w的素数,确实第二个方法快了很多倍。

最后面试官的评价是:基础不错,学习能力不错,但实际经验太少,需要加强

过了几天通知面试没过,又再过了几天打电话来问要不要去实习,当时很犹豫,因为没有offer......


四、厦门极致游戏,游戏研发(二面后,没过)

这个极致游戏的老板是当年搞出问道的人,前几年从吉比特出来自己搞了这个公司,目前发展得不错,虽然没有红爆半边天的游戏,但是今年也已经上市了......而且这公司关于加班的理念、制作游戏的理念也比较符合我个人理念,当时二面后就想着,如果能给offer就直接毫不犹豫签了,在这个地方肯定能好好做技术,以后发展机会大,而且开出的薪酬也不错,有6k~10k福利很多,在厦门这待遇已经是很好了。

燃鹅没拿到offer,这就尴尬啦...

笔试的话也是比较基础的题,不过比较坑的是,有两个大部分:逻辑题专业题

逻辑题就是那种智力题,从几个人说的话中获取信息,判断出每个人是什么身份之类的。花了挺多时间在这部分,后来发现不太好做,做了大概3题就赶紧去做专业题了。

专业题不难,也都是些基础的东西,总之算法和数据结构、c/c++基础基本上都是必考的。

一面技术面,面试官主要是问个人做过的项目上的问题,我在简历上写了个Java分布式爬虫的项目,于是就开始追根究底......只要项目自己是有认真做,基本上没太大问题就是。然后也问到了虚函数,这时候对虚函数有过复习,但是没有实际上机去试试,结果...

项目之外的问题主要就是虚函数相关问题,问题其实都挺简单,但当时没答上来,吸取教训,把虚函数相关问题复习复习:

1. 虚函数:

在基类中冠以virtual的成员函数,它提供一种接口界面,允许在派生类中对基类的虚函数重新定义。其主要作用是实现多态。

(多态:是对于不同对象,接收相同消息时,产生不同的动作。)

2. 虚析构函数有什么用处:

一般基类的虚构函数都会声明为虚函数,这是为了防止内存泄露问题的发生。如果不声明为虚函数,当使用基类指向派生类的指针调用析构函数时,只会执行基类的析构,如此子类无法正确析构,导致内存泄露。这里要注意,纯虚析构函数需要给出定义。

3. 纯虚函数:

在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在。纯虚函数一般不能直接调用。

例子: virtual void demo() = 0; 注意这里不用实现。


二面就是HR面了,感觉交谈得挺愉快的,有种这次能成的错觉......

最后总结,还是因为实际经验太少,有很多实战时的最基本的问题,比如虚析构函数作用这类问题都不了解。

得多撸代码才行啊!~


五、厦门飞鱼科技,游戏研发(一面未过)

飞鱼,就是开发了《保卫萝卜》的那个,待遇不错,不过没明说。不过据说加班比较多......

笔试题目基本上也没什么问题。

一面本来是技术面,然而进去后一个面试官直接就开玩手机,然后另一个让我自我介绍。

介绍完后,基本上就是根据简历上面写的项目问了问,也没怎么深入。

感觉就是看不上我,走个流程赶人吧,总之并没问什么技术问题......



目前比较需要记录的就是这几个了......接下来还得继续战斗,跑宣讲会,刷笔试......

给自己加油!奋斗


posted @ 2016-11-03 12:35  StoneDemo  阅读(135)  评论(0编辑  收藏  举报