笔试题收获

循环队列:
int queueSize = 100;
int front  
int rear
int data[queue];
int count;
 
front = rear;
且count = 0; 空
 
count = 100; 满
一开始front和rear都为0,入列rear+1后模100,出列front+1后模100

带权路径长
   /\
    /\  /\
  /\ 5 6  8
 2  3
2、3长度为3, 3 * 2 + 3 * 3
5、6、8长度为2

关系数据库可以允许重复,在没有声明主键情况下
数据库为每个主键默认建立索引,但用户可以通过CREATE INDEX另建其他索引,所以索引个数不唯一,非主键亦可有索引

单循环链表{data, link}
current当前指针
first指针头
到达列表尾部 current->link = first

二叉排序(查找)树
左边所有节点比根小,右边比根大,构造时就是从根节点开始判断下去,要放哪里,第一个进入为根

linux执行某脚本需要执行权限,ls看某文件夹有哪些文件,需要执行和读文件,进入某文件夹为执行权限

物理地址 = 基地址 + 偏移基地址 = 物理块号 * 页大小
偏移 = (逻辑地址%(页号*页大小))

位置

0

1

2

3

4

5

6

记录

63

48

 

38

25

74

52

查找次数

1

3

 

1

1

2

4

等概率平均查找长度为(1+3+1+1+2+4)/6

依赖:用到了别人的方法或变量;关联:对称的,好比你是我的朋友,我也是你的朋友;聚合:非对称的,员工与公司就是聚合关系,还有一个重要特点就是生命周期可以不同,员工离开了公司还是可以活的;组合:生命周期一致,好比人与心脏,一个没了另一个也没了。强度:依赖<关联<聚合<组合。

指针的sizeof永远是4字节(32位系统)或8字节(64位系统)


 


给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。  

2013-04-11 13:23:18|  分类: 算法 |  标签:随机数  |字号 订阅

 
 
  1. #include <iostream>   
  2. #include <stdio.h>   
  3. using namespace std;  
  4.   
  5. int rand5()  
  6. {  
  7.     return (rand()%5+1);  
  8. }  
  9.   
  10. void main()  
  11. {  
  12.     int a;  
  13.     while((a=rand5()*5+rand5())>26);  
  14.     cout<< (a-3)/3<<endl;  
  15. }  

 

代码解释:

1. 通过 rand5()*5+rand5() 产生 6 7 8 9 10 11 …… 26,27 28 29 30 这25个数,每个数的出现机率相等

2. 只需要前面 3*7 个数,所以舍弃后面的4个数

3. 将 6 7 8 转化为 1,9 10 11 转化为 2,……,24 25 26 转化为 7。公式是 (a-3)/3

每连续三个数产生一个随机数

稳定性好
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法
冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法
初始数据集的排列顺序对算法性能无影响:堆排序
排序问题:各种排序算法的时间复杂度 <wbr>比较

int a1=x+y-z; int a2=x-z+y; A、a1一定等于a2

16、找工作的季节马上就到了,很多同学去图书馆借阅《面试宝典》这本书,现在图书馆外有6名同学排队,其中3名同学要将手中的《面试宝典》还至图书馆,有3名同学希望从图书馆中可以借到《面试宝典》,若当前图书馆内已无库存《面试宝典》,要保证借书的3名同学可以借到书,请问这6位同学有多少种排队方式(180)

C(n,2n)/(n+1) = (2n)!/[a!(a+1)],n是入栈元素的个数,这里n=3,C(3,6)/4=5,同学彼此是不同的,因此要全排列一下,结果为5*3!*3!=180。 如果不考虑同学不同,就是5种

5、为了某项目需要,我们准备构造了一种面向对象的脚本语言,例如,对所有的整数,我们都通过Integer类型的对象来描述。在计算“1+2”时,这里的“1”,“2”和结果“3”分别为一个Integer对象。为了降低设计复杂度,我们决定让Integer对象都是只读对象,也即在计算a=a+b后,对象a引用的是一个新的对象,而非改a所指对象的值。考虑到性能问题,我们又引入两种优化方案:(1)对于数值相等的Integer对象,我们不会重复创建。例如,计算“1+1”,这里两个“1”的引用的是同一个对象——这种设计模式叫做();(2)脚本语言解析器启动时,默认创建数值范围[1,32]的32个Integer对象。现在,假设我们要计算表达式“1+2+3+…+40”,在计算过程需要创建的Integer对象个数是()。

