面试题打卡——C++版

Posted ych9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题打卡——C++版相关的知识,希望对你有一定的参考价值。

格雷编码

格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。格雷编码序列必须以 0 开头。

题解:

1.相邻不同 -> 每次只改变一位

2.改变一位 -> 将本身往右移动一位进行异或

class Solution {
public:
    vector<int> grayCode(int n) {
    //n =3
    //000-> 0  001 -> 1 010->2 011->3 100->4 101->5 110->6 111-> 7 编码总长度为2^n
  
    vector<int> arr;

    for(int i=0;i<pow(2,n);i++)
    {
        arr.push_back(i^i>>1);
    }
        return arr;
    }
};

LRU缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

题解:

1.自己实现一个带头双向链表(STL自带的,无法对节点进行O(1)操作)

2.使用哈希表存储链表的节点

3.当查询或者插入一个元素的时候,就将对应的节点挪动到尾端

4.当需要删除元素的时候,双向链表的第一个节点,就是使用最少的

struct List_node
{
	List_node(int val = int(), int k = int())
	:_prev(nullptr)
	, _next(nullptr)
	, _value(val)
	, _key(k)
	{}

	struct List_node *_prev;
	struct List_node *_next;
	int _value;
	int _key;
};

class LRUCache {
public:

    void link(List_node *node)//将当前节点链接至尾部
    {
		//将node节点前后链接起来
		List_node *prev = node->_prev;
		List_node *next = node->_next;
		prev->_next = next;
		next->_prev = prev;

        //将node链接到尾巴上去
		List_node *tail = _Head->_prev;
		tail->_next = node;
		node->_prev = tail;
		node->_next = _Head;
		_Head->_prev = node;

    }


	LRUCache(int capacity) {
	//创建头结点
		_Head = new List_node;
		_Head->_next = _Head;
		_Head->_prev = _Head;
	//空间大小
		size = capacity;
	}

	int get(int key) {
		auto it = exits.find(key);

		if (it == exits.end())//不存在
			return -1;

		//存在则将其链到尾巴上去
		List_node *node = it->second;
        link(node);
		return node->_value;
	    }

	void put(int key, int value)
	{

		auto it = exits.find(key);
		if (it != exits.end())//只需要更改键值,不需要新插入
		{
		    List_node* node = it->second;
			node->_value = value;

			//将其链接至队尾
            link(node);
		}
		else//该节点不存在,需要创建新的节点,并且将其链接至队尾
		{
			List_node* newnode = new List_node(value, key);//创建新结点
			exits[key] = newnode;//将节点填入哈希表之中

            //获取链表的尾部节点
				List_node* tail = _Head->_prev;

            //将链表链接到尾部
				tail->_next = newnode;
				newnode->_prev = tail;
				_Head->_prev = newnode;
				newnode->_next = _Head;
		}

		if (exits.size()>size)//插入的是新的节点,需要将头节点删除
		{
            //获取第一个节点,即要删除的节点
			List_node* delete_node = _Head->_next;
            
            //将节点链接起来
			List_node* next = delete_node->_next;
			_Head->_next = next;
			next->_prev = _Head;

            //获取要删除的节点的key值(哈希表中的删除需要用来)
			int key = delete_node->_key;
			
            //删除
			delete delete_node;
			exits.erase(key);
		}
	}

			size_t size;
			unordered_map<int, List_node*> exits;
			List_node* _Head;
};

字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

题解:

1.看到在这种带"括号"的题就要想到使用栈来解决问题

2.定义两个栈,一个用来装字符串和 [,一个用来装整形

3.当遇到整形时,全部装入栈1之中,此时的整形是字符的,因此要使用while循环,将所有的整形串在一起,然后转成整形,然后将整形入栈1

4.当遇到 [ 直接入栈2

5.当遇到字符串,同样需要使用while循环,将所有的字符串连接起来,需要注意的是,如果栈1位空,即当前字符串前面没有数字,不需要翻倍,直接插入到返回字符串的后面

6.当遇到 ] 号时,表示可以将字符串构建多倍了,此时从栈1中取出一个整数,从栈2之中取出字符串,此时遇到栈中最近的 [ 之前的所有字符串都是和前面的数字进行配对的,因此全部连接起来,注意字符串的方向,先出来的在后面

7.当字符串翻倍之后,如果栈1之中还有数字,说明,当前字符串是中间的,将字符串入栈,否则直接插入到返回字符串之中

class Solution {
public:
    string decodeString(string s) {
        string ret;

        for(int i=0;i<s.size();i++)
        {
            if(s[i]>='0'&&s[i]<='9')//遇到数字
            {
                 string num_temp;
                while(i<s.size()&&s[i]>='0'&&s[i]<='9')//遇到数字
                {
                    num_temp+=s[i];
                    i++;
                }
                num_st.push(stoi(num_temp));
                i--;//i最后多加了一次
            }
            else if(s[i]=='[')//遇到左括号
            {
                ch_st.push("[");
            }
            else if(s[i]>='a'&&s[i]<='z')//遇到字符串
            {
                string temp;
                while(i<s.size()&&s[i]>='a'&&s[i]<='z')
                {
                    temp+=s[i];
                    i++;
                }
                i--;
                if(num_st.empty())
                    ret+=temp;
                else
                    ch_st.push(temp);
            }
            else//遇到的是]
            {
                //从数字栈中获取数字
                int num=num_st.top();
                num_st.pop();

                //从字符串栈中获取字符串
                string ss;
                while(ch_st.top()!="[")
                {
                    ss=ch_st.top()+ss;//变成正向的
                    ch_st.pop();
                }
                ch_st.pop();//'['出栈

                string str;
                for(int i=0;i<num;i++)
                {
                    str+=ss;
                }

                if(num_st.empty())
                    ret+=str;
                else
                    ch_st.push(str);
            }
        }
        if(!ch_st.empty())
            ret+=ch_st.top();

        return ret;
    }
    stack<int> num_st;//存储数字
    stack<string> ch_st;//存储字符串和符号
};

石子游戏

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

题解1:

石子堆数是偶数堆,并且判断先手拿的人是否能赢,那么先手的人一定能赢
因为,不可能出现和的情况,因此,下标全部为奇数和下标全部为偶数的石子和肯定不同,所以只要先手的人一直控制拿偶数堆或者一直控制拿奇数堆即可获得最终的胜利

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        return true;
    }
};

