风行网2012年10月17笔试题

2610阅读 0评论2013-11-18 梦醒潇湘love
分类:C/C++


1、给定一个字符串A和字符串B,写一函数实现在字符串A中查找字符串B首次出现的位置,如果查找不成功,则返回-1(蛮力法、KMP算法)
解:
    (1)函数strstr()的实现
  1. char sstrstr(const char *haystack, const char *needle)
  2. {
  3.     assert(needle != NULL && haystack != NULL);

  4.     while(*haystack != '\0')
  5.     {
  6.         int i = 0;
  7.         while(true)
  8.         {
  9.             char an = *(haystack + i);
  10.             char bm = *(needle + i);
  11.             if(bm == '\0')
  12.             {
  13.                 return haystack;
  14.             }
  15.             if(an == bm)
  16.             {
  17.                 i++;
  18.             }
  19.             else
  20.             {
  21.                 break;
  22.             }
  23.         }
  24.         haystack++;
  25.     }
  26.     //若needle == '\0',则返回haystack;反之,则查找失败
  27.     return *needle == '\0' ? haystack : NULL;
  28. }
    (2)kmp算法的实现
  1. //获取next数组
  2. void get_next(cosnt char *ptn, int *next)
  3. {
  4.     assert(ptn != NULL && next != NULL);

  5.     int i = 0;
  6.     int j = -1;
  7.     next[0] = -1;
  8.     int plen = strlen(ptn);

  9.     while(i < plen)
  10.     {
  11.         if(j == -1 || ptn[i] == ptn[j])
  12.         {
  13.             i++;
  14.             j++;
  15.             if(ptn[i] != ptn[j])
  16.             {
  17.                 next[i] = j;
  18.             }
  19.             else
  20.             {
  21.                 next[i] = next[j];
  22.             }
  23.         }
  24.         else
  25.         {
  26.             j = next[j];
  27.         }
  28.     }
  29. }
  30. //kmp匹配
  31. int kmp_search(const char *src, const char *ptn, int *next, int pos)
  32. {
  33.     int i = pos;
  34.     int j = 0;
  35.     if(src == NULL || ptn == NULL)
  36.     {
  37.         return -1;
  38.     }
  39.     int slen = strlen(src);
  40.     int plen = strlen(ptn);
  41.     if(pos < 0 || pos > slen)
  42.     {
  43.         return -1;
  44.     }

  45.     while(i < slen && j < plen)
  46.     {
  47.         if(j == -1 || src[i] == ptn[j])
  48.         {
  49.             i++;
  50.             j++;
  51.         }
  52.         else
  53.         {
  54.             //当匹配失效时,直接用p[j_next]与s[i]比较
  55.             j = next[j];
  56.         }
  57.     }
  58.     if(j >= plen)
  59.     {
  60.         return i - plen;    //返回下标,从0开始
  61.     }
  62.     else
  63.     {
  64.         return -1;
  65.     }
  66. }

2、
给定两个有序链表,将这两个有序链表合并成一个有序链表.(归并排序的思想,但是需注意的时此处为链表,指针操作稍微复杂些)
解:
    具体代码如下所示。
  1. ListNode *mergeTwoLists(ListNode *first, ListNode *second)
  2. {
  3.     ListNode *node = NULL;
  4.     ListNode **p = &node;
  5.     
  6.     while(first && second)
  7.     {
  8.         if(first->value <= second->value)
  9.         {
  10.             *p = first;
  11.             first = first->next;
  12.         }
  13.         else
  14.         {
  15.             *p = second;
  16.             second = second->next;
  17.         }
  18.         p = &((*p)->next);
  19.     }
  20.     if(first)
  21.     {
  22.         *p = first;
  23.     }
  24.     if(second)
  25.     {
  26.         *p = second;
  27.     }
  28.     return node;
  29. }

3、给定两个长度相同的有序的数组,求这两个数组的中位数(使用递归实现,比较数组A和B的中位数大小,若A的中位数大于B的中位数,则这来两个数组的中位数必定在A的前半部分和在B的后半部分,反之则在A的后半部分和B的前半部分,依次而推即可得到最终的整个中位数,注意细节)
解:
    (1)将两个数组进行合并,然后取其中位数,代码略。
    (2)二分的思想。