享元模式,40。1到7以及他们的和是不用创建的,从8开始,28(是1到7的和)+8=36,36需要创建,36+9=45,45需要创建…依次类推,在加数是32之前(含32)需要创建的对象是32-8+1=25,某数+32=某数之后33至40所表示的加数也要创建,这样有8个加数 + 8个和,共有16个数需要创建,注意,加数中包含36,这个我们已经创建了,所以有25+8+8-1=40个数的对象需要创建。


 

加分题:

1、给定一个数组a[N],我们希望构造数组b[N],其中b[i]=a[0]*a[1]*...*a[N-1]/a[i]。在构造过程:
不允许使用除法;
要求O(1)空间复杂度和O(n)时间复杂度;
除遍历计数器与a[N] b[N]外,不可使用新的变量(包括栈临时变量、对空间和全局静态变量等);
请用程序实现并简单描述。

加分题:

1、思想是将数组a[j]分成两部分看,先算其前半部分a[0]…a[j-1],然后再乘以其后半部分a[j+1]…a[N-1]。具体代码见CPP程序。

数组分割
1 #include <iostream> 2 using namespace std; 3 4 5 /************************************************************************/ 6 /* 7 打印数组 8 */ 9 /************************************************************************/10 void output(long long* a, int len)11 {12 for(int i = 0; i < len; ++i)13     {14         cout << a[i] << " ";15     }16     cout << endl;17 }18 19 /************************************************************************/20 /*21 题目要求的算法22 */23 /************************************************************************/24 void problem(int* a, long long* b, int N)25 {26     b[0] = 1;27 for(int i = 1; i < N; ++i)28     {29         b[i] = b[i-1] * a[i-1]; // 分水岭的前半段乘积30     }31 32     b[0] = a[N - 1];33 for(int i = N - 2; i >= 1; --i)34     {35         b[i] *= b[0];36         b[0] *= a[i]; // 分水岭的后半段乘积,是从数组尾部向前循环的37     }38 }39 40 41 42 43 int main()44 {45 const int N = 10;46 int a[N] = {1,7,4,3,4,2,5,3,7,3};47 long long multipleResult = 1; // 用long long类型防止数据溢出48 long long refer[N];49 for(int i = 0; i < N; ++i)50     {51         multipleResult *= a[i];52     }53 for(int i = 0; i < N; ++i)54     {55         refer[i] = multipleResult / a[i]; //参考结果56     }57     cout << "参考结果:" << endl;58     output(refer, N);59 60 61 long long b[N];62     problem(a, b, N);63     cout << endl << endl << "满足题意运行的结果:" << endl;64     output(b, N);65 66 return 0;67 }


1-腾讯笔试题:tcp三次握手的过程,accept发生在三次握手哪个阶段?
accept发生在三次握手之后。
第一次握手:客户端发送syn包(syn=j)到服务器。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个ASK包(ask=k)第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)。
三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接。

2-腾讯笔试题:用UDP协议通讯时怎样得知目标机是否获得了数据包
用UDP协议通讯时怎样得知目标机是否获得了数据包?
可以在每个数据包中插入一个唯一的ID,比如timestamp或者递增的int。
发送方在发送数据时将此ID和发送时间记录在本地。
接收方在收到数据后将ID再发给发送方作为回应。
发送方如果收到回应,则知道接收方已经收到相应的数据包;如果在指定时间内没有收到回应,则数据包可能丢失,需要重复上面的过程重新发送一次,直到确定对方收到。

3-腾讯笔试题:统计论坛在线人数分布
求一个论坛的在线人数,假设有一个论坛,其注册ID有两亿个,每个ID从登陆到退出会向一个日志文件中记下登陆时间和退出时间,要求写一个算法统计一天中论坛的用户在线分布,取样粒度为秒。
一天总共有 3600*24 = 86400秒。
定义一个长度为86400的整数数组int delta[86400],每个整数对应这一秒的人数变化值,可能为正也可能为负。开始时将数组元素都初始化为0。
然后依次读入每个用户的登录时间和退出时间,将与登录时间对应的整数值加1,将与退出时间对应的整数值减1。
这样处理一遍后数组中存储了每秒中的人数变化情况。
定义另外一个长度为86400的整数数组int online_num[86400],每个整数对应这一秒的论坛在线人数。
假设一天开始时论坛在线人数为0,则第1秒的人数online_num[0] = delta[0]。第n+1秒的人数online_num[n] = online_num[n-1] + delta[n]。

