笔试问题总结


不完整的笔试总结

趋势科技:

问题1

台阶,初始站在台阶0 ,每次可以走一个台阶或者两个台阶

规定坏台阶数量 并指明坏台阶所在的位置,求到达终点的方案个数。

典型的斐波那契数列的基础上修改

输入:

10 2

3 5

#include<bits/stdc++.h>
using namespace std;
int main(){
    int steps, badsteps;
    cin >> steps >> badsteps;
    vector<int> dp(steps+1, 0);
    for (int i = 0; i < badsteps; i++)
    {
        int index;
        cin >> index;
        //走到该台阶的方法为0
        dp[index] = -1;
    }
    dp[0] = 1;
    dp[1] = dp[1] == -1 ? 0 : 1;
    for (int i = 2; i <= steps;i++){
        if(dp[i]==-1){
            dp[i] = 0;
        }
        else{
            dp[i] = dp[i - 1] + dp[i - 2];
        }
    }
    cout<< dp[steps]<<endl;
    return 0;
}

问题2

扑克牌排序,包含花色。

牌大小 A 2 3 4 5 6 7 8 9 T J Q K

花色 d< c< h< s

输入任意张数的扑克牌,进行从小到大进行排序

#include<bits/stdc++.h>
using namespace std;

struct node{
    char color;
    int num;
    int c;
};

int check(char t){
    switch (t)
    {
    case 'd':
        return 1;
    case 'c':
        return 2;
    case 'h':
        return 3;
    case 's':
        return 4;
    }
}

int judge(char c){
    switch (c)
    {
    case 'A':
        return 1;
    case 'T':
        return 10;
    case 'J':
        return 11;
    case 'Q':
        return 12;
    case 'K':
        return 13;
    }
}

bool cmp(node n1,node n2){
    if(n1.num!=n2.num){
        return n1.num < n2.num;
    }
    return n1.c < n2.c;
}

int main()
{
    string str;
    vector<string> poker;
    do{
        cin >> str;
        poker.push_back(str);
    } while (getchar() != '\n');
    int k = 0;
    int n = poker.size();
    node nd[n];
    for (int i = 0; i < n; i++)
    {
        //题目中10 用T表示了,如果扑克牌10是用数字表示,则需要单独处理
        // if(poker[i][0] == '1'){
        //     nd[k].num = 10;
        //     nd[k].color = poker[i][2];
        //     nd[k].c = check(nd[k].color);
        // }
        if(poker[i][0] >='2' && poker[i][0]<='9'){
            nd[k].num = poker[i][0] - '0';
            nd[k].color = poker[i][1];
            nd[k].c = check(nd[k].color);
        }
        else{
            nd[k].num = judge(poker[i][0]);
            nd[k].color = poker[i][1];
            nd[k].c = check(nd[k].color);
            
        }
        k++;
    }
    sort(nd, nd + n, cmp);
    for (int i = 0; i < n;i++){
        if(nd[i].num>=10){
            switch (nd[i].num){
                case 10:
                    cout << 'T';
                    break;
                
                case 11:
                    cout << 'J';
                    break;
                
                case 12:
                    cout << 'Q';
                    break;
                
                case 13:
                    cout << 'K';
                    break;
                
            }
        }
        else{
            cout << nd[i].num;
        }
        cout << nd[i].color << " ";
    }
    return 0;
}

// 输入:8s Tc Jc Js Qs Kc As 4h
// 输出:1s 4h 8s Tc Jc Js Qs Kc

选择题: 二维数组的指针表示

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a[2][4] = {{1, 2, 3, 4}, {5,10, 7, 9}};
    cout << "输出" << a[1][1] << endl;
    cout << "输出" << *(a[1]+2) << endl; 
    cout << "输出" << *(*(a+1)+2) << endl; 
    cout << "输出" << *(a[1])+3 << endl; 
    cout << "输出" << *((a[1]+1)) << endl; 
    return 0;
}

//
输出10
输出7
输出7
输出8
输出10  

面试问题:

1.linux下获知系统版本信息的方式:

lsb_release -a

2.查找某一个文件的路径

find . -name "文件名"
find . name exam1.cpp
结果
./Linux/design/exam1.cpp
./Linux/string/exam1.cpp
./Linux/redbook/exam1.cpp
./Linux/tencent/exam1.cpp
./Linux/meituan/exam1.cpp

3.字符

char *p  = "hello"
char p[] =  "hello"
区别是什么?
如果是在C++环境下 char *p 不能直接指向常量字符串,会报错。
在C语言中可以。
这两条命令,指针p和字符串数组都是在栈中。"hello"是在常量区。p指针指向”hello“所在的地址 。
"hello"赋值给字符串数组p。
两者区别在于p指针不能改变常量字符串的值。而字符串数组可以修改其中的字符。
另外进行一部分测试,sizeof ,char[] 字符串常量的长度包含最后一个空字符。

