热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构面试集锦

数据结构常用查找算法?具体实现常用排序算法?具体实现,哪些是稳定的,时间复杂度、空间复杂度,快速排序非递归如何
  1. 数据结构
      1. 常用查找算法?具体实现
      2. 常用排序算法?具体实现,哪些是稳定的,时间复杂度、空间复杂度,快速排序非递归如何实现?快排的优势?
      3. 图的常用算法?

  1. 深度广度遍历;
  2. 广度优先遍历;
  3. 最短路径Floyed算法;
  4. 最短路径Dijktral算法;
  5. 最小生成树,Prime算法;
  6. 最小生成树Kuraul算法;
      1. 哈夫曼编码?

  1. 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
  2. 哈夫曼树的构造

将节点权重进行升序排序;

选择权重最小的两个节点,将两个节点的和作为新节点,将新节点加入数组中;

重复以上步骤,最后数组中剩下一个值,该值就是树的带权路径长度,即哈夫曼树;


      1. ***AVL树、B+树、红黑树、B树B+树区别,B+树应用在哪里?
  1. 一个m阶的B+树具有如下特征:

  • 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
  • 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  • 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
  • 在B+树中,只有叶子节点带有数据,其余中间节点仅仅是索引,没有关联任何数据。

  1. B+树的优势

  • 单一节点存储更多的元素,使得查询的IO次数更少;
  • 所有查询都要查询到叶子节点,查询性能稳定;
  • 所有叶子节点形成有序链表,便于范围查询;

  1. B+树与B-树的区别

 

 


      1. 为什么使用红黑树,什么情况使用AVL树。红黑树比AVL树有什么优点。
  1. 首先红黑树是不符合AVL树的平衡条件的,即每个节点的左子树和右子树的高度最多差1的二叉查找树。但是提出了为节点增加颜色,红黑是用非严格的平衡来换取增删节点时候旋转次数的降低任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高!!!红黑树的查询性能略微逊色于AVL树,因为他比avl树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的avl树最多多一次比较,但是,红黑树在插入和删除上完爆avl树,avl树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于avl树为了维持平衡的开销要小得多
  2. 如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。
      1. 单链表如何判断有环? 

  1. 在链表头部设置两个指针,一个每次向后移动两个位置,一个每次向后移动一个位置。两个指针遍历链表过程中,如果快指针到达链表尾还没有和慢指针相遇,说明链表无环,反之有环;
  2. 环的长度,从两个指针的交点开始,移动指针,当指针再次指向两个指针的交点的时候,就可以求出环的长度;
  3. 环的入口,一个指针从头开始一个指针指向(1)中的环中快慢指针的交点,开始遍历,直到这两个指针相遇,两个指针相遇的点就是环的入口点。
      1. 如何判断一个图是否连同?

可以用DFS(O(v^2))BFS(O(v+e))的思想都能实现,只要从一个点出发,然后判断是否能遍历完所有的点。


      1. hash用在什么地方,解决hash冲突的几种方法?负载因子?
  1. 如何构造哈希函数

  1. 数字分析法;
  2. 平方取中法;
  3. 除留余数法;
  4. 伪随机数法;

  1. 处理冲突

  1. 线性探测
  2. 二次探测
  3. 伪随机数探测;
  4. 拉链探测

  1. 如果负载因子是默认的0.75,HashMap(16)的时候,占16个内存空间,实际上只用到了12个,超过12个就扩容。
    如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。增大负载因子可以减少hash表的内存,如果负载因子是0.75,hashmap(16)最多可以存储12个元素,想存第16个就得扩容成32。如果负载因子是1,hashmap(16)最多可以存储16个元素。同样存16个元素,一个占了32个空间,一个占了16个空间的内存。
      1. n个节点的二叉树的所有不同构的个数
      2. 二叉树的公共祖先,排序二叉树的公共祖先

  1. 搜索二叉树

从树的根节点开始和两个节点进行比较,如果根节点大于两个节点值,则去根节点的左孩子去进行查找;如果根节点小于两个节点值,则去根节点的右孩子去进行查找;当根节点大于其中一个节点,小于其中一个节点,则该节点是最近的祖先节点。

方法一首先给出node1的父节点node1->_parent,然后将node1的所有父节点依次和node2->parent作比较,如果发现两个节点相等,则该节点就是最近公共祖先,直接将其返回。如果没找到相等节点,则将node2的所有父节点依次和node1->_parent->_parent作比较......直到node1->_parent==NULL。