4、面向程序设计的五个基本原则。
解:
    略。

5、TCP是如何实现流量控制的?(使用了窗口对流量实现控制)
解:
    (1)TCP利用滑动窗口实现流量控制。
  如果发送方把数据发送的太快,接收方可能会来不及接收,这就会造成数据的丢失。
  所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。
  利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。
  设A向B发送数据,在连接建立时,B告诉A:“我的接收窗口是rwnd=400”(这里的rwnd表示receive window)。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP的窗口单位是字节,不是报文段。
    
    
    从图中可以看出,B进行了三次流量控制。第一次把窗口减少到了rwnd = 300,第二次又减少到了rwnd=100,最后减少到rwnd=0,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发送一个新的窗口值为止。
  TCP为每一个连接设有一个持续计时器。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段(携1字节数据),那么收到这个报文段的一方就重新设置持续计时器。
  必须考虑传输速率
  可以用不同的机制来控制TCP报文段的发送时机。如TCP维持一个变量,它等于最大报文段长度MSS。只要缓存中存放的数据达到MSS字节时,就组装成一个TCP报文段发送出去;由发送方的应用进程指明要求发送报文段,即TCP支持的推送操作;发送方的一个计时器到期,这时就把已有的缓存数据装入报文段发送出去。
  Nagle算法:若发送应用进程把要发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方接收到第一个数据字符的确认后,再把发送缓存中的所有数据组成一个报文段发送出去,同时继续对随后到达的数据进程缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。
  TCP的拥塞控制
  拥塞:即对资源的需要超过了可用的资源。
  拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程。
  流量控制:指点对点通信量的控制,是端到端的问题。流量控制所要做的就是抑制发送端法发送数据的速率,以便使接收端来不及接收。
  几种拥塞控制方法
  慢启动、拥塞避免、快速重传、快速恢复。
  慢启动和拥塞避免
  发送方维持一个拥塞窗口的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地变化。发送方让自己的发送窗口等于拥塞窗口。
  发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减少一些,以减少注入到网络中的分组数。
慢启动算法:当主机开始发送数据时,如果立即把所有的数据注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口的数值。通常在刚刚开始发送报文段的时候,先把拥塞窗口cwnd设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理。
    

    每经过一个传输过程,拥塞窗口cwnd就加倍。一个传输轮次所经历过的时间就是往返时间RTT。
  另外,慢启动的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方再开始时只发送一个报文段,然后在逐渐增大cwnd。
  为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢启动阈值ssthresh状态变量。慢启动阈值ssthresh的用法如下:
    

    拥塞避免算法:让拥塞窗口cwnd缓慢地增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按现行规律缓慢增长,比慢启动算法的拥塞窗口增长速率缓慢得多。
  无论是慢启动阶段还是拥塞避免阶段,只要发送方判断网络出现拥塞(其根据是没有收到确认),就要把慢启动阈值ssthresh设置为出现拥塞时的发送方窗口值的一半。然后把拥塞窗口cwnd重新设置为1,执行慢启动算法。
    
 
   强调:拥塞避免并非指完全能够避免了拥塞。只是使网络比较不容易出现拥塞。
    另外还有快速重传和快速恢复机制。

6、进程和线程之间的区别。
解:  
    (1)进程是程序的一次执行,是系统资源分配的基本单位。而线程是系统调度的基本单位。
  (2)进程消耗的系统资源比较多,而线程基本不消耗系统资源,进程有自己的地址空间,不共享存储区域,而线程则共享其属主进程的地址空间,共享同一存储区域。
    (3)一个程序至少有一个进程,而一个进程至少有一个线程。线程是轻量型进程。

7、大量电影播放日志文件统计出前100个播放次数最多的电影名称
解:
    分而治之的思想。
  (1)将大的日志文件hash(电影名称)%n(n根据实际情况而定),这样产生了n个小文件,这n个小文件的每一个都可以放入内存的;
  (2)对每一个小文件进行统计其所包含的每部电影出现的次数,同时求出每个小文件中播放次数最多的100部电影;
    (3)对这n个小文件取出的n个100部电影进行统计,可以用最大堆,从而求出整个大的日志文件中前100次播放次数最多的电影名称。

    
上一篇:C++函数中那些不可以被声明为虚函数的函数
下一篇:腾讯2012年实习生笔试题