#include<iostream>
#include<string>
using namespace std;
int main(){
    char a[] = "hello";
    cout << "size of a:" << sizeof(a)<<endl;
    char *p = a;
    cout << "p[2] = " << p[2] << endl;
    a[4] = 's';
    cout << "size of a:" <<sizeof(a) << ","
         << "sizeof(p):" << sizeof(p) << endl;
    cout << a << endl;
    cout <<"p[2] ="<< p[2] << endl;
    string str = "hello";
    cout << "size of str:" << sizeof(str) << ","
         << "length of str:" << str.length() << endl;
    return 0;
}

结果:
size of a:6
p[2] = l
size of a:6,sizeof(p):8
hells
p[2] =l
size of str:32,length of str:5

美团:

问题1

优美字符串,如果一个字符串改变顺序后存在不是回文字符串的字符串,则说明这个字符串是优美字符串。

例子:

aaaaa 不是优美字符串

abba  -> abab 是优美字符串

c
a 一个字符不是优美字符串

//其实如果字符串长度大于1 这个字符串还有两个及以上不同字符 则是优美字符串

思路:从小到达进行排序

双指针 i ,j

while(i<j){
	判断s[i]== s[j]
}
#include<bits/stdc++.h>
using namespace std;
bool cmp(char s1,char s2){
    return s1 < s2;
}
bool ans(string &s){
    sort(s.begin(), s.end(),cmp);
    int i = 0;
    int j = s.length()-1;
    while(i<j){
        if(s[i]==s[j]){
            i++;
            j--;
        }
        else{
            return true;
        }
    }
    //false 说明是回文 不是优美
    return false;
}
int main(){
    int n;
    cin >> n;
    vector<string> str(n,"");
    for (int i = 0; i < n; i++)
    {
        cin >> str[i];
    }

    for (int i = 0; i < str.size();i++)
    {
        if(str[i].length() == 1){
            cout << "NO" << endl;
        }
        else if(ans(str[i]) == true){
            cout << "YES" << endl;
        }
        else if(ans(str[i])==false){
            cout << "NO" << endl;
        }
    }
    return 0;
}

博乐科技:

题目1:

给定数组,以及一个int型的值K,寻找数组中两数之和小于K的最大值

思路:排序,双指针,指向index[0] 和index[n-1],while(i< j){}

找出符合条件的最大值

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 输入有两个参数L和K,其中L是一个整型1维数组,K是一个整型变量。0 < L.length < 10000;0 < L[i] < 1000;0 < K > 2000。
     * @param L int整型vector 整型一维数组
     * @param K int整型 整型变量
     * @return int整型
     */
    int TwoSum(vector<int>& L, int K) {
        // 排序
        sort(L.begin(),L.end());
        int i = 0;
        int j  = L.size()-1;
        int res = -1;
        while(i<j){
            int sum = L[i]+L[j];
            if(sum<K){
                i++;
                res = max(res,sum);
            }
            else {
                j--;
            }
        }
        return res;
        
    }
};

题目4:N皇后问题 :输出 所有可行方案

经典回溯问题解法 ,重点在于创建多个bool数组进行条件判断

以及字符串二维数组初始化

vector<string>的初始化方法
vector<vector<string>>(n,string(n,'.'));
#include<bits/stdc++.h>
using namespace std;
class Solution
{

private:
    vector<bool> col, dia1, dia2;
    vector<vector<string>> res;
    void putQueen(int n,int index,vector<int> &row){
        if(index == n){
            res.push_back(generateBoard(n, row));
            return;
        }

        for (int i = 0; i < n;++i){
            //确保各列 ,各对角线唯一
            if (!col[i] && !dia1[index + i] && !dia2[index - i + n - 1]){
                row.push_back(i);
                col[i] = true;
                dia1[index + i] = true;
                dia2[index - i + n - 1] = true;
                putQueen(n, index + 1, row);
                
                //回溯
                col[i] = false;
                dia1[index + i] = false;
                dia2[index - i + n - 1] = false;
                row.pop_back();
            }
            //如果不满足条件则跳过寻找下一个列位置
        }
        return;
    }
    

    vector<string>generateBoard(int n,vector<int> &row){
        assert(row.size() == n);
        
        //字符串数组初始化
        vector<string> board(n, string(n, '.'));
        for (int i = 0; i < n;++i){
            board[i][row[i]] = 'Q';

        }
        return board;
    }

public:
    vector<vector<string>> solveNQueens(int n) {
        
        res.clear();
        //布尔数组进行判断
        col = vector<bool>(n, false);
        dia1 = vector<bool>(2 * n - 1, false);
        dia2 = vector<bool>(2 * n - 1, false);

        //row数组确定每一行row 第几列放置皇后
        vector<int> row;
        
        putQueen(n, 0, row);
        return res;
    }
};

