Repo

2021.3.18 16:30 我面试完了
首先,自我介绍
我说,最近我在复习,然后面试官就问我在看什么书,我说我在看《数据结构》
然后他就问,什么数据结构
我就说了一下 线性结构和非线性结构 线性的数组,链表…
然后就被打断了,他问我知道反转链表吗?
我从脑海里依稀记得 课上老师讲过
我说 记得 但不记得具体怎么写了
他就叫我自己想算法来写 打开腾讯投屏
然后我就在草稿纸上想了很久 大概有20分钟
在写的途中 他看到我写了struct 就问了struct 和class的区别
写的时候也有讨论
然后 写完了 还有两三分钟 他就问我最近看的印象最深刻的是什么
我居然脑抽 说TCP 三次握手 四次挥手 (真的印象最深刻,因为还没学《计算机网络》,新名词,当然印象深刻了 SOS)
然后他叫我解释一下 我晕了 我根本就解释不清楚其中的底层原理 泪目
然后 半个小时就要到了 然后他就例行公事问我 “你有没有什么想问的”
我问了一个特别沙雕 但是确实是我想问的问题 就是“能进腾讯的大佬的生活是什么样的?”(好不容易逮着一个机会跟工作的大佬交流)
然后 他笑了 说”这个不属于面试问题“ 然后就说 到这儿了 我说 ”谢谢“ 刚好半个小时…
game over 我还是 好像 没太 准备 好…
好多细节 原理 都没有记住…光速凉凉T-T
挺好的 又多了刷题 看书的时间 (这两天光看书 都没有刷力扣)

前言

先说明一下情况,本人大三,西南交通大学软件工程在读。虽然成绩大约前20%,但不怎么会敲代码,没有项目经历,是个废物(有太多大佬了,我怎么和做过项目的上进本科生和勤奋研究生们比啊…),只会应试学习:《数据结构》82分,《算法分析与设计》75分,《操作系统》89分(因为不理解,已经完全忘记),《编译原理》94分(完全忘记),《数据库原理》93分(忘记)。也许是简历写得还行,或者说是因为有学长内推,总之,在2021年3月11日,我的简历被捞了起来,接到了一个电话,问我什么时候可以面试?

由于在2021年3月10日,也就是前一天,有另一个部门给我打了电话,在电话里大概聊了20分钟,电话那头的温柔姐姐问了我很多非常基础的问题,比如:介绍一下栈和队列、解释一下0-1背包问题,由于本人非常菜以及没有做好准备(很多大佬,优秀的同学在假期就开始准备了,但是我非常贪玩,无法在假期里集中注意力搞计算机之类的,正如我的博客所见,才开始刷了两道力扣,还是看题解才写出来的Orz),所以一问三不知…

所以我给捞起我部门的面试哥哥推到了这周末(3月20日),接完电话后也没有收到面试确认邮件啥的,想着周一开始可以复习,复习五天,于是快乐的度过了一个周末,谁料到,今天(星期一)突然收到短信,说明天下午面试(视频面试的那种)!这咋办,于是我又给申请推到这周四==

我真的太菜了,太贪玩了,所以,现在开始复习,进入二面就算成功吧。(不成功也没办法,找不到工作就争取保研,保不了研就考研,考不上研就去做生意了,挂了不亏,过了血赚)
我决定从数据结构与算法、操作系统两个方面开始复习。

数据结构

线性结构

在数据元素的非空有限集中,存在唯一的一个被称作“第一个”&&“最后一个”的数据元素;均只有一个前驱&&后继

线性表(linear_list)

线性表的顺序表

用一组地址连续的存储单元依次存储线性表的数据元素
Address=base(基地址)+offset(偏移量)
每个元素占l个存储单元:LOC(ai+1)=LOC(ai)+l
通常用数组来表示顺序存储结构;
以物理位置相邻表示逻辑关系,任一元素均可随机存取
&p:取地址
*p:取内容
指针所指内存的两种访问方式
1.指针计算 p+5 L.elem+5
2.数组引用方式 a[5]

线性表的链式表示

一组任意的存储单元

头结点的数据域可以不存储任何信息也可以存储如线性表的长度等类的附加信息
插入元素