方法二给定的两个节点都含有父节点,因此,可将这两个节点看做是两个链表的头结点,将求两个节点的最近公共祖先节点转化为求两链表的交点,这两个链表的尾节点都是根节点。


  1. 一般二叉树

方法一,将从根节点到node1的路径保存在数组中;将从根节点到node2的路径保存在数组中;两个数组从头开始遍历,直到找到不相同的两个值,该值前一个就是最近祖先;

方法二,从根节点开始遍历,如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。 如果都不匹配,则分别递归左、右子树,如果有一个 节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先.  如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。


      1. 节点的最大距离
  1. 如果具有最远距离的两个节点经过根节点,那么最远的距离就是左边最深的深度加上右边最深的深度之和;如果具有最远距离的两个节点之间的路径不经过根节点,那么最远的距离就在根节点的其中一个子树上的两个叶子节点。

int _Height(root, distance)

{

if(root为空)

return 0;

left = _Height(左子树);

right = _Height(右子树);

if(left+right>distance)

distance = left+right;

return 左子树、右子树较大加1;

}


      1. 把一颗二叉树原地变成一个双向链表

递归中序遍历;


      1. 二叉树的所有路径

vector binaryTreePaths(TreeNode* root) {

        // Write your code here

        vector res;

        if(root==NULL) return res;

        binaryTreePathsCore(root,res,to_string(root->val));

        return res;

    }

     

    void binaryTreePathsCore(TreeNode* root,vector &str,string strpath){

         

        if(root->left==NULL&&root->right==NULL){

            //叶子结点

            str.push_back(strpath);

            return;

        }

        if(root->left!=NULL){

            binaryTreePathsCore(root->left,str,strpath+"->"+to_string(root->left->val));

        }

        if(root->right!=NULL){

            binaryTreePathsCore(root->right,str,strpath+"->"+to_string(root->right->val));

        }

    }


      1. 二叉树中寻找每一层中最大值
  1. 利用队列来分别将每层的树添加进队列进行分析,先记录下第一个值作为最大值并弹出,然后往后边比较边弹出,如果当前值比前面的最大值还要大则替换当前最大值。在弹出每次的节点时将孩子节点加进队列。直到整棵树被遍历完。
  2. 伪代码

if(根节点为空)

返回

申请队列Q,将根节点入队列

While(队列不空)

