【基础问答】数据结构

  1、什么是数据结构

数据结构的定义是三元组数据对象、数据关系和基本操作集。结构包括逻辑结构和物理结构。逻辑结构包括树、图、队列、栈、集合、线性表等,物理结构包括链表、顺序表等。

线性表

2、数组和链表的区别 / 顺序存储和链式存储的区别

数组:静态分配,物理和逻辑一致,插入和删除O(n),访问O(1)

链表:动态分配,物理和逻辑不一定一致,插入和删除O(1),访问O(n)

3、头结点和头指针的区别

头结点是链表第一个结点,内容可以为空,头指针指向第一个结点

栈和队列

4、栈和队列的区别

栈是只能在一端进行插入删除,“先进后出”

队列在一端进行插入,另一端进行删除,“先进先出”

5、简述KMP算法

KMP算法是给定两个字符串,判断长度较短的字符串是否是长字符串的子串,并输出第一次出现的位置

暴力解法:用两个下标分别表示当前匹配的主串和模式串的位置,每次移动主串和模式串的下标并匹配,不匹配时主串回到上个下标的后一个位置,模式串从头开始匹配,不停进行直到匹配成功。时间复杂度是O(mn)

KMP解法:KMP通过next数组记录之前失败的信息,来跳过不可能的尝试,使得主串下标每次不回退,模式串直接从上次失配的位置开始匹配,这样使复杂度变成O(m+n)

6、特殊的二叉树

(1)满二叉树:指深度为k且有2^k-1个结点的二叉树

(2)完全二叉树:每个结点与相应高度满二叉编号一一对应

(3)二叉排序树(二叉搜索树):左<根<右

  • 中序遍历递增
  • 增删查
    • 删:若为叶子结点直接删,若有左或右子树用其代替,若有左右子树,用直接前驱或直接后继代替
    • 查:最好O(logn)最坏O(n)

(4)平衡二叉树:在二叉查找树的基础上增加平衡要求,即每个结点左右子树高度差的绝对值不超过1

  • 插入:寻找最小不平衡子树根据情况进行LL,LR,RR,RL旋转
  • 求给定结点数树的最大高度n0=0,n1=1,n2=2,nh=1+nh-1+nh-2

(5)线索二叉树:如果没有左子树,则指向某种遍历次序的前驱结点,如果没有右子树,则指向某种遍历次序的后继结点

(6)哈夫曼树:给定N个权值作为N个叶子结点,每次选择权值最小的两个结点,构造一棵二叉树,使得该树的带权路径长度达到最小。在哈夫曼树中,权值越大的结点离根越近,越小的离根越远。

7、树与二叉树的转换

 

 8、二叉排序和折半查找性能比较

 二叉排序适合动态查找,进行插入删除O(logn),折半查找O(n)

折半查找适合静态查找,查找某元素O(logn),二叉排序最坏情况下O(n)

9、深度优先和广度优先的对比

它沿着深度遍历所有节点,尽可能深的搜索分支,直到无法深入回退到前一步状态。一般用栈辅助实现

BFS是从根节点开始,沿着宽度遍历 节点。如果所有节点均被访问,则算法中止。一般用队列辅助实现。

10、请比较最小生成树的算法(普里姆算法,克鲁斯卡尔算法)的异同

生成树是指包含图中所有节点的一棵树,而最小生成树则指一棵所有边权和最小的生成树。

11、什么时候最小生成树唯一?

当带权连通图的任意一个环中所包含的权值均不相同

 12、最短路径算法

单源最短路径:Dijkstra时间复杂度为O(n^2)


多源最短路径:Flyod时间复杂度为O(n^3)

 

 13、拓扑排序

时间复杂度O(v+e)

14、关键路径

AOE网中最长路径就是关键路径,完成整个工程最短时间就是关键路径的长度

查找

15、查找方法的实现

分为静态查找表和动态查找表;

静态查找表包括:顺序查找、折半查找、分块查找;动态查找包括:二叉排序树和平衡二叉树

顺序查找:优化(1)表中元素有序(2)按查找概率降序

折半查找:适用于有序的顺序表,当low<=high,不断比较关键字和中间位置的值,若相等则直接返回,若小于,则high=mid-1,大于则low=mid+1

分块查找:特点是:块间有序,块内无序,查找时块间进行顺序或折半查找,块内进行顺序查找