4-从10G个数中找到中数在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。
不妨假设10G个整数是64bit的。
2G内存可以存放256M个64bit整数。每个整数用了存放10G数,在每个段范围出现的值。
我们可以将64bit的整数空间平均分成256M个取值范围,用2G的内存对每个取值范围内出现整数个数进行统计。这样遍历一边10G整数后,我们便知道中数在那个范围内出现,以及这个范围内总共出现了多少个整数。
如果中数所在范围出现的整数比较少,我们就可以对这个范围内的整数进行排序,找到中数。如果这个范围内出现的整数比较多,我们还可以采用同样的方法将此范围再次分成多个更小的范围(256M=2^28,所以最多需要3次就可以将此范围缩小到1,也就找到了中数)。
也可以不用再分,仅仅是累加每段中累计值,到达5G就是中位数了。

5-两个整数集合A和B,求其交集
1. 读取整数集合A中的整数,将读到的整数插入到map中,并将对应的值设为1。
2. 读取整数集合B中的整数,如果该整数在map中并且值为1,则将此数加入到交集当中,并将在map中的对应值改为2。

两个整数集合A,B,求二者交集、并集、差集

交集:
void Intersect(const vector<int>& A,const vector<int>& B,vector<int>& ans)
{
    map<int, int> Counter;
    const int ASize = A.size(),BSize = B.size();
    for(int i=0; i<ASize; ++i)
        Counter[A[i]] = 1;
    for(int i=0; i<BSize; ++i)
    {
        if(Counter.count(B[i]))
            Counter[B[i]] = 2;
    }
    map<int, int>::iterator it;
    for(it=Counter.begin(); it!=Counter.end(); ++it)
    {
        if(it->second == 2)
            ans.push_back(it->first);
    }
}
并集:
void Union(const vector<int>& A,const vector<int>& B,vector<int>& ans)
{
    set<int> Exist;
    Exist.insert(A.begin(), A.end());
    Exist.insert(B.begin(), B.end());
    set<int>::iterator it;
    for(it=Exist.begin(); it!=Exist.end(); ++it)
        ans.push_back(*it);
}
差集:
void Difference(const vector<int>& A,const vector<int>& B,vector<int>& ans)
{
    set<int> Unique;
    Unique.insert(A.begin(), A.end());
    const int BSize = B.size();
    for(int i=0; i<BSize; ++i)
        Unique.erase(B[i]);//删除在B中也存在的元素
    set<int>::iterator it;
    for(it=Unique.begin(); it!=Unique.end(); ++it)
        ans.push_back(*it);
}
简单测试代码:
    int a[] = {1,2,3,4,5,5,4,3,2,1,6,8,10,9,7};
    int b[] = {2,2,4,4,8,8,6,6,2,4,6,8};
    vector<int> AVec(a,a+sizeof(a)/sizeof(int));
    vector<int> BVec(b,b+sizeof(b)/sizeof(int));
    vector<int> ans;
    cout<<"A:\t";
    copy(AVec.begin(), AVec.end(), ostream_iterator<int>(cout," "));
    cout<<"\nB:\t";
    copy(BVec.begin(), BVec.end(), ostream_iterator<int>(cout," "));
    Intersect(AVec,BVec,ans);
    cout<<"\nA Intersect B:\n";
    copy(ans.begin(), ans.end(), ostream_iterator<int>(cout," "));
    ans.clear();
    Union(AVec,BVec,ans);
    cout<<"\nA Union B:\n";
    copy(ans.begin(), ans.end(), ostream_iterator<int>(cout," "));
    ans.clear();
    Difference(AVec,BVec,ans);
    cout<<"\nA - B:\n";
    copy(ans.begin(), ans.end(), ostream_iterator<int>(cout," "));
 

6-找出1到10w中没有出现的两个数字
有1到10w这10w个数,去除2个并打乱次序,如何找出那两个数?
申请10w个bit的空间,每个bit代表一个数字是否出现过。
开始时将这10w个bit都初始化为0,表示所有数字都没有出现过。
然后依次读入已经打乱循序的数字,并将对应的bit设为1。
当处理完所有数字后,根据为0的bit得出没有出现的数字。
首先计算1到10w的和,平方和。
然后计算给定数字的和,平方和。
两次的到的数字相减,可以得到这两个数字的和,平方和。
所以我们有
x + y = n
x^2 + y^2 = m

7-需要多少只小白鼠才能在24小时内找到毒药
有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,至少要多少只小白鼠才能在24小时时鉴别出那瓶水有毒?
最容易想到的就是用1000只小白鼠,每只喝一瓶。但显然这不是最好答案。
既然每只小白鼠喝一瓶不是最好答案,那就应该每只小白鼠喝多瓶。那每只应该喝多少瓶呢?
首先让我们换种问法,如果有x只小白鼠,那么24小时内可以从多少瓶水中找出那瓶有毒的?
由于每只小白鼠都只有死或者活这两种结果,所以x只小白鼠最大可以表示2^x种结果。如果让每种结果都对应到某瓶水有毒,那么也就可以从2^x瓶水中找到有毒的那瓶水。那如何来实现这种对应关系呢?
第一只小白鼠喝第1到2^(x-1)瓶,第二只小白鼠喝第1到第2^(x-2)和第2^(x-1)+1到第2^(x-1) + 2^(x-2)瓶....以此类推。
回到此题,总过1000瓶水,所以需要最少10只小白鼠。