腾讯tencent:

问题1:

存在一个数字由4个以上公因数组成,要求每两个公因数的差不小于n。

输入一个数字n,求出可以构成的最小的数。

思路:公因数包含1和本身。

所以只要找出两个公因数,由于合数还可以再分解,会使得差小于n。

所以另外两个公因数应该为差大于等于n的质数。

根据筛选先求出质数表,自己规定数字范围。

#include<bits/stdc++.h>
using namespace std;
void primelist(vector<int> &ans,int num){
    vector<bool> isprime(num + 1, true);
    for (int i = 2; i <= num; i++)
    {
        if(isprime[i]){
            ans.push_back(i);
            //存在倍数关系的数字全部筛选掉
            for (int j = i * i; j <= num;j+=i){
                isprime[j] = false;
            }
        }
    }
    return;
}

int main(){
    int n;
    cin >> n;
    vector<int> ans;
    primelist(ans,10000);
    int res = 1;
    int k = 2;
    set<int> st;
    for (auto num : ans)
    {
        st.insert(num);
    }
    //st中为去重的素数
    int tmp = 1;
    while (k < 4 )
    {
        tmp += n;
        while(st.find(tmp) == st.end())
        {
            tmp++;
        }
        res *= tmp;
        k++;
    }
    cout << "res = " << res<<endl;
    return 0;
}


WPS:

字符串转字符atoi

简单模式:只对十进制数字有效

#include<iostream>
using namespace std;
int atoi(const char * str){
    int value = 0;
    int sign = 1;
    if(*str == '-'){
        sign = -1;
        str++;
    }
    while(*str >='0' && *str <='9'){
        value = value * 10 + *str - '0';
        str++;
    }
    return sign * value;
}
int main(){
    cout << atoi("123")<<endl;
    return 0;
}

//123

包含8进制和16进制的atoi函数

#include<iostream>
using namespace std;
int atoii(const char * str){
    int value = 0;
    int radix;
    int sign = 1;
    if(*str == '-'){
        sign = -1;
        str++;
    }
    //如果是16进制的情况
    if(*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X' )){
        radix = 16;
        str+=2;
    }
    else if(*str == '0'){
        radix = 8;
        str++;
    }
    else{
        radix = 10;
    }
    while(*str){
        //16进制的情况下 要要考虑10-15 
        if(radix  == 16){
            if(*str >='0' && *str <= '9'){
                value = value * radix + *str - '0';
            }
            else {
                value = value * radix + *str - 'a' + 10;
            }
        }
        //8进制和10进制
        else
            value = value * radix + *str - '0';
        str++;
    }
    return sign * value;
}


int main(){
    cout << atoii("123")<<endl;
    cout << atoii("0x12") << endl;
    cout << atoii("012") << endl;
    return 0;
}
/*
123
18
10
*/

其他

进制转换

给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。

了解知识:位运算

#include<iostream>
using namespace std;
int main(){
    int a = 1;
    a = a >> 1; // 右移1位
    cout << "a=" << a << endl;
    int b = 2;
    b = b << 1; //左移1位
    cout << "b=" << b << endl;
    int c = -2; //负数进行补码运算
    int d = 0xf;  //15 16进制 
    cout << "d = " << d << endl;
    c = c & d;   //14=  (-2+16) & 0xf
    cout << "c=" << c << endl;
    return 0;
}
class Solution {
public:
    string toHex(int num) {
        if(num == 0){
            return "0";
        }
        string res = "";
        for(int i = 7;i>=0;i--){
            int tmp = (num>>(4*i))&0xf;
            if(res.length()>0 || tmp!=0 ){
                char ans = tmp<10 ? tmp +'0' : tmp - 10 + 'a';
                res.push_back(ans); 
            }
        }
        return res;
    }
};

百度面试:

合并两个有序数组

#include<iostream>
#include<cstring>
using namespace std;
void merge(int A[],int m,int B[],int n){
    cout << "m = " << m << ",n = " << n << endl;
    int res[m + n];
    int i = 0;
    int j = 0;
    int k = 0;
    while(i<m || j<n){
        if(i== m){
            res[k++] = B[j++];
        }
        else if(j == n){
            res[k++] = A[i++];
        }
        else{
            res[k++] = A[i] <= B[j] ? A[i++] : B[j++];
        }
    }
    cout << sizeof(res)/sizeof(res[0]) << endl;
    // for (int i = 0; i < m + n; i++)
    // {
    //     A[i] = res[i];
    // }
    memcpy(A, res, sizeof(res));
    return;
}
int main(){
    int A[] = {1, 3, 4};
    int m = sizeof(A)/sizeof(A[0]);
    int B[] = {4, 5, 6};
    int n = sizeof(B)/sizeof(B[0]);
    merge(A, m, B, n);
    for (int i = 0; i < m + n; i++)
    {
        cout << A[i] << ",";
    }
    cout << endl;
    return 0;
}