{

s=队列长度

for(i=0;i

{

比较队首元素,并将队首元素出栈

队首左孩子入栈,右孩子入栈

}

}


      1. 最大深度、最小深度、会否是平衡树
  1. 二叉树的深度等于二叉树的高度,也就等于根节点的高度。根节点的高度为左右子树的高度较大者+1。 

int deepth(root)

{

if(root为空)

return 0;

else

{

left = deepth(左孩子);

right = deepth(右孩子);

return left>right? left+1:right+1;

}

}


  1. 求最大深度的时候,只需要比较左右子树的深度,取较大者+1就行了;但是求最小深度的时候,需要区分双子树与单子树,双子树时,深度较小者+1,单子树时(即左右子树有一颗为空时)为深度较大者+1。 

int deepth(root)

{

if(root为空)

return 0;

left = deepth(左孩子);

right = deepth(右孩子);

if(左孩子或有孩子为空)

return 不为空的深度+1;

return 左右较小者+1;

}


  1. 判断平衡二叉树,只需要判断平衡因子小于1即可,递归判断左右节点是否是平衡的即可;

bool isBalance(root)

{

left = 根节点左孩子高度;

right = 根节点右孩子高度;

if(right与left不满足平衡因子)

return false;

return isBalance(左孩子)&&isBalance(右孩子);

}


      1. 二叉树中叶子节点的数量
  1. 叶子节点就是左右孩子都是空的节点,在进行遍历的过程中,判断是否为叶子节点,如果是叶子节点,将计数器加1;
  2. 伪代码

void leafNodeNum(root,k)

{

if(树为空)

return;

if(root不为空)

{

if(叶子节点)

k++;

leafNodeNum(root->左孩子,k);

leafNodeNum(root->右孩子,k);

}

}


      1. 交换左右孩子、二叉树镜像
  1. 递归交换左右孩子,从跟节点开始交换,节点的左右孩子不都为空,则进行交换操作;否则返回;

void exchangeChild(root)

{

if(root->left空&&root->right空)

return;

swap(root->left,root->right);

exchangeChild(root->left);

exchangeChild(root->right);

}


      1. 两个二叉树是否相等
  1. 判断两颗树的根节点是否相同,如果不相同返回false,如果相同则递归判断根节点的左右子节点;如果两颗树中有一个树没有遍历完则说明不相等;两棵树都为空则两棵树相等;两棵树一颗为空一颗不为空则不相等;

bool treesEqual(root1,root2)

{

if(root1空 && root2空)

return true;

if(root1空 || root2空)

return false;

if(root1->data == root2->data)

return treesEqual(root1->left,root2->left)&&treesEqual(root1->right,root2->right);

else

return false;

}


      1. 是否为完全二叉树
  1. 如果一个结点有右孩子而没有左孩子,那么这棵树一定不是完全二叉树。 
    如果一个结点有左孩子,而没有右孩子,那么按照层序遍历的结果,这个结点之后的所有结点都是叶子结点这棵树才是完全二叉树。 
    如果一个结点是叶子结点,那么按照层序遍历的结果,这个结点之后的所有结点都必须是叶子结点这棵树才是完全二叉树。 

用一个标记变量leaf标记,当一个节点有左孩子无右孩子,leaf=true。之后所有的节点必须为叶子节点。


      1. 是否为对称二叉树

bool isSymmetrical(root1,root2)

{

if(root1空 && root2空)

return true;

if(root1空 || root2空)

return false;

if(root1->data != root2->data)

return false;

else

return isSymmetrical(root1->left,root2->right)&&isSymmetrical(root1->right,root2->left);

}


      1. 判断B是否为A的子树
  1. 找值相同的根结点(遍历解决)

判断两结点是否包含(递归:值、左孩子、右孩子分别相同)

bool isPart(root1,root2)

{

if(root1空&&root2空)

return true;

if(root1空 || root2空)

return false;

if(root1->data != root2->data)

return false;

else

return isPart(root1->left,root2->left)&&isPart(root1->right,root2->right);

}

bool isPartTree(root1,root2)

{

    if (root1不空 && root2不空)

    {

        if (root1->data == root2->data)

            result = IsPart(root1, root2);

        if (!result)

            result = IsPartTree(root1->left, root2);

        if (!result)

            result = IsPartTree(root1->right, root2);

    }

    return result;

}


      1. 构建哈夫曼树
      2. 手写单链表反转?删除指定的单链表的一个节点
  1. 单链表反转:尾插法转头查法;设置三个指针,当前节点指针,下一个节点指针,上一个节点指针,一开始上一个节点指针置为空;最后当下一个节点指针为空时说明到达最后节点,则返回该节点指针。
  2. 删除指定节点:如果我们把要删除节点的下一个节点的内容复制到需要删除的节点上,然后把删除节点的下一个节点删除,就可以完成删除该节点,同时时间复杂度为O(1)。如果是尾节点,只能遍历删除,如果只有一个节点,还要删除头节点。
      1. 实现一个循环队列

循环中front与rear的求值。

队首指针进1:frOnt= (front+1)%MaxSize;

队尾指针进1:rear = (rear+1)%MaxSize;

队空:rear == front;

队满:(rear+1)%MaxSize == front


      1. Top K问题
  1. 如果要找前K个最大的数,我们小堆,每次用堆顶元素和遍历的数比,如果堆顶元素小,则让堆顶元素的值等于它,然后向下调整
  2. 如果要找前K个最小的数,我们用大堆,每次用堆顶元素和遍历的数比,如果堆顶元素大,则让堆顶元素的值等于它,然后向下调整
  3. 用快速排序将数组进行排序,然后将数组输出
  4. 两者的时间复杂度都是O(nlog2n),快排的空间复杂度为O(log2n),堆排序的空间复杂度为O(1)
      1. 求一颗树的最大距离

对于二叉树,若要两个节点U,V相距最远,有两种情况:

1,从U节点到V节点之间的路径经过根节点

2,从U节点到V节点之间的路径不经过根节点,这种情况下,U,V节点必定在根节点的左子树或者右子树上,这样就转化为求以根节点的孩子节点为根节点的二叉树中最远的两个节点间的距离


      1. KMP

核心是next()函数的书写;


      1. 数组和链表的区别?
  1. 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况,即数组的大小一旦定义就不能改变。当数据增加时,可能超出原先 定义的元素个数;当数据减少时,造成内存浪费;链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删 除数据项时,需要移动其它数据项)。  
  2. (静态)数组从栈中分配空间(用NEW创建的在堆中), 对于程序员方便快速,但是自由度小;链表从堆中分配空间, 自由度大但是申请管理比较麻烦.
  3. 数组在内存中是连续存储的,因此,可以利用下标索引进行随机访问;链表是链式存储结构,在访问元素的时候只能通过线性的方式由前到后顺序访问,所以访问效率比数组要低。
      1. 逆序对思路
      2. 100个有序数组合并

