面试题打卡——C++版

二进制手表

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。小时不会以零开头:例如,“01:00” 是无效的时间,正确的写法应该是 “1:00” 。分钟必须由两位数组成,可能会以零开头:例如,“10:2” 是无效的时间,正确的写法应该是 “10:02”

题解:

1.给定亮起的灯的数量,求所有的时间,排列组合问题 -> 回溯法

2.分析时钟:分别有 1 2 4 8 四种灯,因此循环的开始是1,并且每次增加的是自身

3.分析分钟: 分别有 1 2 4 8 16 32 六种灯,循环的开始是1,并且每次增加自身

4.时钟的递归构建:
a.递归终止条件:灯的数量超过了给定的数量
b.时间超过了规定的时间(11点)

5.分钟的递归构建:
a.递归终止条件:灯的数量超过了给定的数量(给定的灯 - 时钟用去的灯 )
b.时间超过了规定的时间(59分钟)

class Solution {
public:
    void Watchminute(vector<string>& ret,int hours,int minute,int count,int num,int sub)
    {
        if(minute>59||count>num)//时间超了
            return;
        if(count==num)//灯数量到了
        {
            //将对应的时间保存起来
            string temp;
            temp+=to_string(hours);
            temp+=":";
            if(minute<10)
            {
                temp+=to_string(0);
            }
            temp+=to_string(minute);
            ret.push_back(temp);
            return;
        }
        for(int i=sub;i<=32;i+=i)
        {
            minute+=i;//时间增加
            count++;//灯数量增加
            Watchminute(ret,hours,minute,count,num,sub+=sub);//sub+=sub相同的灯只可出现一次
            minute-=i;
            count--;
        }
    }
    void Watchhours(vector<string>&ret,int hours,int count,int num,int sub)
    {
        if(count>num||hours>11||num==0)//灯的数量超了或者小时数超了
            return;
        
        Watchminute(ret,hours,0,0,num-count,1);

        for(int i=sub;i<=8;i+=i)
        {
            count++;//灯数量计数器
            hours+=i;
            Watchhours(ret,hours,count,num,sub+=sub);
            hours-=i;
            count--;
        }
    }
    vector<string> readBinaryWatch(int turnedOn) {
        //整数,表示亮着的LED数量,所有可能的时间
        //一个灯表示的小时: 1 2 4 8 
        //一个灯表示的分钟:1 2 4 8 16 32  -> 循环从1开始,每次翻倍自身

        int num=0;
        vector<string> ret;
        if(turnedOn==0)
        {
            string temp("0:00");
            ret.push_back(temp);
            return ret;
        }
        Watchhours(ret,0,0,turnedOn,1);
        return ret;
    }
};

打开转盘锁

题解1:

采用bfs的搜索办法,每一个位置有两种变化,即加1或者减1,如果变化之后的字符串没有访问过或者没有在死亡数组里面,则将其添加至下一层的队列

当队列为空时,说明达到不了目的地

如果出队的元素等于目标字符串,那么返回当前元素所在层数即可

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        int count=0;
        queue<string> qe;
        string str="0000";
        unordered_set<string> st;//死亡数组
        unordered_set<string> st2;//记录这个数字是否出现过

        for(auto&e:deadends)
        {
            st.insert(e);
        } 

        if(st.find(str)!=st.end())//第一个就是死亡数字
            return -1;
        if(target==str)//第一个就是密码
            return count;
        
        qe.push(str);
        st2.insert(str);
        int size=1;//记录每一层的数字的个数
        while(!qe.empty())
        {
            //从队列之中取出数字
            str=qe.front();
            qe.pop();
            size--;
            if(str==target)
                return count;
            
            //将数字添加到队列之中
            for(int i=0;i<str.size();i++)
            {
                //当前数字+1
                if(str[i]=='9')
                    str[i]='0';
                else
                    str[i]=str[i]+1;
                if(st.find(str)==st.end()&&st2.find(str)==st2.end())//不是锁定数字
                {
                    qe.push(str);
                    st2.insert(str);
                }
                    //恢复原来模样
                    if(str[i]=='0')
                        str[i]='9';
                    else
                        str[i]=str[i]-1;

                //当前数字-1
                if(str[i]=='0')
                    str[i]='9';
                else
                    str[i]=str[i]-1;
                if(st.find(str)==st.end()&&st2.find(str)==st2.end())//不是锁定数字
                {
                    qe.push(str);
                    st2.insert(str);
                }
                    if(str[i]=='9')
                        str[i]='0';
                    else
                        str[i]=str[i]+1;
            }

            if(size==0)//当前层的数字全部用完了
            {
                count++;
                size=qe.size();
            }
        }
        return -1;
    }
};