结果

m = 3,n = 3
6
1,3,4,4,5,6,

左值引用和右值引用

左值引用,传入参数为引用,浅拷贝,效率高。

右值引用可以完美转发。调用移动构造函数,避免了拷贝,提高了程序效率。

TCP四次挥手

LRU算法简述。

常见的缓存算法:

LRU(Least recently used)最近最少使用,如果数据最近被访问过,那么将来被访问的几率也很高。

LFU(Least frequently used)最不经常使用,如果一个数据在最近一段时间内使用次数最少,那么在将来一段时间内使用的可能性很小。

FIFO(First in first out)先进先出,如果一个数据最先进入缓存中,则应该最早淘汰掉。

LRU缓存

浏览器的缓存策略、memcached的缓存策略都是使用LRU这个算法,LRU算法会将近期最不会访问的数据淘汰掉。LRU如此流行的原因是实现比较简单,而且对于实际问题也很实用,良好的运行时性能,命中率较高。下面谈谈如何实现LRU缓存:

  • 新数据插入到链表头部
  • 每当缓存命中(即缓存数据被访问),则将数据移到链表头部。
  • 当链表满的时候,将链表尾部的数据丢弃。

LRU Cache具备的操作:

  • set(key,value):如果key在hashmap中存在,则先重置对应的value值,然后获取对应的节点cur,将cur节点从链表删除,并移动到链表的头部;若果key在hashmap不存在,则新建一个节点,并将节点放到链表的头部。当Cache存满的时候,将链表最后一个节点删除即可。
  • get(key):如果key在hashmap中存在,则把对应的节点放到链表头部,并返回对应的value值;如果不存在,则返回-1。

LRU采用双向链表和MAP实现。这里采用双向链表的原因是:如果采用普通的单链表,则删除节点的时候需要从表头开始遍历查找,效率为O(n),采用双向链表可以直接改变节点的前驱的指针指向进行删除达到O(1)的效率使用Map来保存节点的key、value值便于能在O(logN)的时间查找元素,对应get操作。

#include<iostream>
#include<map>
using namespace std;

struct CacheNode{
    int key;
    int value;
    CacheNode *pre, *next;
    CacheNode(int k,int v):key(k),value(v),pre(NULL),next(NULL){}
};


class LRUCache{
private:
    int size;
    CacheNode *head, *tail;
    map<int, CacheNode *> mp;
public:
    LRUCache(int capacity){
        size = capacity;
        head = NULL;
        tail = NULL;
    }

    int get(int  key){
        map<int, CacheNode *>::iterator it = mp.find(key);
        if(it != mp.end()){
            CacheNode *node = it->second;
            remove(node);
            setHead(node);
            return node->value;
        }
        else{
            return -1;
        }
    }


    void set(int key,int value){
        map<int, CacheNode *>::iterator it = mp.find(key);
        if(it != mp.end()){
            CacheNode *node = it->second;
            remove(node);
            setHead(node);

        }

        else{
            CacheNode *newnode = new CacheNode(key, value);
            if(mp.size()>=size){
                map<int, CacheNode *>::iterator iter = mp.find(tail->key);
                remove(tail);
                mp.erase(iter);
            }
            setHead(newnode);
            mp[key] = newnode;
        }
    }


    void remove(CacheNode *node){
        if(node->pre != NULL){
            node -> pre->next = node->next;
        }
        else{
            head = node->next;
        }

        //更新删除结点的下一个结点的前驱结点
        if(node -> next !=NULL){
            node->next->pre = node->pre;
        }
        else{
            //如果删除的是最后一个结点,则尾结点指向删除结点 的前一个结点
            tail = node->pre;
        }
    }

    void setHead(CacheNode *node){
        node->next = head;
        node->pre = NULL;
        if(head != NULL){
            head->pre = node;
        }
        head = node;
        if(tail == NULL){
            tail = head;
        }
    }
};


int main(){
    //设置缓存大小
    LRUCache *lrucache = new LRUCache(2);
    lrucache->set(2, 1);
    lrucache->set(1, 1);
    cout << lrucache->get(2) << endl;
    lrucache->set(4, 1);
    cout << lrucache->get(1) << endl;
    cout << lrucache->get(2) << endl;
    return 0;
}

评论
  目录