归并排序,两个数组合并生成50个数组,生成25个数组,13个数组,6个数组,3个数组,2个数组,1个数组


      1. 使用递归和非递归求二叉树的深度
      2. 索引、链表的优缺点?
      3. 找一个点为中心的圆里包含的所有的点。
      4. 字典树的理解
  1. 字典树,又称单词查找树,是一种树形结构,是一种哈希树的变种;
  2. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符;

从根节点到叶子节点,路径上经过的字符链接起来,就是该节点对应的字符串;

每个节点的所有子节点包含的字符都不同;


  1. 典型应用是用于统计,排序和保存大量的字符串(不仅限于字符串),经常被搜索引擎系统用于文本词频统计。
      1. 速排序的优化

  1. 当我们每次划分的时候选择的基准数接近于整组数据的最大值或者最小值时,快速排序就会发生最坏的情况,但是每次选择的基准数都接近于最大数或者最小数的概率随着排序元素的增多就会越来越小,我们完全可以忽略这种情况。但是在数组有序的情况下,它也会发生最坏的情况,为了避免这种情况,我们在选择基准数的时候可以采用三数取中法来选择基准数。三数取中法:选择这组数据的第一个元素、中间的元素、最后一个元素,这三个元素里面值居中的元素作为基准数。
  2. 当划分的子序列很小的时候(一般认为小于13个元素左右时),我们在使用快速排序对这些小序列排序反而不如直接插入排序高效。因为快速排序对数组进行划分最后就像一颗二叉树一样,当序列小于13个元素时我们再使用快排的话就相当于增加了二叉树的最后几层的结点数目,增加了递归的次数。所以我们在当子序列小于13个元素的时候就改用直接插入排序来对这些子序列进行排序。
      1. 海量数据的bitmap使用原理

  1. BitMap解决海量数据寻找重复、判断个别元素是否在海量数据当中等问题
  2. 40亿个int占(40亿*4)/1024/1024/1024 大概为14.9G左右,很明显内存只有2G,放不下,因此不可能将这40亿数据放到内存中计算;40亿个int需要的内存空间为40亿/8/1024/1024大概为476.83MB;

推荐阅读
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 在机器学习领域,深入探讨了概率论与数理统计的基础知识,特别是这些理论在数据挖掘中的应用。文章重点分析了偏差(Bias)与方差(Variance)之间的平衡问题,强调了方差反映了不同训练模型之间的差异,例如在K折交叉验证中,不同模型之间的性能差异显著。此外,还讨论了如何通过优化模型选择和参数调整来有效控制这一平衡,以提高模型的泛化能力。 ... [详细]
  • 探讨Redis的最佳应用场景
    本文将深入探讨Redis在不同场景下的最佳应用,包括其优势和适用范围。 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 在 LeetCode 第 307 题中,我们详细探讨了树状数组(Fenwick Tree)的应用及其优化策略。树状数组 `tree` 用于存储特定区间内的元素和,其中区间的左边界是当前索引的二进制表示中去掉最后一个 1 后再加 1,右边界即为当前索引本身。此外,还维护了一个原始数组 `nums` 和一个表示数组长度的变量 `N`。通过这些结构,我们可以高效地实现区间求和与单点更新操作。 ... [详细]
  • OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战
    OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 深入解析CAS机制:全面替代传统锁的底层原理与应用
    本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
  • 在交换机链路聚合中,负载均衡算法通过哈希表实现。每当创建一个新的聚合组时,交换机的底层硬件会生成一个对应的哈希表,该表存储在交换芯片上。哈希表的结构包括索引(Index)和相应的条目,这些索引由硬件支持,用于确定数据包的传输路径。通过这种方式,负载均衡算法能够高效地分配网络流量,提高链路利用率和系统性能。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
author-avatar
Aries小阳光
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有