题解2:

方法1中,假设没有死亡数组,每一个位置有两种变化,即每一个数字对应八种变化,对应的增长是很恐怖的,因此可以用双向bfs的方法进行优化

给两个队列,两个哈希表,一个队列从“0000”出发 一个队列从target出发,如果两个队列相遇了,说明找到了最短的路径,此时的路径长度为 count1+count2

搜索的顺序,可以是谁的队列元素少,谁就去进行搜索,这样才能保证平衡

class Solution {
public:
	void bfs(unordered_set<string> &dead, queue<string> &qe, unordered_map<string, int> &mp1,
		unordered_map<string, int> &mp2, int &count, int &ret)
	{
		int size = qe.size();//获取当前层的队列的元素个数
		while (size)
		{
			//取出元素
			string str = qe.front();
			qe.pop();
			size--;

			//将下一层的元素添加至队列之中
			for (int i = 0; i<4; i++)
			{
				//拷贝
				string add(str);
				string sub(str);

				//数字加1
				if (add[i] == '9')
					add[i] = '0';
				else
					add[i] = add[i] + 1;

				//数字减1
				if (sub[i] == '0')
					sub[i] = '9';
				else
					sub[i] = sub[i] - 1;


				if (mp2.find(add) != mp2.end() )//对端已经访问过了
				{
					ret = count + 1 +mp2[add];
					return;
				}
				if (mp2.find(sub) != mp2.end())
				{
					ret = count + 1+ mp2[sub];
					return;
				}


				//不在死亡列表之中,且没有出现过
				if (dead.find(add) == dead.end() && mp1.find(add) == mp1.end())
				{
					qe.push(add);
					mp1[add] = count + 1;//表示这个字符串,出现在第几层
				}
				if (dead.find(sub) == dead.end() && mp1.find(sub) == mp1.end())
				{
					qe.push(sub);
					mp1[sub] = count + 1;//表示这个字符串,出现在第几层
				}
			}
		}
		count++;//下一次访问的层数
	}

	int openLock(vector<string>& deadends, string target) {
		queue<string> qe1;
		queue<string> qe2;
		string str = "0000";

		unordered_set<string> dead;//记录死亡数字

		unordered_map<string, int> mp1;//记录搜索到的节点,并且记录当前节点是第几层

		unordered_map<string, int> mp2;

		for (auto&e : deadends)
		{
			dead.insert(e);
		}

		if (dead.find(str) != dead.end())//第一个就是死亡数字
			return -1;
		if (target == str)//第一个就是密码
			return 0;

		//一个从起点搜索,一个从终点开始搜索
		qe1.push(str);
		qe2.push(target);
		int count1 = 0;
		int count2 = 0;

		mp1[str] = count1;
		mp2[target] = count2;

		while (!qe1.empty() && !qe2.empty())//两个都不为空,有一个为空说明过不去
		{
			int ret = -1;

			if (qe1.size() <= qe2.size())
				bfs(dead, qe1, mp1, mp2, count1, ret);
			else
				bfs(dead, qe2, mp2, mp1, count2, ret);

			if (ret != -1)
				return ret;
		}
		return -1;
	}
};

滑动谜题

在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.一次移动定义为选择 0 与一个相邻的数字(上下左右)进行交换.最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。给出一个谜板的初始状态,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。

题解:

1.和上一题的本质是一样的,都是用双向bfs来进行解题

2.首先将二维数组转换成字符串,比较好操作

3.在一次bfs之中,首先找到0的位置,再用0的位置与上下左右进行交换

4.当与左边交换的时候,左边坐标 sub>=0 && sub!=2 ,等于2时,是从下一层跳到了上一层

5.当与右边交换的时候,右边左边 sub<str.size() && sub !=3,等于3时,是从上一层跳到了下一层

6.当sub-3>=0 || sub +3 <str.size() 表示可以与上面或者下面进行交换

class Solution {
public:

    void bfs(queue<string>&qe,unordered_map<string,int>&mp1,unordered_map<string,int>&mp2,int &count,int &ret)
    {
        int size=qe.size();

        while(size)
        {
            //去除字符串
            string str=qe.front();
            qe.pop();
            size--;

            //添加下一层的字符串
            int sub=0;
            for(int i=0;i<str.size();i++)//找到0的位置
            {
                if(str[i]=='0')
                {
                    sub=i;
                    break;
                }
            }

            if(sub-1>=0&&sub-1!=2)//和左边进行交换
            {
                string left=str;
                left[sub-1]=str[sub];
                left[sub]=str[sub-1];
                
                if(mp2.find(left)!=mp2.end())//在另外一边出现过了
                {
                    ret=count+1+mp2[left];
                    return;
                }
                if(mp1.find(left)==mp1.end())//没有出现过
                {
                    mp1[left]=count+1;
                    qe.push(left);
                }
            }
            if(sub+1<str.size()&&sub+1!=3)//和右边进行交换
            {
               string right=str;
                right[sub+1]=str[sub];
                right[sub]=str[sub+1];
                
                if(mp2.find(right)!=mp2.end())//在另外一边出现过了
                {
                    ret=count+1+mp2[right];
                    return;
                }
                if(mp1.find(right)==mp1.end())//没有出现过
                {
                    mp1[right]=count+1;
                    qe.push(right);
                }
            }
            if(sub-3>=0)//和上面进行交换
            {
               string up=str;
                up[sub-3]=str[sub];
                up[sub]=str[sub-3];
                
                if(mp2.find(up)!=mp2.end())//在另外一边出现过了
                {
                    ret=count+1+mp2[up];
                    return;
                }
                if(mp1.find(up)==mp1.end())//没有出现过
                {
                    mp1[up]=count+1;
                    qe.push(up);
                }
            }
            if(sub+3<str.size())//和下面进行交换
            {
               string down=str;
                down[sub+3]=str[sub];
                down[sub]=str[sub+3];
                
                if(mp2.find(down)!=mp2.end())//在另外一边出现过了
                {
                    ret=count+1+mp2[down];
                    return;
                }
                if(mp1.find(down)==mp1.end())//没有出现过
                {
                    mp1[down]=count+1;
                    qe.push(down);
                }
            }
        }
        count++;
    } 
    int slidingPuzzle(vector<vector<int>>& board) {
        //首先转换成字符串(哈希表无法直接处理二维数组)
        string end_str="123450";
        string str;
        for(int i=0;i<2;i++)
        {
            for(int j=0;j<3;j++)
            {
                str+=to_string(board[i][j]);
            }
        }

        if(str==end_str)//相等了,不用进行变化
            return 0;
        
        queue<string> qe1;
        queue<string> qe2;
        unordered_map<string,int>mp1;
        unordered_map<string,int>mp2;
        int count1=0;
        int count2=0;

        qe1.push(str);
        qe2.push(end_str);
        mp1[str]=0;
        mp2[end_str]=0;
        int ret=-1;
        while(!qe1.empty()&&!qe2.empty())//两个都不为空
        {
            if(qe1.size()<qe2.size())
                bfs(qe1,mp1,mp2,count1,ret);
            else
                bfs(qe2,mp2,mp1,count2,ret);
            
            if(ret!=-1)
                return ret;
        }
        return -1;
    }
};

堆盘子

堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构SetOfStacks,模拟这种行为。SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。 进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。

题解:

1.用一个数组来保存栈

2.需要操作的时候,就是操作最后一个栈,下标即为数组的大小-1

3.当栈为空的时候,在数组中将这个栈去除掉

4.插入的时候,如果栈满了,则用rsize在后面增加一个

5.注意cap==0时,此时不能进行任何操作

class StackOfPlates {
public:
    StackOfPlates(int cap) 
        :_cap(cap)
    {}
    
    void push(int val) {//入栈
    if(_cap==0)
        return ;
        int sub=st.size()-1;
        if(sub<0||st[sub].size()==_cap)//没有栈,或者栈满了
        {
            st.resize(st.size()+1);
            sub++;
        }
        st[sub].push(val);
    }
    
    int pop() {
        if(_cap==0)
            return -1;
            
        int sub=st.size()-1;

        if(sub<0)//没有栈
            return -1;
        
        int num=st[sub].top();
        st[sub].pop();

        if(st[sub].empty())//为空
            st.erase(st.begin()+sub);
  
        return num;
    }
    
    int popAt(int index) {//指定栈进行pop操作,index是栈的下标
        if(_cap==0)
            return -1;

        if(index>=st.size())//不存在这个栈
            return -1;
        
        int num=st[index].top();

        st[index].pop();

        if(st[index].empty())//为空删除
            st.erase(st.begin()+index);

        return num;
    }
    vector<stack<int>> st;
    int _cap;
};

直线上最多的点数

题解:

1.双重for循环,以每一个节点为基点,计算在当前节点所在直线的节点的个数

2.用map保存每次的斜率