16、B树和B+树的区别

17、哈希表概念、构造方法、解决冲突的方法

把关键码值映射到表中一个位置来访问,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

常见散列函数:除留余数、数字分析、平方取中、直接定址.

解决冲突的方法:

18.贪心算法和动态规划以及分治法的区别?

贪心算法顾名思义就是做出在当前看来是最好的结果,它不从整体上加以考虑,也就是局部最优解

动态规划是穷举求最值,但存在重叠子问题和最优子结构,通过找到合适的状态转移方程来简化计算(各子问题重叠) ,从上往下分析问题,从下往上解决问题

分治法:将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。(各子问题独立) 

排序

(1)直接插入排序(稳定):将序列分为有序部分和无序部分,从无序部分依次选择元素与有序部分比较找到待插入的位置,将原来的元素往后移,将元素插入到相应位置上。

时间复杂度为:顺序O(n) 逆序O(n^2) 平均O(n^2),空间复杂度为O(1)

顺序比较n-1次,逆序比较n(n-1)/2次

(2)折半插入排序(稳定):通过折半查找在有序部分找到待插入的位置

时间复杂度为O(n^2),空间复杂度为O(1)

比较次数为O(nlog2n),优点是:比较次数大大减少。

(3)希尔排序(不稳定):先将序列按下标的一定增量分组,对每组进行直接插入排序,然后逐渐减少增量直至1。

(4)简单选择排序(不稳定):每次从待排序元素中选取关键字最小的元素加入有序子序列

时间复杂度为O(n^2),空间复杂度为O(1)。

(5)冒泡排序(稳定):基本思路为:每一趟都将元素进行两两比较,并且按照“前小后大”的规则进行交换。每一趟找到一个最大的元素放到序列后面

时间复杂度为O(n^2)【顺序O(n),逆序O(n^2)】,空间复杂度为O(1)。

(6)堆排序(不稳定):让此序列排列成完全二叉树,该树具有以下特点,该树中任意节点均大于或小于其左右孩子,此树的根节点为最大值或者最小值。

时间复杂度为O(nlogn),空间复杂度为O(1)。

(7)快速排序(不稳定):在序列中任意选择一个元素作为基元,比它大的元素一律向后移动,比它小的元素一律向前移动,形成左右两个子序列,则基元放在了最终位置。再把子序列递归地按上述操作进行调整,直到所有的子序列中都只有一个元素时序列即为有序。

时间复杂度为 O(nlog2n)【每次划分平均:O(nlogn) 原本正序或逆序O(n^2)】,

空间复杂度为O(log2n)【每次划分平均:O(n) 原本正序或逆序O(logn)】

优化:使用左端、右端和中心位置上的三个元素的中值作为基准元使划分更加平均

(8)归并排序(稳定):递归地把两个有序表合并成一个新的有序表。

时间复杂度为O(nlogn),空间复杂度O(n) 。

(9)基数排序(稳定):将关键字分为d位,进行d趟分配和收集,若当前关键字取得r个值,则需要建立r个队列,然后顺序扫描各元素进行分配O(n),然后耗时O(r)收集

时间复杂度为:对于n个记录进行链式基数排序的时间复杂度为O(d(n+r)),其中每一趟分配的时间复杂度为O(n)

算法题

简单:合并两个有序链表/寻找单链表的倒数第k个节点/寻找单链表的中点/两个链表是否相交/重建二叉树/数组中重复的数字/二维数组的查找/替换空格/斐波拉契

模拟

 (1)矩阵中的路径

 通过回溯法解决:设置一个等大的visit数组表明对应位置元素是否被访问过,从(0,0)作为起点,从它的四个相邻格子去匹配下一个字符,如果存在不匹配或者无路可走则回到前一个字符

(2)二进制1的个数 取最后一位只能 “与”

 

 通过/2右移效率低,如果是负数不断右移会出现死循环

 (3)数值的整数次方

正整数n次方:循环相乘;负整数:先求绝对值循环相乘后取倒数;特判0次方

(4)打印从1到最大的n位数

当n很大的时候会溢出,在字符串上模拟加法:

 (5)全排列时间复杂度O(N!)

 

(6) 

 

 (7)

(8)

 

 (9)

 

 (10)

 

链表

(1)判断链表是否包含环并确定环的入口

