24数据结构与算法面试题21题.docx
- 文档编号:6303574
- 上传时间:2023-01-05
- 格式:DOCX
- 页数:30
- 大小:3.88MB
24数据结构与算法面试题21题.docx
《24数据结构与算法面试题21题.docx》由会员分享,可在线阅读,更多相关《24数据结构与算法面试题21题.docx(30页珍藏版)》请在冰豆网上搜索。
24数据结构与算法面试题21题
数据结构面试题
1、栈(stack)
栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶(top)。
它是后进先出(LIFO)的。
对栈的基本操作只有push(进栈)和pop(出栈)两种,前者相当于插入,后者相当于删除最后的元素。
2、队列(queue)
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端
(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
进行插入操作的端称为队尾,进行删除操作的端称为队头。
3、链表(Link)
链表是一种数据结构,和数组同级。
比如,Java中我们使用的ArrayList,其实现原理是数组。
而LinkedList的实现原理就是链表了。
链表在进行循环遍历时效率不高,但是插入和删除时优势明显。
4、散列表(HashTable)
散列表(Hashtable,也叫哈希表)是一种查找算法,与链表、树等算法不同的是,散列表算法在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据元素)的比较操作。
散列表算法希望能尽量做到不经过任何比较,通过一次存取就能得到所查找的数据元素,因而必须要在数据元素的存储位置和它的关键字(可用key表示)之间建立一个确定的对应关系,使每个关键字和散列表中一个唯一的存储位置相对应。
因此在查找时,只要根据这个对应关系找到给定关键字在散列表中的位置即可。
这种对应关系被称为散列函数(可用h(key)表示)。
用的构造散列函数的方法有:
1)直接定址法:
取关键字或关键字的某个线性函数值为散列地址。
即:
h(key)=key或h(key)=a*key+b,其中a和b为常数。
(2)数字分析法
(3)平方取值法:
取关键字平方后的中间几位为散列地址。
(4)折叠法:
将关键字分割成位数相同的几部分,然后取这几部分的叠加和作为散列地址。
(5)除留余数法:
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址,即:
h(key)=keyMODpp≤m
(6)随机数法:
选择一个随机函数,取关键字的随机函数值为它的散列地址,即:
h(key)=random(key)
5、排序二叉树
首先如果普通二叉树每个节点满足:
左子树所有节点值小于它的根节点值,且右子树所有节点值大于它的根节点值,则这样的二叉树就是排序二叉树。
插入操作
首先要从根节点开始往下找到自己要插入的位置(即新节点的父节点);具体流程是:
新节点与当前节点比较,如果相同则表示已经存在且不能再重复插入;如果小于当前节点,则到左子树中寻找,如果左子树为空则当前节点为要找的父节点,新节点插入到当前节点的左子树即可;如果大于当前节点,则到右子树中寻找,如果右子树为空则当前节点为要找的父节点,新节点插入到当前节点的右子树即可。
删除操作
删除操作主要分为三种情况,即要删除的节点无子节点,要删除的节点只有一个子节点,要删除的节点有两个子节点。
1.对于要删除的节点无子节点可以直接删除,即让其父节点将该子节点置空即可。
2.对于要删除的节点只有一个子节点,则替换要删除的节点为其子节点。
3.对于要删除的节点有两个子节点,则首先找该节点的替换节点(即右子树中最小的节点),接着替换要删除的节点为替换节点,然后删除替换节点。
查询操作
查找操作的主要流程为:
先和根节点比较,如果相同就返回,如果小于根节点则到左子树中归查找,如果大于根节点则到右子树中递归查找。
因此在排序二叉树中可以很容易获取最大(最右最深子节点)和最小(最左最深子节点)值
6、前缀树
前缀树(PrefixTrees或者Trie)与树类似,用于处理字符串相关的问题时非常高效。
它可以实现快速检索,常用于字典中的单词查询,搜索引擎的自动补全甚至IP路由。
下图展示了“top”,“thus”和“their”三个单词在前缀树中如何存储的:
7、红黑树
红黑树的特性
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。
[注意:
这里叶子节点,是指为空(NIL或NULL)的叶子节点!
]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
左旋
对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将x变成了一个左节点(x成了为z的左孩子)!
。
因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
右旋
对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将x变成了一个右节点(x成了为y的右孩子)!
因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。
添加
第一步:
将红黑树当作一颗二叉查找树,将节点插入。
第二步:
将插入的节点着色为"红色"。
根据被插入节点的父节点的情况,可以将"当节点z被着色为红色节点,并插入二叉树"划分为三种情况来处理。
①情况说明:
被插入的节点是根节点。
处理方法:
直接把此节点涂为黑色。
②情况说明:
被插入的节点的父节点是黑色。
处理方法:
什么也不需要做。
节点被插入后,仍然是红黑树。
③情况说明:
被插入的节点的父节点是红色。
这种情况下,被插入节点是一定存在非空祖父节点的;进一步的讲,被插入节点也一定存在叔叔节点(即使叔叔节点为空,我们也视之为存在,空节点本身就是黑色节点)。
理解这点之后,我们依据"叔叔节点的情况",将这种情况进一步划分为3种情况(Case)
第三步:
通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
删除
第一步:
将红黑树当作一颗二叉查找树,将节点删除。
这和"删除常规二叉查找树中删除节点的方法是一样的"。
分3种情况:
①被删除节点没有儿子,即为叶节点。
那么,直接将该节点删除就OK了。
②被删除节点只有一个儿子。
那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③被删除节点有两个儿子。
那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。
第二步:
通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。
所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
选择重着色3种情况。
①情况说明:
x是“红+黑”节点。
处理方法:
直接把x设为黑色,结束。
此时红黑树性质全部恢复。
②情况说明:
x是“黑+黑”节点,且x是根。
处理方法:
什么都不做,结束。
此时红黑树性质全部恢复。
③情况说明:
x是“黑+黑”节点,且x不是根。
处理方法:
这种情况又可以划分为4种子情况。
这4种子情况如下表所示:
参考:
代码实现:
8、B-TREE
B-tree又叫平衡多路查找树。
一棵m阶的B-tree(m叉树)的特性如下(其中ceil(x)是一个取上限的函数):
1.树中每个结点至多有m个孩子;
2.除根结点和叶子结点外,其它每个结点至少有有ceil(m/2)个孩子;
3.若根结点不是叶子结点,则至少有2个孩子(特殊情况:
没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);
4.所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部结点或查询失败的结点,实际上这些结点不存在,指向这些结点的指针都为null);
5.每个非终端结点中包含有n个关键字信息:
(n,P0,K1,P1,K2,P2,,Kn,Pn)。
其
中:
a)Ki(i=1.n)为关键字,且关键字按顺序排序K(i-1) b)Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。 c)关键字的个数n必须满足: ceil(m/2)-1<=n<=m-1。 一棵m阶的B+tree和m阶的B-tree的差异在于: 1.有n棵子树的结点中含有n个关键字;(B-tree是n棵子树有n-1个关键字) 2.所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (B-tree的叶子节点并没有包括全部需要查找的信息) 3.所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (B-tree的非终节点也包含需要查找的有效信息) 参考: 9、位图 位图的原理就是用一个bit来标识一个数字是否存在,采用一个bit来存储一个数据,所以这样可以大大的节省空间。 bitmap是很常用的数据结构,比如用于BloomFilter中;用于无重复整数的排序等等。 bitmap通常基于数组来实现,数组中每个元素可以看成是一系列二进制数,所有元素组成更大的二进制集合。 算法面试题 1、数据里有{1,2,3,4,5,6,7,8,9},请随机打乱顺序,生成一个新的数组(请以代码实现) 2、写出代码判断一个整数是不是2的阶次方(请代码实现,谢绝调用API方法) 3、假设今日是2015年3月1日,星期日,请算出13个月零6天后是星期几,距离现在多少天(请用代码实现,谢绝调用API方法) 4、有两个篮子,分别为A和B,篮子A里装有鸡蛋,篮子B里装有苹果,请用面向对象的思想实现两个篮子里的物品交换(请用代码实现) B.show(); } } classBasket{ publicStringname;//篮子名称privateGoodsgoods;//篮子中所装物品publicBasket(Stringname){ //TODOAuto-generatedconstructorstub this.name=name;System.out.println(name+"篮子被创建"); } //装物品函数 publicvoidload(Stringname){goods=newGoods(name); System.out.println(this.name+"装载了"+name+"物品"); } publicvoidchange(BasketB){ System.out.println(this.name+"和"+B.name+"中的物品发生了交换");Stringtmp=this.goods.getName();this.goods.setName(B.goods.getName()); B.goods.setName(tmp); } publicvoidshow(){ System.out.println(this.name+"中有"+goods.getName()+"物品"); } } classGoods{ privateStringname;//物品名称publicStringgetName(){ returnname; } publicvoidsetName(Stringname){this.name=name; } publicGoods(Stringname){ //TODOAuto-generatedconstructorstubthis.name=name; } } 5、二分查找 又叫折半查找,要求待查找的序列有序。 每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程。 直到查找到了为止,否则序列中没有待查的关键字。 6、冒泡排序算法 1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。 (2)这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。 (3)N=N-1,如果N不为0就重复前面二步,否则排序完成。 7、插入排序算法 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。 插入排序非常类似于整扑克牌。 在开始摸牌时,左手是空的,牌面朝下放在桌上。 接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。 为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。 无论什么时候,左手中的牌都是排好序的。 如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。 如果输入数组是逆序排列的,将出现最坏情况。 平均情况与最坏情况一样,其时间代价是(n2)。 8、快速排序算法 快速排序的原理: 选择一个关键值作为基准值。 比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。 一般选择序列的第一个元素。 一次循环: 从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。 找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。 直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值来说,左右两边就是有序的了。 9、希尔排序算法 基本思想: 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。 1.操作方法: 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1; 2.按增量序列个数k,对序列进行k趟排序; 3.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。 仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。 10、归并排序算法 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。 然后再把有序子序列合并为整体有序序列。 11、桶排序算法 桶排序的基本思想是: 把数组arr划分为n个大小相同子区间(桶),每个子区间各自排序,最后合并 。 计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。 1.找出待排序数组中的最大值max、最小值min 2.我们使用动态数组ArrayList作为桶,桶里放的元素也用ArrayList存储。 桶的数量为 (maxmin)/arr.length+1 3.遍历数组arr,计算每个元素arr[i]放的桶 4.每个桶各自排序 12、基数排序算法 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。 然后,从最低位开始,依次进行一次排序。 这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。 13、剪枝算法 在搜索算法中优化中,剪枝,就是通过某种判断,避免一些不必要的遍历过程,形象的说,就是剪去了搜索树中的某些“枝条”,故称剪枝。 应用剪枝优化的核心问题是设计剪枝判断方法,即确定哪些枝条应当舍弃,哪些枝条应当保留的方法。 14、回溯算法 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。 15、最短路径算法 从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。 解决最短路的问题有以下算法,Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法等 16、最小生成树算法 现在假设有一个很实际的问题: 我们要在n个城市中建立一个通信网络,则连通这n个城市需要布置 n-1一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网? 于是我们就可以引入连通图来解决我们遇到的问题,n个城市就是图上的n个顶点,然后,边表示两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有n个顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。 构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质: MST性质(假设N=(V, {E})是一个连通网,U是顶点集V的一个非空子集,如果(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必定存在一颗包含边(u,v)的最小生成树),下面就介绍两种使用MST性质生成最小生成树的算法: 普里姆算法和克鲁斯卡尔算法。 17、AES 高级加密标准(AES,AdvancedEncryptionStandard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。 对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图: 18、RSA RSA加密算法是一种典型的非对称加密算法,它基于大数的因式分解数学难题,它也是应用最广泛的非对称加密算法。 非对称加密是通过两个密钥(公钥-私钥)来实现对数据的加密和解密的。 公钥用于加密,私钥用于解密。 19、CRC 循环冗余校验(CyclicRedundancyCheck,CRC)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。 它是利用除法及余数的原理来作错误侦测的。 20、MD5 MD5常常作为文件的签名出现,我们在下载文件的时候,常常会看到文件页面上附带一个扩展名 为.MD5的文本或者一行字符,这行字符就是就是把整个文件当作原数据通过MD5计算后的值,我们下载文件后,可以用检查文件MD5信息的软件对下载到的文件在进行一次计算。 两次结果对比就可以确保下载到文件的准确性。 另一种常见用途就是网站敏感信息加密,比如用户名密码,支付签名等等。 随着https技术的普及,现在的网站广泛采用前台明文传输到后台,MD5加密(使用偏移量)的方式保护敏感数据保护站点和数据安全。 21、更多算法练习 更多算法练习题,请访问https: //leetcode-
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 24 数据结构 算法 试题 21