3.由于精度的问题,因此斜率用字符串表示,字符串之中,保存的是约定的斜率

class Solution {
public:

	int maxchild(int y, int x)//求最大公约数
	{
		if (x == 0)//分子为0
			return 1;
		int c = y%x;
		while (c)
		{
			y = x;
			x = c;
			c = y%x;
		}
		return x;
	}
	int maxPoints(vector<vector<int>>& points) {
		int max = 0;
		for (int i = 0; i<points.size(); i++)
		{
			unordered_map<string, int> mp;//用string来保存斜率
			int x1 = points[i][0];
			int y1 = points[i][1];
			for (int j = i + 1; j<points.size(); j++)
			{
				int x2 = points[j][0];
				int y2 = points[j][1];

				int y = y2 - y1;
				int x = x2 - x1;
				int flag = 1;
				if (x*y<0)//算符号
					flag = -1;

				y = abs(y);
				x = abs(x);
				int num = maxchild(y, x);//得到最大公约数
				//约分处理
				y /= num;
				x /= num;

				//得到斜率
				string k;
				if (flag == -1)
					k += '-';

				k += to_string(y);
				k += to_string(x);

				if (x == 0)//此时为竖线
					mp[to_string(abs(x2))]++;
				else
					mp[k]++;//该斜率上增加一个点
			}
			for (auto&e : mp)
			{
				max = fmax(e.second, max);
			}
		}
		return max+1;
	}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是对大数据面试题——spark面试题(一)的回答: 1. 什么是Spark?它与Hadoop有什么区别? Spark是一个快速、通用、可扩展的大数据处理引擎,它可以在内存中进行数据处理,因此比Hadoop更快。与Hadoop相比,Spark的优点在于它可以在内存中进行数据处理,因此速度更快,而且它支持更多的数据处理方式,例如流处理、图形处理等。 2. Spark的核心组件有哪些? Spark的核心组件包括Spark Core、Spark SQL、Spark Streaming、MLlib和GraphX。 3. 什么是RDD?它有哪些特点? RDD是Spark中的一个基本概念,它代表一个不可变的分布式数据集合。RDD具有以下特点: - 可以在内存中进行计算,因此速度快; - 支持多种操作,例如map、reduce、filter等; - 可以进行持久化,以便在后续计算中重复使用。 4. Spark中的map和flatMap有什么区别? map和flatMap都是RDD中的转换操作,它们的区别在于: - map操作对每个元素进行转换,返回一个新的元素; - flatMap操作对每个元素进行转换,返回一个包含多个元素的序列。 5. 什么是Spark的shuffle操作? Spark的shuffle操作是指将数据重新分区的操作,它通常发生在reduce操作之前。Shuffle操作会将数据从多个节点上收集到一个节点上,然后重新分区,以便进行后续的计算。 6. Spark中的cache和persist有什么区别? cache和persist都是将RDD持久化到内存中,以便在后续计算中重复使用。它们的区别在于: - cache操作默认将数据持久化到内存中,而persist操作可以指定将数据持久化到内存、磁盘或者其他存储介质中; - cache操作是persist操作的一种简化形式,它默认将数据持久化到内存中,并且只能持久化到内存中。 7. Spark中的reduceByKey和groupByKey有什么区别? reduceByKey和groupByKey都是对键值对RDD进行操作的函数,它们的区别在于: - reduceByKey操作会在每个分区内先进行本地聚合,然后再进行全局聚合,因此效率更高; - groupByKey操作会将所有的键值对都进行网络传输,然后在一个节点上进行聚合,因此效率较低。 8. Spark中的broadcast变量有什么作用? broadcast变量是一种只读的变量,它可以在所有节点上共享,以便在计算过程中使用。使用broadcast变量可以避免在网络上传输大量的数据,从而提高计算效率。 9. 什么是Spark的checkpoint操作? Spark的checkpoint操作是将RDD持久化到磁盘上,以便在后续计算中重复使用。与cache和persist不同的是,checkpoint操作会将数据持久化到磁盘上,以便在内存不足时可以从磁盘上恢复数据。 10. Spark中的Task是什么? Task是Spark中的一个基本概念,它代表一个可以在一个节点上执行的计算任务。Spark将一个RDD分成多个分区,每个分区对应一个Task,这些Task可以并行执行,以提高计算效率。 ### 回答2: 今天我们来讨论一下关于Spark大数据面试的一些常见问题。Spark是一种基于Hadoop的开源计算系统,它能够快速处理大规模数据,并且支持多种编程语言,包括Java、Scala和Python等。以下是一些Spark面试题及其答案: 1. Spark有哪几种部署模式? Spark有三种部署模式,分别是本地模式、集群模式和分布式模式。本地模式指的是在本地运行Spark应用程序,不需要连接到外部计算机。集群模式指的是单个Spark集群环境,它由一组Spark节点组成,可以在数据中心或云中运行。分布式模式指的是使用多个Spark集群并行处理大规模数据。 2. Spark和Hadoop的区别是什么? Spark和Hadoop都是处理大规模数据的工具,但它们有一些区别。首先,Spark处理数据速度快,因为它将数据存储在内存中,而Hadoop则将数据存储在磁盘中。其次,Spark支持更多的编程语言,包括Java、Scala和Python等,而Hadoop只支持Java。此外,Spark具有更好的机器学习和图形处理功能,可以更好地支持大规模数据分析。 3. Spark的RDD是什么? RDD是Spark中重要的概念,全称为Resilient Distributed Dataset。它是一个不可变的分布式数据集合,可以分区存储在不同节点上,并且每个分区都可以在并行处理中进行处理。RDD支持两种操作,即转化操作和行动操作。转化操作将一个RDD转换为另一个RDD,而行动操作返回一个结果或将结果输出至外部系统。 4. Spark的优化技术有哪些? Spark优化技术包括数据本地化、共享变量、宽依赖和窄依赖、缓存和持久化,以及数据分区等技术。数据本地化将数据存储在尽可能接近计算节点的位置,以减少网络传输的开销。共享变量将常用的变量通过广播或累加器的方式在节点中共享,从而减少网络传输量。宽依赖和窄依赖指的是在转化操作中RDD之间的依赖关系,窄依赖表示每个父分区最多与一个子分区有关联,而宽依赖则表示多个子分区可能与多个父分区关联。缓存和持久化技术可将RDD保存在内存中,从而加速访问速度。数据分区可以将数据划分为较小的块进行并行处理。 5. Spark Streaming是什么? Spark Streaming是Spark的一个扩展模块,它支持实时数据流处理。Spark Streaming可以将实时数据流以微批次方式处理,每个批次的数据处理平均耗时只有几秒钟。Spark Streaming可以将数据存储在内存或磁盘中,同时支持多种数据源和数据输出方式。 以上是关于Spark大数据面试题的一些回答,希望能够对大家有所帮助。如果你想深入学习Spark和大数据处理技术,可以考虑参加相关的培训课程或在线课程。 ### 回答3: Spark是一个分布式计算框架,它可以使大规模数据处理更加高效和便捷。因此,在企业招聘大数据领域的人才时,对Spark的技术能力要求越来越高。以下是Spark面试题的回答: 1. Spark有哪些组件? Spark框架由三个核心组件组成:Spark Core、Spark SQL和Spark Streaming。此外,还有Spark MLlib、Spark GraphX、Spark R等个别不同的子组件。 2. 什么是RDD?与Dataframe有什么区别? RDD(弹性分布式数据集)是Spark的核心数据抽象,是不可变的分布式对象集合。RDD可以从文件中读取数据、从内存中读取数据、并行修改数据等。而Dataframe和RDD类似,但是Dataframe更加强大,因为它是带有结构化的RDD。Dataframe在处理大规模结构化数据时非常有效和便捷。 3. Spark如何处理缺失数据? Spark提供了两种处理缺失数据的方法:第一种是使用DataFrame API中的na函数,可以删除或替换缺失值;第二种是使用MLlib中的Imputer类,可以将缺失值替换为均值或中位数。 4. 什么是Spark的任务(task)? 一个任务是Spark作业中的最小执行单位。Spark集群上的作业被划分为多个任务,这些任务可以并行执行。 5. Spark的shuffle操作是什么?它为什么是昂贵的? Spark的shuffle操作是将一组数据重新分配到不同计算节点上的操作。Shuffle操作可能会导致大量数据的磁盘写入、网络传输和数据重组,这些都是非常昂贵的操作。因此,它在Spark集群中是一个相当昂贵的操作。 6. Spark中的Partition有什么作用? Partition是Spark中的数据划分单位。它可以将数据分成多个块并对每个块进行处理。Partition 可以提高 Spark 的并行度和运行效率,因为它可以将大规模数据分成多个小块,并在集群的多个计算节点上并行处理数据。 总而言之,Spark是大数据领域中使用最广泛的计算引擎之一,其技术理念和应用场景非常广泛。对于求职者而言,掌握 Spark 的基本概念和技术特点,提高对 Spark 的理解和应用能力,将有助于更好地处理和分析大规模数据集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值