8-给40亿个不重复的unsigned int的整数,没排过序的,然后再给几个数,如何快速判断这几个数是否在那40亿个数当中?
unsigned int 的取值范围是0到2^32-1。我们可以申请连续的2^32/8=512M的内存,用每一个bit对应一个unsigned int数字。首先将512M内存都初始化为0,然后每处理一个数字就将其对应的bit设置为1。当需要查询时,直接找到对应bit,看其值是0还是1即可。
或者二分查找,折半,多的不漏,少的漏。

9-腾讯笔试题:根据上排的数填写下排的数,并满足要求。
根据上排给出十个数,在其下排填出对应的十个数, 要求下排每个数都是上排对应位置的数在下排出现的次数。上排的数:0,1,2,3,4,5,6,7,8,9。

举一个例子,   
数值: 0,1,2,3,4,5,6,7,8,9   
分配: 6,2,1,0,0,0,1,0,0,0   
0在下排出现了6次,1在下排出现了2次,   
2在下排出现了1次,3在下排出现了0次....   
以此类推..   

 

解题思路:关键是理解“要求下排每个数都是先前上排那十个数在下排出现的次数”。

做以下分析:设总共有n个数,上排a[0...n-1],下排b[0...n-1],。

1)下排n个数的累加和为n,即b[0]+b[1]+...+b[n-1] = n

2)ai*bi的累加和也为n,即a[0]*b[0]+a[1]*b[1]+...+a[n-1]*b[n-1] = n

3)对于b中任意一个元素b[j], 都存在i,a[i] = b[j].

4)对于b中任意一个元素b[j],都有b[j] >= 0

5)如果a中存在负数。其在b中出现的次数一定为0. 如果a中数值大于n,则其出现次数也为0.

6)a中至少有两个非0数值在b中出现的次数非0

 

a:由1)n > n*b[i],其中b[i]为最小值,则a b中一定均有数值0,否则无解。设a[0] = 0,b[0]为a[0]在b中出现次数。

b:由于b中一定存在0,则0的出现次数一定大于0,因此b[0]>0 且b[0] < n,b[1...n-1]中至少一个值为0. 非0元素出现的次数一共是n-b[0].

c:有2)和6)对任意a[i],a[i]*b[i] < n,即b[i] < n/a[i],对所有a[i]>=n/2的元素中,在b中出现的次数必须最多只有1个出现次数不为0,且为1.其余出现次数均为0,即[1, n/2)范围内最多只有n/2-1个元素,故0出现的次数必不小于n/2, [n/2,n)范围内的元素必有一个出现次数为1。因此a数列中也必须有1,否则无解。

d:有c得在数值范围为(0,n/2)中(假设有x这样的数)出现的次数和s为n - b[0]或n-b[0]-1。其中1出现的次数至少为1(由c得)。又如果1出现的次数为1,则1出现的次数已经为2,故1出现的次数必大于1.设为x,则x出现的次数至少为1,而x>1,如果x出现的次数大于1,那么必须要有其他数出现的次数为x,这样无法收敛。故x出现的次数只能为1,1出现的次数只能为2.

 另外:(感谢coolria提出)如果上排数列中无0,则下排数列全是0,是其唯一解。

结论:

1)如果上排数列中有0,此时如果上排数列中无0,1,2,n-4这四个数,则下排数列无解;否则下排数列中0出现的次数为n-4;1出现的次数为2;2出现的次数为1;n-4出现的次数为1;其余为0。

2)如果上排数列中无0,则下排数列全0,是其唯一解。

 

 

10-win32下那个优先级最高, THREAD_PRIORITY_HIGHEST

11-Unix下fork的用法,好像是子进程返回0,父进程返回子进程ID

12-Unxi下通信的最快方法,貌似是共享内存

13-编译执行和解释执行的区别:
 
1、解释程序
 
