精华内容
下载资源
问答
  • 1. 有10个学生,每个学生数据包括学号、姓名、英语、数学、物理三门课成绩,从键盘输入10个学生数据,要求打印出3门课程总平均成绩,以及最高分学生数据(包括学号,姓名,3门课成绩,平均分数)。...
  • ``` typedef struct LNode { int data; struct LNode* next; }LNode,*LinkList; LinkList LocalElem(LinkList a,int e) ...输出怎么总是出来一个8,请问要怎么改???(代码里面省略了创建链表的部分)
  • ”01—map概览Go语言中map用来存储键值对,可以分别指定键和值的类型。var a map[string]stringmap类型变量本质上是个指针,这些键值对实际上是通过哈希表来存储。图:map结构概览为什么要用哈希表?能够存储...

     来画一画map底层的哈希表,再看一看map是怎样扩容的吧!

    01

    map概览

    Go语言中map用来存储键值对,可以分别指定键和值的类型。
    var a map[string]string
    map类型的变量本质上是个指针,这些键值对实际上是通过哈希表来存储的。

    3ec441c6acd371d5c608d6c2381e01b6.png

    图:map结构概览
    为什么要用哈希表?能够存储键值对的数据结构有很多种,可以是数组,也可以是链表,但是能用不代表好用。如果每次查找一个键都要从第一个元素开始遍历,直到发现匹配的键或者遍历完所有元素,这样的时间复杂度就是O(n)。

    cf6d6298e0a55aa60f9a2540f314464f.png

    图:数组和链表存储键值对
    而哈希表理论上可以实现O(1),下面我们一起设计一个简单的哈希表,看看它是怎样工作的。

    02

    HashTable


    哈希表首先要有一段内存用作存储键值对的桶(bucket),每个桶里存什么?暂时就先存一个键值对外加一个哈希值。

    5d5e5aff7265c2f4e76ff27e68dfa9f5.png

    图:哈希表结构概览这个哈希值就是对键值对的键经过哈希函数处理后得到的结果,但是我们只把它当作一个数值来用,用来干什么呢?选择桶。你想,如果你要存储键值对“k1:v1”,这有8个桶,你选哪一个?假设哈希值为hash,桶数量为m。通常我们有两种方法来选择一个桶:第一种:取模运算    hash%m第二种:按位与运算    hash&(m-1)第二种更加高效,但限制了m必须要是2的整数次幂,这样才能保证与运算结果落在[0,m-1],而不会出现有些桶注定不会被选中的情况。我们这里就用第一种方法简单展示下,如果选择了索引4这个桶,把键值对存进去以后,哈希表如下图所示:

    c4c74cfb3fbefeaaa80e49dbe547522d.png

    如果想从哈希表里查找这个键的值,只需用同样的方法对键进行哈希求值,再对桶数目取模就能定位到键值对所在的桶了。不用遍历,的确很不错~

    03

    哈希冲突

    继续上面的哈希表,那咱们再存个键值对“k2:v2”,同样要对键求哈希值然后对桶个数取模,好巧不巧,又是编号为4这个桶。这个桶已经被占用了,咋办?这种情况,就叫做发生了哈希冲突,通常有两种思路来解决。第一种:开放地址法编号为1的桶被占用了是吧,那我就用下一个空闲桶来存就好了,正好下一个编号为5的桶闲着呢,那就存这里吧!这样查找这个key时,同样会先定位到编号为4的桶,但是一比较发现key不对,所以,就继续检测下一个桶,直到key相等或遇到空桶时结束。

    f08ef5d3fcfe33d7264aa28ceec08d9c.png

    图:开放地址法第二种:拉链法这种方法会把发生冲突的键值对用链表结构串起来,所以每个键值对还要额外记录一个指针,指向下一个选择同一个桶的键值对。按照key查找时,也会首先定位到同一个桶,然后通过比较key的值来锁定键值对。

    2487c98f91daaa715da6ac90315b9843.png

    图:拉链法通过拉链法来解决哈希冲突较为常见,不过上面介绍的仅仅是解决问题的思路,具体实现还要根据需求做特定的修改和设计,哈希函数的选择也很关键,如果哈希函数可以很均匀的把key映射到各个桶里,发生哈希冲突的几率就会降低,反正绝不会像我们这么简单粗暴就是了......好了,我们设计的哈希表已经可以存储和查找,并且可以解决哈希冲突了,接下来要考虑的就是扩容问题了。

    04

    扩容

    关于扩容,我们要搞清楚两点:第一:何时扩容?第二:怎么扩容?判断哈希表是否需要扩容的指标叫做负载因子(Load Factor),它是哈希表中存储的键值对数量和桶数量的比值,通常需要设定一个触发扩容的最大负载因子。这里我们设置50%(因为好算~)。    loadFactor = keyCount / bucketCount知道了何时扩容,下面就来到了第二个问题,怎么扩容?直接的想法是重新分配更多的桶,然后把键值对都存到新的桶里。那我们就翻倍扩容吧。下面我们再添加几个键值对以触发扩容。

    eaa0e992b326aec3ae773e710543345b.png

    图:再添就要扩容啦首先分配16个新桶,然后逐个把原来的键值对映射到新的桶里。这种扩容方式被称为“一次性扩容”,就是一次性把所有键值对挪到新的桶里。

    5e9a3f1ba498382d077cfef9ee4a4ec2.png

    图:一次性扩容但是如果键值对数量很多,每次扩容占用的时间太长就会造成性能瞬时明显抖动,所以通常会选择“渐进式扩容”这种方式下,旧桶里的键值对是分多次挪到新桶里的,可以在触发扩容后,先分配新桶,然后标记当前哈希表正在扩容,并在哈希表的读写操作中判断若在扩容中,则迁移一部分键值对到新桶里,这样可以把扩容消耗的时间分散到多次操作中。

    6abbdc22a043316cba7e56f7e48cd060.png

    图:渐进式扩容中

    650ea2ecd564a3ad796c4bc8e524c617.png

    图:渐进式扩容中OK,我们这个简单的哈希表就设计到这里,理解哈希表大致的结构与用法就好。下面我们看看Go语言的map到底长啥样~

    05

    map 桶

    map类型的变量本质上是一个hmap类型的指针:
    type hmap struct {    count     int    //已经存储的键值对个数    flags     uint8    B         uint8  // 常规桶个数等于2^B    noverflow uint16 // 使用的溢出桶数量    hash0     uint32 // hash seed    buckets    unsafe.Pointer // 常规桶起始地址    oldbuckets unsafe.Pointer // 扩容时保存原来常规桶的地址    nevacuate  uintptr        // 渐进式扩容时记录下一个要被迁移的旧桶编号    extra *mapextra}
     map底层的哈希表通过与运算的方式选择桶,所以hmap中并不直接记录桶的个数,而是记录这个数目是2的多少次幂。下面我们先看看map使用的桶长什么样子。map使用的桶很有设计感,每个桶里可以存储8个键值对,并且为了内存使用更加紧凑,8个键放一起,8个值放一起。对应每个key只保存其哈希值的高8位(tophash)。而每个键值对的tophash、key和value的索引顺序一一对应。这就是map使用的桶的内存布局。

    a3cccd22ec562d7278f9ec25d5fdd9b0.png

    图:bmap的内存布局既然每个桶里可以存储8个元素,那存满了怎么办?扩容的代价还是比较高的,所以为了减少扩容次数,这里引入了溢出桶(overflow)溢出桶的内存布局与之前介绍的常规桶相同。如果哈希表要分配的桶的数目大于2^4,就会预分配2^(B-4)个溢出桶备用。这些常规桶和溢出桶在内存中是连续的,只是前2^B个用作常规桶,后面的用作溢出桶。hmap结构体最后有一个extra字段,指向一个mapextra结构体,里面记录的都是溢出桶相关的信息:
    type mapextra struct {    overflow    *[]*bmap //把已经用到的溢出桶链起来    oldoverflow *[]*bmap //渐进式扩容时,保存旧桶用到的溢出桶    nextOverflow *bmap   //下一个尚未使用的溢出桶}
     

    d660667394d78d257eae0ea100ae5ca5.png

    图:预分配溢出桶如果当前桶存满了以后,检查hmap.extra.nextoverflow还有可用的溢出桶,就在这个桶后面链上这个溢出桶,然后继续往这个溢出桶里存。而hmap.extra.nextoverflow继续指向下一个空闲的溢出桶。所以这里解决哈希冲突的方式应该属于拉链法。

    9448d4c5b5de9f93ce0e2e70259287c4.png

    图:占用溢出桶

    06

    map key

    Go语言中可以通过“==”来比较是否相等的类型,都可以作为map的key类型。其实Go语言中每种类型都有对应的类型元数据,类型元数据都有一个相同的Header,就是runtime._type。而在_type.alg这里记录了该类型的两个函数:hash和equal。
    type typeAlg struct {    hash  func(unsafe.Pointer, uintptr) uintptr    equal func(unsafe.Pointer, unsafe.Pointer) bool}
      

    7c9f312a94dc10cc8b2effeba1f9da91.png

    图:key的可比较性而不可比较的类型,例如slice,它的类型元数据里是没有提供可用的equal方法的。因此并不能用作map的key,连带着含有slice的结构体类型也不可以。

    07

    用下试试

    a := map[string]string{}a["k1"] = "v1"
    因为每个桶里可以存储8个键值对,所以上面创建的这个map只拥有一个桶,也没有预分配的溢出桶。注意观察bmap的内存布局,key和value都是string类型。

    66a61340458f1ee6a1c608b07c636d49.png

    图:只有一个桶的map下面我们把这个桶填满:
    a["k2"] = "v2"a["k3"] = "v3"a["k4"] = "v4"a["k5"] = "v5"a["k6"] = "v6"a["k7"] = "v7"a["k8"] = "v8"
    存完以后,这个桶变成了这样:

    b38a47146f52460be43e96337e60cee3.png

    图:存满一个桶如果继续存储新的键值对,这个哈希表是会创建溢出桶还是会发生扩容呢?下面我们就具体看一下map的扩容规则。

    08

    map翻倍扩容

    map的扩容有两种情况,如果超过负载因子(默认6.5)就触发翻倍扩容;    hmap.count / 2^hmap.B > 6.5分配新桶数目是旧桶的2倍,hmap.oldbuckets指向旧桶,hmap.buckets指向新桶。hmap.nevacuate为0,表示接下来要迁移编号为0的旧桶。

    1263d54990570bca8da52c0282f9d4f9.png

    然后就是通过渐进式扩容的方式,每次读写map时检测到当前map处在扩容阶段(hmap.oldbuckets != nil),就执行一次迁移工作,把编号为hmap.nevacuate的旧桶迁移到新桶,每个旧桶的键值对都会分流到两个新桶中,这是一个数学问题。例如:将旧桶数量记为m=4,新桶数量就是2m=8,如果一个哈希值选择0号旧桶:h&(m-1)=0,那么h的二进制最低两位一定为0:        xxxxxx00    & 00000011    -------------------        00000000所以h&(2m-1) 的结果只有两种:0或4,取决于h的第三位是0还是1。如果h的第三位为0,则选择编号为0的新桶:        xxxxx000    & 00000111    -------------------        00000000如果h的第三位为1,则选择编号为4的新桶:        xxxxx100    & 00000111    -------------------        00000100因为m一定是2的整数次幂,所以无论容量为多少,翻倍扩容后,每个旧桶都会按照这样的规律分流到两个新桶中。

    ab96f4cd7bfa02bf2554e9f52a456f09.png

    图:旧桶迁移分流编号为hmap.nevacuate的旧桶迁移结束后会增加这个编号值,直到所有旧桶迁移完毕,把hmap.oldbuckets置为nil,一次翻倍扩容结束。

    09

    map等量扩容

    如果没有超过设置的负载因子上限,但是使用的溢出桶较多,也会触发扩容,不过这一次是等量扩容那我们就要弄清楚两个问题:第一:用多少溢出桶算多?第二:既然等量,迁移来迁移去的有什么用?先来看第一个问题:(1)如果常规桶数目不大于2^15,那么使用的溢出桶数目超过常规桶就算是多了;(2)如果常规桶数目大于2^15,那么使用溢出桶数目一旦超过2^15就算多了。下面再看第二个问题,等量扩容,就是创建和旧桶数目一样多的新桶,然后把原来的键值对迁移到新桶中。我们可以考虑什么情况下,桶的负载因子没有超过上限值,却偏偏使用了很多溢出桶呢?自然是有很多键值对被删除的情况。

    c4da4a2179b2037099307ba24fa95d27.png

    图:桶中很多键被删除这种情况下,如果把这些键值对重新安排到等量的新桶中,虽然哈希值没变,常规桶数目没变,每个键值对还是会选择与旧桶一样的新桶编号,但是能够存储的更加紧凑,进而减少溢出桶的使用。

    0e4584fda34eeb2b75ac0ed9a32fe4ab.png

    图:等量扩容迁移后

    10

    不可寻址

    正是因为扩容过程中会发生键值对迁移,键值对的地址也会发生改变,所以才说map的元素是不可寻址的,如果要取一个value的地址则不能通过编译。
     //k := &a["k1"]会在编译阶段出错cannot take the address of a["k1"]

    86d884ea858c5a6342f32839cebde7ae.png

    展开全文
  • //k记录字符串中的字符转化为整数的值,l使每个节点记录4位 while(a[m]!=';') m++; //m记录字符串中被加数的字符数 n=m; while(a[n]!='\0') n++; //n记录字符串的总字符数 if(a[0]=='-') { head0->data=(-1...
  • 你必须知道495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    5.10但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 5.12 我用预处理宏#defineNullptr(type)(type*)0帮助...
  • 问题:建立一个简单链表输出各结点中数据。 这是原程序:#include ...其实n可以事先在程序定义好,但是我用New运算符怎么输出不了结果。请求大神能在上面程序基础上做一些修改使其满足条件。谢谢!!
  • 5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 58  5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 58 5.12 我用预处理宏#define Nullptr(type)(type...
  • 《你必须知道495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 58  5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 58 5.12 我用预处理宏#define Nullptr(type)(type...
  • int List::FindCurrent()//存取当前结点的值 { int item; item=p->data;// return item; } int List::FindK(int k)//存取第K个结点的值 { int item; if(k){return -1;} else{ p=head; int i=0; while(p!=...
  • 5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0)不是更好吗? 87 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 87 5.12 我用预处理宏#define Nullptr(type)(type *)0...
  • 5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0)不是更好吗? 87 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 87 5.12 我用预处理宏#define Nullptr(type)(type *)0...
  • 5.10但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。  5.12 我用预处理宏#defineNullptr(type)(type*)0帮助创建...
  •  5.10但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 5.12 我用预处理宏#defineNullptr(type)(type*)0帮助...
  • 5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 5.12 我用预处理宏#define Nullptr(type)(type *)0帮助创建...
  •  5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0)  不是更好吗?  5.11 我曾经使用过一个编译器,不使用NULL就不能编译。  5.12 我用预处理宏#define Nullptr(type)...
  • 1)在有序链表中,插入一个n; 2)统计英文文本中,单词出现次数,要求,输出按照出现次数降序输出。 3)头文件,定义全局变量和静态变量。会怎么样? 4)阻塞与非阻塞IO区别,举例说明应用场景。 5)...

    声明:本文系找工作以来,笔试面试题,只为广大毕业党做个参考。如有不妥之处,欢迎联系本人微笑

    2016年THS的笔试题(只记得这些)

    1)在有序链表中,插入一个值n;

    2)统计英文文本中,单词的出现次数,要求,输出按照出现次数的降序输出。

    3)头文件里,定义全局变量和静态变量。会怎么样?

    4)阻塞与非阻塞IO的区别,举例说明应用场景。

    5)某个四位数的4倍等于它的反序数,输出这个四位数。

    6)指针与引用的区别。

    7)宏定义求数组的长度。

     

    2016年BD测试一面

    1)  举例UDP的适用情况。流socket使用TCP协议,数据包socket使用UDP协议。

    TCP和UDP两种协议都是传输层协议,为应用层提供信息载体。

    TCP协议是基于连接的可靠协议,有流量控制和差错控制,也正因为有可靠性的保证和控制手段,所以传输效率比UDP低;

    UDP协议是基于无连接的不可靠协议,没有控制手段,仅仅是将数据发送给对方,因此效率比TCP要高

    基于上述特性,不难得到结论,TCP协议适用于对效率要求相对低,但对准确性要求相对高的场景下,或者是有一种连接概念的场景下;而UDP协议适用于对效率要求相对高,对准确性要求相对低的场景。

    举几个应用的例子。TCP一般用于文件传输FTP和 HTTP 对数据准确性要求高,速度可以相对慢),发送或接收邮件POP IMAP SMTP 对数据准确性要求高,非紧急应用),远程登录TELNET SSH 对数据准确性有一定要求,有连接的概念)等等;

    UDP一般用于即时通信QQ聊天,对数据准确性和丢包要求比较低,但速度必须快),在线视频(RTSP 速度一定要快,保证视频连续,但是偶尔花了一个图像帧,人们还是能接受的),网络语音电话(VoIP 语音数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题)等等。

    作为知识的扩展,可以再说一些其他应用。比如,TCP可以用于网络数据库,分布式、高精度计算系统的数据传输UDP可以用于服务系统内部之间的数据传输,因为数据可能比较多,内部系统局域网内的丢包错包率又很低,即便丢包,顶多是操作无效,这种情况下,UDP经常被使用。

    2)  给你一个聊天类的开发软件,测试其性能,举出测试用例的情况。

    3)  C++中的多态,举一个虚函数的应用的例子,说明虚函数的作用。

    4)  STL中vector的内存分配是怎么进行的。

    5)  说出信息,比如聊天软件的信息,传输的三个比较重要的特征。

    6)  手写代码,求两个二叉树结点的最低父节点。

    2016QZ研发一面

    1)  写函数strlen的递归与非递归。有瑕疵!!!

    2)  其他谈的都是项目。LCD分辨率800*480,LCD的刷新率,LCD与MCU的接口类型(LCD接口类型为16位的80并口,MCU通过FSMC接口)。


    2016SG测试一面(霸面)

    1、  归并排序。在面试官笔记本实现完整函数!!!

    2、  单链表逆置。没运行出来!!!见附件程序。

    以下为智力开发题

    3、  分蛋糕问题。

    第一种情况:首先,有两块圆形蛋糕,比如我跟面试官老师。由面试官老师切蛋糕,两次的优先选择权,每人一次。如,第一个蛋糕老师先拿,第二个蛋糕我先拿。但切蛋糕的人是老师!问老师最多拿多少?(最多拿5/4)

    第二种情况:有三块蛋糕,每次都是面试官切。我有2次优先选择权,老师有一次优先选择权。问老师最多拿多少块蛋糕?(详细解答见图片)

    4、  判断一个正整数n是不是2的整次幂?

    只需要判断该整数的二进制中只有一个1.用n&(n-1)运行一次即可。

    bool  fun(int i){  return (i>0)&&(i&(i-1)==0);   }

    判断一个正整数n是不是4的整次幂?

    代码:bool fun(int num)

    {

      //依次判断num>0,只有一个1,判断唯一的1在奇数位上。则返回true

      return(num>0)&&(num&(num-1))==0&&(num&0xAAAAAAAA)==0;

    }

    5、12个球,只有1个异常球,11个正常球。求只需称重三次,无刻度天平,找出哪个球是异常球,并且异常球比正常球轻了,还是重了?见附图。

    6、  附加题,8个人过河。见附件程序。

    2016ZX终面

    1、  LCD驱动函数

    2、  中断函数(项目上)

    3、  内联函数,函数调用的时候,局部变量需要入栈吗?首先入栈的是被调函数的下一条有效指令,对吗?

    内联函数主要分成两种,一种是类成员内部的内联函数,一种是类外面的全局内联函数。

    在C++的类成员中,如果成员函数的函数体本身结构不复杂,代码量也较少的时候,直接在定义这个函数的时候就完成该函数的实现,这样的一个过程在C++类中会被默认当做内联函数。构造函数在某些情况下就是一个较为典型的内联函数。

    第一种为类的成员函数,当类成员函数的定义在内部,实现的时候在外部实现,也需要在前面加上关键字inline,就能够告诉编译器这是一个内联函数了,这个与全局函数区别不大。

    第二种为类外部的内联函数,当将一个全局函数定义成内联函数的时候,需要加一个inline 的关键字说明,相当于告诉编译器建议将该函数当内联函数进行处理。

    减少因为函数调用引起开销,主要是参数压栈、栈帧开辟与回收,以及寄存器保存与恢复等。

    函数调用过程中,第一个进栈的是(主函数中的)调用处的下一条指令(即函数调用语句的下一条可执行语句)的地址;然后是函数的各个参数,而在大多数C/C++编译器中,在函数调用的过程中,函数的参数是由右向左入栈的;然后是函数内部的局部变量(注意static变量是不入栈的);在函数调用结束(函数运行结束)后,局部变量最先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行。

    函数调用方式决定了函数参数入栈的顺序,是由调用者函数还是被调用函数负责清除栈中的参数等问题,而函数名修饰规则决定了编译器使用何种名字修饰方式来区分不同的函数,如果函数之间的调用约定不匹配或者名字修饰不匹配就会产生以上的问题。

    4、  进程和线程的区别。线程共享进程的系统资源,举例说明什么系统资源(打开的文件,创建的socket等),线程拥有运行中必不可少的资源(如程序计数器、一组寄存器和栈)

    5、  手写双链表的插入。(需要考虑插入的后一节点是否为空)

     

    2016年DJ笔试

    编程题1、2(不太记得了)

    问答题1、现分别有两个5升和3升大的水桶,在操作步数最少的情况下,求欲取出4升水怎么办?

    问答题2、extern声明全局函数。

    http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html

    const关键字作用

    http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html

    选择题:

    1、  JTAG测试信号5个:

    2、  ARM体系的7种工作模式:

    3、  A/D转换步骤:

    4、  ARM的异常处理

    5、  中断函数:

    6、  信号量与消息队列。消息队列可通过信号量和Buffer队列实现。

    2016XHS笔试

    1、整型加法运算,整型乘法,浮点数加法,浮点数乘法运算速度排序。

    2、进程和线程的含义及区别。

    进程:具有一定独立功能的程序关于某个数据集合上的一次运行,是系统进行资源分配和调度的独立单元。

    线程:是进程的一个实体,是CPU调度和分配的基本单元。

    区别:1)一个线程只能属于一个进程,而一个进程可以拥有多个线程。

    2)  同属于一个进程的多个线程共享该进程的所有资源,如打开的文件,创建的socket等。

    3)  线程之间切换的代价比进程之间切换的代价小。

    4)  每个进程都有独立的内存空间,而线程共享其所属进程的内存空间。

    3、编程题1:链表的倒数第K个结点。

    4、编程题2:实现一个单例模式。

    5、编程题3:检查输入表达式中的(),[]和{}是否匹配。匹配则输出true,否则输出false。

    6、编程题4:实现单一对的()匹配,要求空间复杂度为O(1)。

    7、编程题5:数组的循环右移。要求时间复杂度为O(n)

    8、简答题:浏览器输入url,到整个页面渲染的整个过程。

      整个过程大致可分为网络通信和页面渲染。

    1) 网络通信

    互联网设备间的通信遵循TCP/IP协议,利用TCP/IP协议族进行网络通信时,数据发送端从应用层、传输层、网络层、数据链路层,往下走。接收端从数据链路层往上走。

    (1)      应用层DNS解析域名:首先检查浏览器缓存中是否有对应的IP地址,若找到则返回,否则,请求上级DNS服务器,直至到达根服务器。

    (2)      应用层客户端发送HTTP请求,请求包里包含请求方法,目标url,协议类型,及客户端是否发送cookie等。

    (3)      传输层TCP传输报文。(TCP的三次握手)

    (4)      网络层IP协议查询MAC地址。

    IP协议的作用是把TCP分割好的各种数据包传送给接收方。而要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址。IP地址和MAC地址是一一对应的关系,一个网络设备的IP地址可以更换,但是MAC地址一般是固定不变的。ARP协议可以将IP地址解析成对应的MAC地址。当通信的双方不在同一个局域网时,需要多次中转才能到达最终的目标,在中转的过程中需要通过下一个中转站的MAC地址来搜索下一个中转目标。

    (5)      数据到达数据链路层

    在找到对方的MAC地址后,就将数据发送到数据链路层传输。这时,客户端发送请求的阶段结束。

    (6)      服务器接收请求数据

    接收端的服务器在链路层接收到数据包,再层层向上直到应用层。这过程中包括在运输层通过TCP协议将分段的数据包重新组成原来的HTTP请求报文。

    (7)      服务器响应请求

    服务接收到客户端发送的HTTP请求后,查找客户端请求的资源并返回响应报文,响应报文中包括一个重要的信息——状态码。状态码由三位数字组成,其中比较常见的是200 OK表示请求成功。301表示永久重定向,即请求的资源已经永久转移到新的位置。在返回301状态码的同时,响应报文也会附带重定向的url,客户端接收到后将http请求的url做相应的改变再重新发送。404 not found 表示客户端请求的资源找不到。

    (8)      服务器返回相应文件

    请求成功后,服务器会返回相应的HTML文件。接下来就是页面渲染。

    2) 页面渲染

    现代浏览器渲染页面的过程是这样的:jiexiHTML以构建DOM树 –> 构建渲染树 –> 布局渲染树 –> 绘制渲染树。

       DOM树是由HTML文件中的标签排列组成,渲染树是在DOM树中加入CSS(层叠样式表)或HTML中的style样式而形成。渲染树只包含需要显示在页面中的DOM元素。像<head>元素或display属性值为none的元素都不在渲染树中。

    在浏览器还没接收到完整的HTML文件时,它就开始渲染页面了,在遇到外部链入脚本标签或样式标签或图片时,会再次发送HTTP请求重复上述的步骤。在收到CSS文件后会对已经渲染的页面重新渲染,加入它们应有的样式,图片文件加载完立刻显示在相应位置。在这一过程中可能会触发页面的重绘或重排。

     

     

     

    展开全文
  • 你必须知道495个C语言问题(PDF)

    热门讨论 2009-09-15 10:25:47
    2.11 为什么sizeof 返回的值大于结构的期望值, 是不是尾部有填充? . . 9 2.12 如何确定域在结构中的字节偏移? . . . . . . . . . . . . . . . . . 9 2.13 怎样在运行时用名字访问结构中的域? . . . . . . . . . . ...
  • 5.10 但是如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0) 不是更好吗? 5.11 我曾经使用过一个编译器,不使用NULL就不能编译。 5.12 我用预处理宏#define Nullptr(type)(type *)0帮助创建...
  • 3.3.9 一个大含有50M个URL记录,一个小含有500个URL记录,找出两个记录相同URL 3.4.0 海量日志数据,提取出某日访问百度次数最多那个IP 3.4.1 有10个文件,每个文件1G,每个文件每一行都存放是...
  • 易学C++,C++入门

    2009-12-06 14:30:11
     5.3.2 怎么输出的东西更好看   5.4 While循环   5.4.1 当型循环   5.4.2 导火索——do   5.5 方法指导   5.6 习题   第6章 好用“工具”——函数   6.1 简单“工具”——函数   ...
  • 这是一个无参函数,里面只有两个语句,它的作用是使链表初始化,使head的值为NULL和一个清屏语句。比如:没有这个函数的话,在你没有输入任何数据的情况下,去执行显示功能的时候会显示一些乱码! 输入记录函数 ...
  • 大话数据结构

    2018-12-14 16:02:18
    他每次一吃完早饭就冲着去了图书馆,挑一个好地儿,把他书包里的书,一本一本的按座位放好,长长一排,九个座硬是被他占了。 3.4.1顺序存储定义 47 3.4.2顺序存储方式 47 3.4.3数据长度与线性表长度区别 48 ...
  • o 6.8 但是如果 NULL 的值改变了, 比如在使用非零内部空指针的机器上, 难道用 NULL (而不是 0) 不是更好吗? o 6.9 用预定义宏 #define Nullptr(type) (type *)0 帮助创建正确类型的空指针。 o 6.10 这有点奇怪。...
  •  字符型常量所表示的值是字符型变量所能包含的值。我们可以用ASCII表达式来表示一个字符型常量,或者用单引号内加反斜杠表示转义字符。  'A', '\x2f', '\013';  其中:\x表示后面的字符是十六进制数,\0表示后面...
  • 面试题4:设置地址为0x67a9的整型变量的值为0xaa66 面试题5:评论下面这个中断函数 面试题6:评价一个代码片段 第8章 面向对象 8.1 面向对象的基本概念 面试题1:谈谈你对面向对象的认识 面试题2:面向对象的三大...

空空如也

空空如也

1 2
收藏数 37
精华内容 14
关键字:

怎么输出链表里的值