用快慢指针:每当慢指针slow前进一步,快指针fast就前进两步。如果fast最终遇到空指针,说明链表中没有环;如果fast最终和slow相遇,那肯定是fast超过了slow一圈,说明链表中含有环。

 

(2)从尾到头打印链表

典型的“先入后出”:每经过一个节点,把这个节点放入栈中,当遍历完整个链表后,再从栈顶开始逐个输出节点的值

(3)调整数组顺序使奇数位位于偶数前面/将负数移到非负数前面/能被3整除移到不能被3整除

(4)反转链表

头插法:新建一个头结点,遍历原链表,把每个节点用头结点插入到新建链表中。最后,新建的链表就是反转后的链表

(5)寻找两个链表的第一个公共节点

栈/队列

(1)用两个栈实现一个队列

push: 往stack1中push元素。

pop: 先判断stack2是否为空,若为空,将stack1中的所有元素pop至stack2中,取出stack2的栈顶元素pop出去;若不为空,直接取出stack2的栈顶元素pop出去

(2)用两个队列实现一个栈

定义一个队列为存储队列(queue1),另一个为中转队列(queue2)。入栈时直接压入queue1中,出栈时先将除queue1最后一个元素外依次pop出队列,并压入queue2中,将留在queue1中的最后一个元素出队列即为出队元素,之后再次将queue2中的元素压回queue1中

(3)定义栈的数据结构,实现一个能得到栈的最小元素的min函数

 

 (4)栈的压入弹出序列

 

(1)给定一棵二叉树和其中一个节点,找出中序遍历的下一个节点

如果当前节点存在右子树,那么下一个节点就是右子树的最左结点;如果没有右子树,就一直找它的父节点,直到它是某一个父节点左子结点,这个父节点就是下一个节点。
(2)树的子结构:输入两颗二叉树A,B,判断B是不是A的子结构

 (3)二叉树的镜像

 

 (4)判断二叉树是否对称

 根左右->根右左

(5)从上到下遍历二叉树

 

 

 

 (6)输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果

 

(7)

 

 (8)求二叉树的高度

 (9)判断二叉树是否是平衡二叉树

排序与查找

(1)查找数组中第k大的数字

经过一次partition后,数组被pivot分成左右两部分:S左、S右。当S左的元素个数|S左|等于k-1时,pivot即是所找的数;当|S左|小于k-1,所找的数位于S右中;当|S左|>k-1,所找的数位于S左中。时间复杂度为:O(N)

(2)旋转数组的最小数字

针对有序的数组进行查找考虑折半查找:设定两个指针low、high指向数组第一个元素和最后一个元素,如果mid对应元素大于等于low指针所指元素,则令low指向mid,如果mid对应元素小于等于high所指元素,则令high指向mid来缩小查找范围,如此重复直到low和high相邻,第二个指针指的位置就是要查找的元素(low指向前面子数组的最后一个元素,high指向后面子数组的第一个元素)

(3)数组中出现次数超过一半的数字 / 寻找数组的中位数

 (4)输出最小的k个数

 

 (5)

 

 

 时间复杂度O(logn)

 (6)

 

 (7)

 (8)数组中数字出现的次数

 

动规

(1)青蛙跳台阶问题(和斐波拉契思路一样,只是初始值不同)

状态定义:dp[i]跳上第i个台阶有dp[i]种跳法

转移方程:dp[i]=dp[i-1]+dp[i-2]

初始值:dp[0]=dp[1]=1

(2)有2*n 的地板,用1*2和 2*1 的骨牌进行铺地板,问共有多少种情况

状态定义:2*i的地板有dp[i]种情况

转移方程:dp[i]=dp[i-1]+dp[i-2]

初始值:dp[1]=1,dp[2]=2

(3)

 

(4)【动态规划】最长子数列和(序列问题)_falodr的博客-CSDN博客

(5)剪绳子

状态定义:长度为i的绳子剪成若干段后乘积最大值是dp[i]

转移方程:dp[i]=max(dp[j]*dp[n-j])

初始值:dp[0]=0,dp[1]=1,dp[2]=2,dp[3]=3

 (6)礼物最大价值

状态定义:到达坐标(i,j)格子时能拿到礼物价值总和最大值dp[i][j]

转移方程:dp[i][j]=max(dp[i-1][j],dp[i][j-1])+gift[i][j]

初始值:dp[0][0]=gift[0][0]

(7)最长不含重复字符的子字符串

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值