void ListInsert_L(LinkList *L,int i,ElemType e){//带头结点的单链线性表,L中第i个位置之前插入元素e
LinkList *p=L;
int j=0;
while(p&&j<i-1){p=p->next;++j}
//寻找第i-1个结点
if(!p||j>i-1)return ERROR;
LinkList *s=(LinkList*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
}

删除运算

ElemType ListDelete_L(LinkList *L,int i){//在带头结点的单链线性表L中,删除第i个元素,并返回其值
LinkList *p=L;j=0;
while(p->next&&j<i-1){//寻找第i个结点,并令其指向前驱
p=p->next;++j;
}
if(!(p->next)||j>i-1)return ERROR;
//删除位置不合理
LinkList *q=p->next;
p->next=q->next;
ElemType e=q->data;
free(q);//删除并释放结点
return e;
}

循环链表:表中最后一个结点的指针域指向头结点,整个链表形成一个环。
双向链表:结点中有两个指针域,其一指向后继,其二指向前驱
d->next->prior=d->prior->next=d

一元多项式的表示及相加

Pn(x)=p0+p1x+p2x2+…+pnxn
p=(p0,p1,p2,…,pn)
Q=(q0,q1,q2,…,qm)
R=(p0+q0,p1+q1,p2+q2,…,pn+qn,qn+1…,qm)

这种结构可能会造成空间浪费,所以更优的结构:
((p0,e0),(p1,e1),(p2,e2),…,(pn,en))
对于两个一元多项式中所有指数相同的项,对应系数相加,若其和不为零,则构成“和多项式”中的一项;
对于两个一元多项式中所有指数不相同的项,则分别复抄到“和多项式”中去。

栈和队列

和线性表类似,栈也有两种存储表示方式。
顺序栈:利用一组地址连续的存储但隐患依次存放自栈底到栈顶的数据元素。

typedef struct {SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int stacksize;//当前分配的栈可使用的最大存储容量
}SqStack

若base的值为NULL,则表明栈结构不存在,称top为栈顶指针,其初值指向栈底,即top=base,可作为栈空标记
应用:迷宫求解 表达式求解(编译原理:归约栈)

队列:先进先出
链队列
循环队列
front(头)和rear(尾)分别指示队列头元素及队列尾元素的位置
将顺序队列臆造为一个环状空间
Q.front=Q.rear无法判断队列空间是“空”还是“满”
可有两种处理方法:
另设一个标志位;
少用一个元素空间,约定以“队列头指针在队列尾指针的下一位置上”作为满的标志

int QueueLength(SqQueue Q){//返回Q的元素个数,即队列的长度
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE
}
void EnQueue(SqQueue *Q,QElemType e){if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;//队列满
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
}

是由零个或多个字符组成的有限序列
串中任意连续的字符组成的子序列成为该串的子串
串的存储结构:
定长顺序存储表示
堆分配存储表示 动态分配 malloc() free()来管理
块链存储表示
串值的存储密度=串值所占的存储位/实际分配的存储位

串的模式匹配算法

普通思想:从主串S的第pos个字符起和模式的第一个字符比较之,若相等,则继续诸葛比较后继字符;否则,从主串的下一个字符起再重新和模式的字符比较之。
以此类推,直至模式T中的每个字符依次和主串S中的一个连续的祖父序列相等,则称匹配成功,否则匹配不成功。

模式匹配的一种改进算法:

问题:当主串中第i个字符与模式中第j个字符“失配”时,主串中第i个字符应与模式中哪个字符再比较?
若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需要和主串中该字符进行比较的字符的位置。
next[j]=0,当j=1时
next[j]=Max, {k|1<k<j且‘p1…pk-1’=‘pj-k+1…pj-1’}
next[j]=1,其他

数组和广义表

数组

二维数组:一个定长线性表,它的每一个数据元素也是一个定长的线性表
次序:一种以列序为主序
一种以行序为主序
二维数组 aij的存储位置:可由下式确定
LOC(i,j)=LOC(0,0)+(b2×i+j)L
矩阵的压缩存储:
为多个值相同的元只分配一个存储空间;对零元不分配空间。
稀疏矩阵:假设在m×m的矩阵中有t个元素不为0,则稀疏因子δ=t/m×n
压缩矩阵的存储结构
三元组顺序表;
行逻辑链接的顺序表;
十字链表(在链表中,每个非零元可用含5个域的结点表示,其中i,j和e这3个域分别表示该非零元所在的行、列和非零元的值,向右域right用以链接同一行中的下一个非零元。同一行的非零元通过right域链接成一个线性链表)

广义表

广义表是线性表的推广
当广义表LS非空时,称第一个元素a1为LS的表头,其余元素组成的表(a2,a3,…,an)是LS的表尾。
表头可以是原子,也可以是子表
表尾一定是一个子表
若列表不空,则可分解成表头和表尾;反之,一对确定的表头和表尾可唯一确定列表。

非线性数据结构

树和二叉树

树的结点包含一个数据元素及若干指向其子树的分支,结点拥有的子树成为结点的度。

二叉树

每个结点至多有两棵子树
1.在二叉树的第i层上至多有2i-1个结点
2.深度为k的二叉树至多有2k-1个结点(k>=1)
3.对任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
满二叉树:一颗深度为k且有2k-1个结点的二叉树称为满二叉树。
深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。
完全二叉树的性质:
叶子结点只可能在层次最大的两层上出现。
对任一结点,若其右分支下的子孙的最大层次为l,则其左分支下的子孙的最大层次必为l或l+1
4.具有n个结点的完全二叉树的深度为[log2n]+1
5.如果对一棵有n个结点的完全二叉树(其深度为[log2n+1])的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),则对任一结点i(l<=i<=[log2n]+1)有
(1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点[i/2]
(2)如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子LCHILD(i)的结点2i
(3)如果2i+1>n,则结点i无右孩子;否则其右孩子RCHILD(i)是结点2i+1
遍历二叉树
如果按某搜索路径巡访树中每一个结点,使得每个结点均被访问一次,而且仅被访问一次。因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上。

先序遍历二叉树 前缀表示(波兰式)

访问根节点;
先序遍历左子树;
先序遍历右子树;

void PreOrderTraverse(BiTree T, status (*visit)(TElemType){//采用二叉链表存储结构,visit是对数据元素操作的应用函数
//先序遍历二叉树T的递归算法,对每个数据元素调用函数visit
if(T){if(visit(T->data)if(PreOrderTraverse(T->lchild,Visit))if(PreOrderTraverse(T->rchild,Visit))return OK;return ERROR;}else return OK;}
}
中序遍历二叉树

中序遍历左子树;
访问根节点;
中序遍历右子树;

后序遍历二叉树 后缀遍历(逆波兰式)

后序遍历左子树
后序遍历右子树
访问根节点

线索二叉树

若结点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱;若结点有右子树,则其rchild域指示其右孩子,否则令rchild域指示其后继

树的存储结构

1.双亲表示法:附设一个指示器,指示其双亲结点在链表中的位置
2.孩子表示法:同构的,空间较浪费;异构的,不方便
3.孩子兄弟表示法:链表中结点的两个链域分别指向该结点的第一个孩子结点和下一个兄弟结点

赫夫曼树

最优二叉树
结点的带权路径长度为从该结点到树根之间的路径长度与结点上权的乘积
WPL=Σnk=1wklk
(1)根据给定的n个权值构成n棵二叉树的集合,其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树均为空
(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在F中删除这两棵树,同时将新得到的二叉树加入F中
(4)重复(2)和(3)直到F只含一棵树为止
赫夫曼编码
让电文中出现次数较多的字符采用尽可能短的编码
正则二叉树:没有度为1的结点;
一棵有n个叶子结点的赫夫曼树共有2n-1个结点

图的存储结构

1.数组表示法
2.邻接表
3.十字链表

图的遍历

深度优先搜索:依次从v的未被访问的邻接点出发
广度优先搜索:使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。

周一晚上面试题练习

【网上找的,答案也是网上找的,侵删】
1、struct与class的区别
因为struct是一种数据类型,那么就肯定不能定义函数,所以在面向c的过程中,struct不能包含任何函数。否则编译器会报错。
面向过程的编程认为,数据和数据操作是分开的。然而当struct进入面向对象的c++时,其特性也有了新发展,就拿上面的错误函数来说,在c++中就能运行,因为在c++中认为数据和数据对象是一个整体,不应该分开,这就是struct在c和c++两个时代的差别。
在C++中struct得到了很大的扩充:
1.struct可以包括成员函数
2.struct可以实现继承
3.struct可以实现多态
区别
1.默认的继承访问权。class默认的是private,strcut默认的是public。
所以我们在写类的时候都会显示的写出是公有继承还是私有继承
到底默认是public继承还是private继承,取决于子类而不是基类。
2.默认访问权限:struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
3.“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数

从上面的区别,我们可以看出,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。

2、c++的标准
C++11 核心语言功能表:并发
C与C++不兼容:
1.最常见的差异之一是,C 允许从 void * 隐式转换到其他指针类型,但C++不允许。
2.另一个常见的可移植问题是,C++重新定义了关键字,如 new, class,它们在C程序中可以作为识别字(例:变量名)的。
3.在C标准(C99)中去除了一些不兼容之处,也支持了一些C++的特性,如注解,以及在代码中混合声明。不过C99也纳入了几个和C++冲突的特性(如:可变长度数组、原生复数类型和复合逐字常数)。
若要混用C和C++的代码,则所有在C++中调用的C代码,必须放在 extern “C”{/C代码/}内。

3、C++的多态
C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
1:用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
2:存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
3:多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
4:多态用虚函数来实现,结合动态绑定.
5:纯虚函数是虚函数再加上 = 0;
6:抽象类是指包括至少一个纯虚函数的类。
纯虚函数:virtual void fun()=0;即抽象类!必须在子类实现这个函数,即先有名称,没有内容,在派生类实现内容。
编译器在编译的时候,发现Father类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表(即 vtable),该表是一个一维数组,在这个数组中存放每个虚函数的地址。
那么如何定位虚表呢?编译器另外还为每个对象提供了一个虚表指针(即vptr),这个指针指向了对象所属类的虚表,在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向了所属类的虚表,从而在调用虚函数的时候,能够找到正确的函数。
在构造函数中进行虚表的创建和虚表指针的初始化,在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否还有继承者,它初始化父类对象的虚表指针,该虚表指针指向父类的虚表,当执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。

4、内存分布(堆、栈、静态/全局/局部变量、虚指针…)
在c中分为这几个存储区
1.栈 - 由编译器自动分配释放
2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
4.另外还有一个专门放常量的地方。- 程序结束释放
5.程序代码区,存放2进制代码。
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)
和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。
栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而堆是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。

5、写一个void memcpy(const void* pSrc, void * pDest, size_t length)
版本一的memcpy实现的是char到char的拷贝的循环,效率可能不高,实际上,标准库中的memcpy是一个效率很高的内存拷贝函数,他不会逐个字节的copy,在地址不对齐的情况下,他是逐字节copy,地址对齐后,就会使用CPU字长来copy,(32bits或64bits),此外还会根据cpu的类型来选择一些优化的指令来进行拷贝。总的来说,memcpy的实现是和CPU类型、操作系统、cLib相关的。
下面实现CPU字长为4个字节,对齐状态按32bits(4个字节)来copy


void *Upgrade_memcpy(void *pDest,const void *pSrc,size_t n)
{assert((pDest!=NULL)&&(pSrc!=NULL));int wordnum = n/4;            //计算有多少个32位,按4字节拷贝int slice = n%4;              //剩余的按字节拷贝int * pIntsrc = (int *)pSrc;int * pIntdest = (int *)pDest;while(wordnum--)*pIntdest++ = *pIntsrc++;while (slice--)*((char *)pIntdest++) =*((char *)pIntsrc++);return pDest;
}

6、STL
STL 是“Standard Template Library”的缩写,中文译为“标准模板库”。STL 是 C++ 标准库的一部分,不用单独安装。
C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。

发现了一个好东西,牛客网面试宝典==,好多问题都有解答。
以上2021.3.16 1:26 睡一觉再继续看书
我来更新了,昨天2021.3.17号,课太多了,上午满课,下午有一讲,还是看了点数据结构,记录在本子上了,没有打字打上来。而且凌晨1:30看完书后,居然失眠到了三点钟,然后整个人都感觉特别累,产生了一种搞完了面试就要睡一大觉的强烈欲望,就是非常非常困…稍微有点不正常(原来熬夜是不会产生这种情况的),然后今天早上第一讲课,在看面试题的时候,发现我来大姨妈了,还把裤子打脏了,于是向老师请了假,回宿舍,吃芬必得,本来想看题的,结果一不小心睡到了11:40,我好废物啊…
不过还是要相信自己,继续做准备,昨天看到一句话,就是说:“要用六分的能力做十分的事情。”
虽然由于我的贪玩,不勤奋,我只有一分的能力,但是在搞这些事情的时候还是有收获的,真不错Orz。
希望别给我的学校丢脸啊…我的学校有超级多努力上进的同学…希望不要因为我给面试官留下不好的印象嗷嗷嗷嗷~~~~

查找

顺序查找

从表中最后一个记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值的比较相等,则查找成功,找到所查记录;反之,若直至第一个记录,其关键字和给定值比较都不等,则查找不成功。

有序表的查找

折半查找

静态查找时,Searcg函数可用折半查找来实现:
(1)假设指针low和high分别指示待查元素所在范围的下界和上界,指针mid指示区间的中间位置mid=(low+high)/2
(2)if(ST.elem[mid].key>key)说明待查元素若存在,则必在[low,mid-1]的范围内
if(ST.elem[mid].key<key) 若存在则必在区间[mid+1,high]内
若low>high,则说明表中没有关键字等于key的元素,查找不成功

索引顺序表查找

除表本身以外需要建立一个”索引表“,其中包含两项:关键字(其值为该子表内的最大关键字)和指针项(指示该子表的第一个记录在表中位置)
索引表按关键字有序,则表或者有序,或者分块有序。
所谓”分块有序“指第二个子表中所有记录的关键字均大于第一个子表中的最大关键字,第三个子表中的所有关键字均大于第二个表中的最大关键字。
因此,分块查找过程分两步进行,先确定待查记录所在的块(子表)然后在块中顺序查找。

动态查找表

若在查找过程中同时插入查找表中不存在的数据元素,或者查找表中删除已存在的某个数据元素,则称此类表为动态查找表。

二叉排序树

一棵空树或者具有以下性质:
(1)若它的左子树不空,则在左子树上所有结点的值均小于它的根结点的值
(2)若它的右子树不空,则右子树上所有结点的值,均大于它的根结点的值
(3)它的左、右子树也分别为二叉排序树
中序遍历二叉排序树可得到一个关键字的有序序列。
一个无序序列可用通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。

void SearchBST(BiTree T,KeyType key,BiTree f,BiTree &p){//在根指针T所指二叉排序树中递归地查找其关键字等于key的数据元素。若查找成功则指针p指向该数据元素结点,并返回TRUE,否则指针p指向查找路径上访问的最后一个结点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL
if(!T){p=f;return FALSE;}//查找不成功
else if(key==T->data.key){p=T;return TRUE;}//查找成功
else if(key<T->data.key)return SearchBST(T->lchild,key,T,p);
else return SearchBST(T->rchild,key,T,p);
}
void InsertBST(BiTree &T,ElemType e){//当二叉排序树中不存在关键字e.key的数据元素时,插入e并返回TRUE
if(!SearchBST(T,e.key,NULL,p){s=(BiTree)malloc(sizeof(BiTNode));
s->data=e;
s->lchild=s->rchild=NULL;
if(!p)T=s;//被插结点*s为新的根结点
else if(e.key<p->data.key) p->lchild=s;
else p->rchild=s;
return True;
}
else return FALSE;
}

平衡二叉树

AVL树 左子树和右子树的深度之差的绝对值不超过1

B树

也叫B-树,主要用于文件索引
能够存储数据,对数据进行排序并允许以O(logn)的时间复杂度运行进行查找,顺序读取插入和删除的数据结构
概括来说是一个结点可以拥有多于2个结点的二叉查找树
一个m阶的B树具有如下特点
B树根结点至少有两个结点,每个结点可以有多个子树,每个中间结点都包含k-1个元素和k个子树
所有的叶子结点都位于同一层‘每个结点中的元素从小到大排列、结点当中k-1个元素正好是k个孩子包含的元素的值域的划分。

B+树

B+树比B树更适合数据库索引
B+树是对B树的一种变形树
1.有k个叶子结点的结点必然有k个关键码
2.非叶子结点仅具有索引作用,跟记录有关的信息均存放在叶结点中
3.树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录

B+树与 B树比较

B+树的优点:
1.由于B+树在内部结点上不含数据信息,因此在内存页中能够存放更多的key,数据存放发更紧密,具有更好的空间局部性
2.B+树的叶子结点都是相连的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。
B树的优点:
由于B树的每一个结点都包含key和value,因此,经常访问的元素可能离根结点更近,因此访问也更加迅速。

红黑树

也叫RB树,不严格控制左右子树高度或结点数之差小于1
1.结点是红色或者黑色
2.根结点是黑色
3.每个叶子结点都是黑色空结点
4.每个红色结点的两个子节点都是黑色,也就是说,从每个叶子到根的所有路径上不能有两个连续的红色结点
5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点

哈希表

理想的情况是希望不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。
这个对应关系f称为哈希函数。
1.哈希函数是一个映像
2.对不同的关键字可能得到同一哈希地址
均匀哈希函数:经哈希函数映像到地址集合中,任何一个地址的概率是相等的。
1.直接定址法
2.数字分析法
取关键字的若干数位组成哈希地址。
3.平方取中法
取到关键字平方后的中间几位为哈希地址。
4.折叠法
将关键字分割成位数相同的几部分,然后取这几部分的叠加和作为哈希地址。
5.除留余数法
被某个不大于哈希函数表表厂m的数p除后所得的余数为哈希地址。
冲突处理的方法:
(1)开放定址法
用线性探测再散列的方法,直到找到空位置。
(2)再哈希法
(3)链地址法
(4)建立一个公共溢出区

排序

内部排序
在排序前Ri领先于Rj,其关键字相等,若在排序后的序列中Ri仍领先于Rj,则称所用的排序方法是稳定的;反之,若可能使排序后的序列Rj领先于Ri,则称所用的排序方法是不稳定的。

插入排序

O(n2)

折半插入排序

利用折半查找来实现

希尔排序

缩小增量排序
将整个待排记录序列分割成若干子序列,分别进行直接的插入排序
待整个序列中记录”基本有序“时,再对全体记录进行一次直接的插入排序,先将该序列分为5个子序列{R1,R6},{R2,R7}…
分别对每个子序列进行插入排序
然后进行第二趟希尔排序,对{R1,R4,R7,R10}进行直接的插入排序

冒泡排序

使最大的记录被安置到最后一个记录位置上,进行第二趟排序…
总时间复杂度为O(n2)

快速排序

通过一趟排序将待排记录分割成独立两部分,其中一部分记录的关键字均比另一部分关键字小。
假设待排序的序列为{r1,r2,r3,…,rt}
首先任意选取一个记录作为枢纽pivot
附设两个指针low和high
首先从high所指的位置起向前搜索找到第一个关键字小于pivot的记录和枢轴记录,互相交换,
然后从low所指位置向后搜索,找到第一个关键字大于pivot的记录和枢轴交换

改进的快速排序

没必要和枢轴交换 high和low交换就可以了。

int Partition(Sqlist &L,int low,int high){L.r[0]=L.r[low];
pivot=L.r[low].key;
while(low<high){while(low<high&&L.r[high].key>=pivot)--high;
L.r[low]=L.r[high];
while(low<high&&L.r[low].key<=pivot)++low;
L.r[high]=L.r[low];
}
L.r[low]=L.r[0];
return low;
}void QSort(Sqlist &L,int low,int high){//对顺序表L中的子序列L.r[low...high]作快速排序
if(low<high){pivot=Partition(L,low,high);
QSort(L,low,pivot-1);
QSort(L,pivot+1,high);
}
}

堆排序

小顶堆:
ki<=k2i
ki<=k2i+1
大顶堆:
ki>=k2i
ki>=k2i+1
顶堆必为序列中n个元素的最小值(或最大值)
实现堆排序需要解决两个问题:
(1)如何由一个无序序列建成一个堆?
(2)如何在输出顶堆元素之后,调整剩余元素成为一个新的堆
首先解决第二个问题:
仅需自上至下进行调整
首先,以顶堆元素和其左、右子树根结点的值比较之,
如果右子树根结点值小于左子树根结点的值,且小于根结点的值,交换之
对右子树进行上述相同调整
这就是一个”筛选“过程
从一个无序序列建堆的过程就是一个反复“筛选”的过程。
其最坏时间复杂度为O(nlogn)

归并排序

假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2或n/2+1个长度为2或1的有序子序列;在两两归并,如此重复。
其时间复杂度为O(nlogn)

算法设计

全排列

int main()
{int list[5]={1,2,3,4,5};Perm(list,0,4);  return 0;
}void Perm(int *list, int k, int m)
{int i;if(k==m){for(i=0;i<=m;i++)printf("%d ",list[i]);      printf("\n");}else{for(i=k;i<=m;i++){Swap(list[k],list[i]);Perm(list,k+1,m);Swap(list[k],list[i]);}}return;
}void Swap(int &i, int &j)
{int temp;temp=i;i=j;j=temp;return;
}

动态规划

在用分治法求解问题时,有些子问题被重复计算了许多次。如果能够保存已经解决的子问题的答案,在需要时再找出已经求得的答案,就可以避免大量重复的计算,从而得到多项式时间算的。

为了达到此目的,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将结果填入表中。

动态规划算法的基本要素:
1.最优子结构
2.重叠子问题

0-1背包问题:

给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为c,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
不能将物品i装入背包多次,也不能只装入部分的物品i
这是一个特殊的整数规划问题。
MAX{找W-wi的容量时的最大价值+vi,不放入这个物品i的时候的价值}

回溯法与分支限界法

回溯法按照“深度优先”策略,从根结点出发,搜索解空间树,算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的搜索。

分支限界法常以广度优先或以最小耗费(最大收益)的优先方式搜索问题的解空间。在分支限界中,每个活结点只有一次机会成为拓展结点,活结点一旦成为扩展结点,就一次性产生其所有儿子结点。

操作系统

2021.3.17 22:44 看书中…
23:30停电,不一定发上来。
明天就要面试了,可能面试完再来填,如果面试完就不想看了就不填了。

周三晚上的面试题练习

1、TCP三次握手 四次挥手 UDP
TCP/IP协议是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。
TCP用于应用程序之间的通信
IP负责计算之间的通信
TCP负责把数据分割并装入IP包,然后他们到达的时候重新组合他们。
IP负责将包发送至接收者。
第一次握手:
客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
第二次握手:
服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。
第三次握手.
客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

TCP协议是有连接的,有连接的意思是开始传输实际数据之前TCP的客户端和服务器端必须通过三次握手建立连接,会话结束之后也要结束连接。而UDP是无连接的
TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到。
TCP协议所需资源多,TCP首部需20个字节(不算可选项),UDP首部字段只需8个字节。
TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
TCP是一对一的连接,而UDP则可以支持一对一,多对多,一对多的通信。
TCP面向的是字节流的服务,UDP面向的是报文的服务。

2、LRU
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

3、HashMap实现原理
HashMap的实现原理:. 利用key的hashCode重新hash计算出当前对象的元素在数组中的下标. 存储时,如果出现hash值相同的key,此时有两种情况。. (1)如果key相同,则覆盖原始值; (2)如果key不同(出现冲突),则将当前的key-value放入链表中. 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。. 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。.

4、网络粘包、少包怎么处理
粘包是对于TCP来说的,UDP是不存在粘包一说的,那么TCP在传输数据的过程的特点是什么呢:
1 会将数据量较小,且发送时间间隔较短的的数据一起打包发送,那么这里所讲的时间较短是相比较网络延迟来说的,
比如我们两次发送间隔为0.00001秒,那么网络延迟为0.001,这个时候两次的数据就会打包发送,这是一种优化机制。
2 TCP协议发送数据时,是源源不断的发送,像水流一样,因此TCP又叫流式协议。
我们知道服务端在接收消息时是有一个最大限制的=====>conn.recv(1024),1024表示1024个bytes。那么如果我们一次传输的数据超过了1024bytes,剩余的数据会存在我们接收端计算机操作系统缓存中,也就是说,接收方并不知道发送方传输了多少数据,所以这个时候问题就出现了,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时:
先收报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

第一次暑假实习面试,进入二面就算成功(没有成功)相关推荐

  1. 2021年4月19日 深圳头条后台开发实习面试(二面)(含总结)

    title: 2021年4月19日 深圳头条后台开发实习面试(二面) tags: 面经 2021年4月19日 深圳头条后台开发实习面试(二面) 自我介绍 面试直接跟我讲你想让我问哪个方面的问题呢?(这 ...

  2. 2021年4月16日 阿里供应链Java研发实习面试(二面)

    title: 2021年4月16日 阿里供应链Java研发实习面试(二面) tags: 面经 2021年4月16日 阿里供应链Java研发实习面试(二面)(70min) 自我介绍 介绍自己的研究生项目 ...

  3. 数据暑假实习面试_面试数据科学实习如何准备

    数据暑假实习面试 Unfortunately, on this occasion, your application was not successful, and we have appointed ...

  4. 暑假实习面试总结——Go语言|Golang

    目录 前景 一面HR 二面技术面 终面 总结 前景 学Go语言也有大半年了,就想着大二暑期找个实习 面试了一家学校附近的公司,记录一下我的第一次面试过程 一面HR 自我介绍. 我是xxx,来自xxxx ...

  5. 从简历被拒,到 Android 面试。二本如何在 359 天成功拿下 offer

    免费福利 此份文档详细记录了千道面试题与详解:  !     私信我回复[03]即可免费获取 二.面试背景 自从去年被头条笔试的算法凌辱和被头条的前辈指教了之后对头条没啥好感了,也感觉进不去,所以后面 ...

  6. 2018华为暑假实习面试

    非科班想找软件岗位,参加了华为IT应用软件开发实习生的招聘. 华为招聘实习生,在学校总共举办了四场实习生招聘宣讲会,参加宣讲会很有好处,可以了解各个部门实际的工作内容,也可找他们了解招聘需要准备哪方面 ...

  7. 头条后台实习面试(二面挂)

    因为大四保完研以后没什么事,想找份实习磨炼一下自己,亦或是赚点外快.于是在同学的介绍下,拿到了头条后台开发实习的内推.但因为底子不稳,信念不坚止步二面,在此记录以自省. 先是接到hr电话,声音很好听, ...

  8. 2021年3月15日 腾讯PCG运营开发实习面试(一、二、三面)(面经含总结)

    title: 2021年3月15日 腾讯PCG运营开发实习面试(一.二.三面) tags: 面经 2021年3月15日 腾讯PCG运营开发实习面试(一面2h) 自我介绍你能讲一下Java中的集合,你都 ...

  9. 自己一路经历的实习面试总结(希望能给看到这篇博客的小伙伴提供一些帮助)

    自己一路经历的实习面试总结(希望能给看到这篇博客的小伙伴提供一些帮助) 找实习这一路走来,已经到了快要结束的时候啦,这一路,面试了很多家公司,经过许许多多的坎坎坷坷,但也是非常感谢自己有这样一段经历的 ...

最新文章

  1. CentOS下SVN服务的启动与关闭
  2. 波卡链Substrate (7)Babe协议六“Secondary slot leader”
  3. 【Linux】一步一步学Linux——who命令(95)
  4. CSS之background-position属性
  5. phpstudy中的mysql
  6. Nginx(五):动静分离
  7. 用国产还是国外?BI工具深度盘点洞察,选这个肯定没差
  8. 杭电oj 1000 c++ 版本
  9. AxWebBrowser打开本地文件
  10. ansible vim_Vim插件,使用Ansible自动化部署策略,Pelican入门,Linux,Joplin,Python以及更多新鲜读物
  11. 布林通道参数用20还是26_这样设置均线参数
  12. jsp页面struts2标签展示clob类型的数据
  13. 如何测试WiFi路由器小包性能
  14. 基于PCQQ协议的Python QQ机器人库
  15. 魔方教程公式口诀七步「视频教程」
  16. 政府12345便民服务热线 -- MICC全媒体智能呼叫中心建设方案
  17. 洛谷P2006 赵神牛的游戏
  18. 用html实现模糊效果,纯html css实现动态高斯模糊效果(第二弹),实现导航条动态模糊内容...
  19. Spring Boot Redis关闭
  20. Pikachu靶场练习

热门文章

  1. Embedded Linux S3C2440 Profiling
  2. 2020年最新手机处理器对比(附手机CPU天梯图)
  3. html input datetime格式化及后端接收
  4. 学习discovery studio对对接结果进行分析
  5. 《东周列国志》第三十九回 柳下惠授词却敌 晋文公伐卫破曹
  6. 在MTK平台里,,函数kal_prompt_trace起什么作用???Kal_prompt_trace的参数有表示什么?...
  7. 计算机网络技术中,分组交换技术在计算机网络技术中的作用及特点是什么?
  8. vue练手小项目--眼镜在线试戴
  9. Imagination 推出最先进的光线追踪图形处理器(GPU)
  10. V-Appstore 9Apps(Android)