所谓解释程序是高级语言翻译程序的一种,它将源语言(如BASIC)书写的源程序作为输入,解释一句后就提交计算机执行一句,并不形成目标程序。就像外语翻译中的“口译”一样,说一句翻一句,不产生全文的翻译文本。这种工作方式非常适合于人通过终端设备与计算机会话,如在终端上打一条命令或语句,解释程序就立即将此语句解释成一条或几条指令并提交硬件立即执行且将执行结果反映到终端,从终端把命令打入后,就能立即得到计算结果。这的确是很方便的,很适合于一些小型机的计算问题。但解释程序执行速度很慢,例如源程序中出现循环,则解释程序也重复地解释并提交执行这一组语句,这就造成很大浪费。
 
2、编译程序
 
这是一类很重要的语言处理程序,它把高级语言(如FORTRAN、COBOL、Pascal、C等)源程序作为输入,进行翻译转换,产生出机器语言的目标程序,然后再让计算机去执行这个目标程序,得到计算结果。
 
编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化、存储分配和代码生成。为了完成这些分析综合任务,编译程序采用对源程序进行多次扫描的办法,每次扫描集中完成一项或几项任务,也有一项任务分散到几次扫描去完成的。
值得一提的是,大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,但也有的编译程序则先产生汇编语言一级的符号代码文件,然后再调用汇编程序进行翻译加工处理,最后产生可执行的机器语言目标文件。
 
在实际应用中,对于需要经常使用的有大量计算的大型题目,采用招待速度较快的编译型的高级语言较好,虽然编译过程本身较为复杂,但一旦形成目标文件,以后可多次使用。相反,对于小型题目或计算简单不太费机时的题目,则多选用解释型的会话式高级语言,如BASIC,这样可以大大缩短编程及调试的时间。

14:
有两个集合
集合A{1,7,19,21,55,100。。。}
集合B{7,22,100。。。}
两个集合都是10万个数据(已排序),要求写一个算法,判断B是不是A的子集,算法时间复杂度为Q(N)
如果都是10万,要是子集关系,不就一定要相同嘛,不然怎么成子集关系呢
bool isSubUnion(int a[], int b[], int lenA, int lenB)
{
    int bb = 0;
    int aa = 0;
    while (bb < lenB && aa < lenA)
    {
        if (b[bb] < a[aa])  // b小的话,说明不被包含
        {
            return false;
        }
        else if (b[bb] > a[aa]) // 如果b大的话,a++
        {
            aa++;
        }
        else  // 相等的话,都进入下一位
        {
            bb++;
            aa++;
        }
    }
    if (bb == lenB)
    {
        return true;
    }
    else
    {
        return false;
    }
    return true;
}

15-{1,2,3,5,8,13}
可以用01110100100001000000表示。
21-在程序设计中,要对两个16K×16K的多精度浮点数二维数组进行矩阵求和时,行优先读取和列优先读取的区别是(B)
A、没区别
B、行优先快                                   // 懂二维数组在内存中的存放顺序
C、列优先快
D、2种读取方式速度为随机值,无法判断

22-字符串www.qq.com所有非空子串(两个子串如果内容相同则只算一个)个数是(D)
A、1024
B、1018
C、55
D、50  // 长度1的子序列有10-2-1-1=6个长度2子序列有9-1=8个,长度3有8个,长度4有7个…长度10有1个,加起来就是50。

23-操作系统的一些特别端口要为特定的服务做预留,必须要root权限才能打开的端口描述正确的是(C)
A、端口号在64512-65535之间的端口
B、所有小于1024的每个端口      // 也有大于1024的特殊端口
C、RFC标准文档中已经声明特定服务的相关端口,例如http服务的80端口,8080端口等
D、所有端口都可以不受权限限制打开

24-卡特兰数,C(n,2n)/(n+1)
 
 

问题:

给出一个链表,判断是否有环(可能是首尾相连,也可能是中间的某个节点构成环)。

这个题最开始想的是输入参数包含一个头节点及这个链表的元素个数,那么这样我们在循环的时候加一个步长就可以了,如果steps>len说明肯定存在环了。但是没说给你元素个数怎么办,只给你个头指针。然后我就想开一个数组记录p指针走过的路径,这样每走过一个节点判断这个地址是否走过,这样的话空间复杂度又不行了。看网上的流行解法吧:

步长法:

p=head;

q=head;

while(p && q && q->next)

{

    p=p->next;

    q=q->next->next;

    if(p == q)

        return 1;

}

return 0;

解释:p每次走一步,q每次走2步,这样如果存在循环节,我们假设循环节长度为m,那么肯定存在一个整数i使得

(p+i)%m=(q+2*i)%m,这样我们就可以判断是否存在循环节了。这个题可以算是一个拓展思维的好题了。

 
 

 
 





posted @ 2013-09-20 23:12  陈卓生  阅读(395)  评论(0编辑  收藏  举报