题解2:

1.设dp[i][j] 表示 从石堆 i - j 中拿取石子,先手多余后手的石头数量

2.当 i==j时,表示只有一个石子,则只有先手的那个人可以拿到,dp[i][j] = piles[i]

3.当 i<j 时,表示没有石头可拿,此时dp[i][j] = 0

4当 i< j时, 此时先手有两种拿法,那么拿前面的,要么拿后面的,自己拿完后,对方就变成了先手,即要保证自己拿到的是最大的

-> dp[i][j] = fmax(piles[i] -dp[i+1][j],piles[j]-dp[i][j-1])

class Solution {
public:
    bool stoneGame(vector<int>& piles) 
    {
        vector<vector<int>> dp(piles.size(),vector<int>(piles.size(),0));

        for(int i=0;i<piles.size();i++)//i==j时,表示最后一个石头,肯定是由对面拿的
        {
            dp[i][i]=piles[i];
        }

        for(int i=0;i<piles.size();i++)
        {
            for(int j=i+1;j<piles.size();j++)
            {
                dp[i][j]=fmax(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
            }
        }
        if(dp[0][piles.size()-1]>0)
            return true;
        
        return false;

    }
};

蘑菇阵

现在有两个好友A和B,住在一片长有蘑菇的由n*m个方格组成的草地,A在(1,1),B在(n,m)。现在A想要拜访B,由于她只想去B的家,所以每次她只会走(i,j+1)或(i+1,j)这样的路线,在草地上有k个蘑菇种在格子里(多个蘑菇可能在同一方格),问:A如果每一步随机选择的话(若她在边界上,则只有一种选择),那么她不碰到蘑菇走到B的家的概率是多少?

题解:

1.设dp[i][j]表示到达i、j位置,不碰到蘑菇的概率

2.当i==0&&j!=0时,表示处于第一行,则当前位置只能由前面一个位置而来,但是前面的位置可以选择往下走,或者往右走,这里是关键,平常的相似动态规划题是不考虑概率问题的,因此 dp[i][j] = dp[i][j-1]*0.5 ,但是这里还需要注意的是,如果只有一行,则一路通行到底了

3.当i!=0&&j==0时,处于第一列,此时只能由上面而来,同理,上面的位置可以选择往下走或者往右走,同样需要带上概率,也需要判断是否只有一列

4.当i==n-1&&j!=m-1时,处于最后一行,此时可以由左边和上边来,左边只可以往右边走,上边的右两种选择

5.当i!=n-1&&j==m-1时,表示处于最后一列,此时上面的只可以往下走,左边的右两种选择

6.当位于最后一个位置时,上面和左边的都只有一种选择了,因此不用乘上概率

#include <iostream>
using namespace std;
#include <vector>
#include <math.h>

int main()
{
	int n, m, k;
	int x, y;
	while (cin >> n)//[n,m]
	{
		cin >> m;
		cin >> k;
		vector<vector<double>> arr(n, vector<double>(m, 1));

		for (int i = 0; i<k; i++)
		{
			cin >> x >> y;
			arr[x - 1][y - 1] = 0;//表示每行的蘑菇
		}

		for (int i = 0; i<n; i++)
		{
			for (int j = 0; j<m; j++)
			{
				if (i == 0 && j == 0)
					continue;
				if (arr[i][j] == 0)//有蘑菇的路径概率为0,不用走了
					continue;
				else if (i == 0)
				{
					if (n>1)//不是只有一行
						arr[i][j] = arr[i][j - 1] * 0.5;
					else
						arr[i][j] = arr[i][j - 1];
				}
				else if (j == 0)
				{
					if (m>1)//不是只有一列
						arr[i][j] = arr[i - 1][j] * 0.5;
					else
						arr[i][j] = arr[i - 1][j];
				}
				else
					arr[i][j] =
					(j == m - 1 ? 1 : 0.5)*arr[i - 1][j] +//上面来
					(i == n - 1 ? 1 : 0.5)*arr[i][j - 1];//左边来

				//else if (i == n - 1 && j != m - 1)//在最后一行
				//	arr[i][j] = arr[i - 1][j] * 0.5 + arr[i][j - 1];
				//else if (i != n - 1 && j == m - 1)//在最后一列
				//	arr[i][j] = arr[i - 1][j] + arr[i][j - 1] * 0.5;
				//else if (i != n - 1 && j != m - 1)//在中间
				//	arr[i][j] = arr[i - 1][j] * 0.5 + arr[i][j - 1] * 0.5;
				//else//在最后那个位置
				//	arr[i][j] = arr[i - 1][j] + arr[i][j - 1];
			}
		}
		printf("%.2f\\n", arr[n - 1][m - 1]);
	}
	return 0;
}


以上是关于面试题打卡——C++版的主要内容,如果未能解决你的问题,请参考以下文章

面试题打卡——C++版

面试题打卡——C++版

面试题打卡——C++版

面试题打卡——C++版

面试题打卡——C++版

C++经典面试题打卡