文章目录
- 笔试题
- 知识点: 堆和栈的区别
- 知识点: 进程(Process)和线程(Thread)的区别
- 知识点: 二叉树的性质
- 知识点: 指针
- 知识点: 黑盒测试——等价类划分
- 知识点: 进程通信
- 知识点:十大经典排序操作
- 知识点:HTTP协议
- 编程题:给出一串阿拉伯数字,输出对应的中文读法
笔试题
- 对于堆和栈的描述,正确的是()
A.栈的大小会随着函数调用层次的变化而变化
B.从堆上申请内存,没有释放的话,会造成内存泄漏;栈的内存由编译器管理,不会内存泄漏
C.在windows上,堆的生长方向是向下的,随着内存地址减小的方向增长;栈的生长方向是向上的,即向着内存地址增加的方向增长
D.在C语言中,指针只能指向堆上的内存,不能指向栈上的内存
知识点: 堆和栈的区别
- 堆和栈都是临时存储数据的地方
堆:利用完全二叉树的结构来维护一组数据,然后进行相关操作,一般的操作进行一次的时间复杂度在O(1)~O(logn)之间。
栈:先进后出的数据结构 - 栈一般默认大小,地址空间连续;
- 栈由编译器需要的时候分配内存,不需要的时候自动清除,由系统管理。而堆是由程序员来释放的,容易产生内存泄漏;
- 堆向高地址增长,栈向低地址增长;
- 对于线程和进程的描述,正确的是()
A.进程退出前,需要手动销毁所有线程,否则进程退出后,依然会有线程在运行,可能导致不可预测的后果
B.在单核的CPU上使用多线程,并不会比单线程有优势
C.进程是调度和拥有资源的基本单位
D.使用全局变量、内存时,需要使用同步机制,因为它们在同一地址空间内
知识点: 进程(Process)和线程(Thread)的区别
- 进程的销毁方式:自行退出、手动结束;
线程的结束不一定需要手动销毁,有以下几种方式:
run所有代码逻辑执行完成,线程结束
线程在执行过程中抛出了一个异常或者error,线程结束
调用了该线程的stop、resume、suspend或者Runtime.runFinalizersOnExit这几个暴力方法,线程结束
使用interrupt方法终端线程 - 通常一个任务不光CPU要花时间,I/O也要花时间,一个进程等I/O时,CPU是闲置的,另一个进程可以利用CPU进行计算。单核多线程指的是单核CPU轮流执行多个线程,通过给每个线程分配CPU时间片来实现,因为时间片非常短用户感觉是多个线程同时执行。
- 进程是资源分配的基本单位(划分地盘);线程是处理器调度的基本单位(调用CPU等)。
- 某二叉树有2000个节点,则二叉树的最小高度为:
A.9 B.10 C.11 D.12
解:最小高度,则这个二叉树是一个满二叉树的时候高度最小。所以高度K = log2(n)+1 = log2(2000) +1 ≈ 11。 (其中log2(2000)要向下取整)
知识点: 二叉树的性质
- 二叉树中,第
i
层最多有 - 个结点;
- 深度为K的二叉树,最多有
- 个结点;
- 二叉树中,终端结点数(叶子节点数)为n0,度为2的节点数为n2,则n0 = n2+1
- 满二叉树、完全二叉树等的性质
- 在C++中,定义“int *p=new int(10)”,释放p指向的内存,语句是()
A.delete *p B.delete &p C.delete p D.delete []p
知识点: 指针
-
int *p=new int[10]
指的是定义一个指针p并给其分配10个int大小的空间,首地址为*p; -
int *p=new int(10)
分配一个int型变量所占大小的空间,并赋值10; - 一般用法是new一个数组的话用delete [],其他直接用delete即可。
- 某函数有且只有2个输入参数,参数A的取值可划分为5个等价类,参数B的取值可划分为3个等价类。至少应为函数设计()组测试数据
A.4 B.10 C.15 D.30
知识点: 黑盒测试——等价类划分
- 黑盒测试:通过各种输入观察软件的各种输出结果来发现软件的缺陷,而不关心具体时间如何实现的。
- 常用的黑盒测试方法:
- 下列哪些linux的跨进程通信方式不适合数据传输()
A.pipe,管道 B.shared memory,共享内存 C.socket,套接字 D.semaphore,信号量
知识点: 进程通信
- 进程通信的概念:进程用户空间是相互独立的,一般而言是不能相互访问的。但很多情况下进程间需要互相通信,来完成系统的某项功能。进程通过与内核及其它进程之间的互相通信来协调它们的行为。
- 进程通信的应用场景:
数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。 - Linux进程通信有6种方式:管道、信号量、信号、消息队列、共享内存和套接字。
- 管道
无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用;
有名管道也是一种半双工的通信方式,但是它允许无亲缘关系进程间的通信。 - 共享内存
共享内存使多个进程访问同一块内存空间。 - 套接字
套接字用于不同机器之间的进程间通信。
套接字的特性由3个属性确定,它们分别是:域、类型和协议。 - 信号量
信号量作为进程间及同进程不同线程间的同步手段。 - 信号
信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。可用于进程间通信和进程本身通信 - 消息队列
消息队列是消息的链表,允许一个或多个进程向它写入与读取消息。
- 使用以下哪种技术无法伪装自己的IP地址()
A.HTTP Proxy B.Socks Prox C.VPN D.修改DNS
修改DNS可以提高上网的速度,可以访问某些因为域名解析存在问题而不能访问的网站,可以屏蔽运营商的广告避免被钓鱼的危险,但并不能伪装IP地址。 - 有一项考试的成绩,目前是按照完成时间进行升序排列的,需要按照考试成绩进行排序,成绩高的排在前面,如果成绩相同,则完成时间短的排在前面。以上场景适合采用下列哪些排序算法?
A.插入排序 B.快速排序 C.堆排序 D.冒泡排序
【解析】本题实际考察哪种排序算法是稳定的,插入、冒泡是稳定的,快速和堆是不稳定的。
知识点:十大经典排序操作
- 下列关于HTTP协议,描述正确的是
A.HTTP协议工作在应用层
B.HTTP只能使用80端口,HTTPS只能使用443端口
C.HTTP是有状态协议
D.HTTP协议支持一定时间内的TCP连接保持,这个连接可以用于发送/接收多次请求
知识点:HTTP协议
- HTTP常用端口80/8080/3128/8081/9098,HTTPS默认端口TCP/443,UDP/443;
- HTTP是无状态协议。由于Web服务器不保存发送请求的Web浏览器进程的任何信息,因此HTTP协议属于无状态协议,可以通过Cookie和Session将状态分别保存在客户端和服务器端;
- HTTP是建立在TCP协议之上的,建立和释放连接的时间都很短,所以HTTP是一种短连接,为了解决每次连接释放效率低的问题,提出keep-alive保持连接特性,可以在一段时间内多次发送、接收。
编程题:给出一串阿拉伯数字,输出对应的中文读法
输入描述:
输入数字字符串
输出描述:
数字字符串所对应的中文读法,只考虑最高5位数的情况。
样例:
输入:18
输出:十八
- 分析:
对于零的读法问题:
- 12345:正常读;
- 01234:最前边的0省略;
- 10234:中间的0读出来,且省略位的读法;
- 12003:连续零时只读一个0;
注意:18读作十八,而不是一十八,因此对10-19的读法做特殊处理。
思路30min,解决2hour。。。
- 代码:
#include<iostream>
#include<string>
using namespace std;
void NumToRead(string str)
{
if(str == "")
return;
int len = str.size();
string str1[] = {"", "十", "百", "千", "万"};
string str2[10] = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
int i=0, j=len-1;
int *num;
num = (int*)malloc(sizeof(int)*len);
for(i,j;i<len;i++,j--)
{
num[j] = str[i] - '0';
}
if(len==2 && num[1]==1)
{
cout<<"十";
cout<<str2[num[0]];
return;
}
int x = len-1;
while(x>0)
{
if(num[x] == 0)
{
x--;
if(x == 0)
cout<<"零";
}
else
break;
}
int count0=0;
while(x>=0)
{
if(num[x] != 0)
{
cout<<str2[num[x]];
if(x != 0)//当str1[0]=""时,这步其实可以忽略判断
cout<<str1[x];
x--;
}
if(num[x] == 0 && x>=0)
{
count0++;
x--;
}
if(x<0)//x-count0 == 0改成x<0
break;
else if(num[x]==0)//
continue;
else if(count0>0)
{
cout<<str2[0];
count0=0;
}
if(x>0)
{
cout<<str2[num[x]];
if(x != 0)
cout<<str1[x];
x--;
}
}
free(num);
}
int main()
{
//测试用例
cout<<"Test 1:"<<endl;
string s = "12345";
NumToRead(s);
cout<<"\nTest 1 pass"<<endl;
cout<<"\n";
cout<<"Test 2:"<<endl;
string s1 = "01234";
NumToRead(s1);
cout<<"\nTest 2 pass"<<endl;
cout<<"\n";
cout<<"Test 3:"<<endl;
string s2 = "12340";
NumToRead(s2);
cout<<"\nTest 3 pass"<<endl;
cout<<"\n";
cout<<"Test 4:"<<endl;
string s3 = "10234";
NumToRead(s3);
cout<<"\nTest 4 pass"<<endl;
cout<<"\n";
cout<<"Test 5:"<<endl;
string s4 = "12003";
NumToRead(s4);
cout<<"\nTest 5 pass"<<endl;
cout<<"\n";
return 0;
}