之前的排序都是基于比较的排序,而桶排序是基于数据状况的排序,这就比较麻烦了,虽然很快,可是分析数据状况是很繁琐的。桶排序 是可以实现稳定排序的。常用有两种实现,一种是计数排序,一种是基数排序 (最后一节)。
桶排序的扩展,排序后的最大相邻数差值问题。有N个数字,建立N+1个桶,最大的差值不可能来自一个桶中,只能来自相邻的桶中,这样就可以得到最大差值了。
用数组结构实现大小固定的队列和栈?
数组实现栈,通过一个指针变量指向下一个进入栈中的元素的位置就可以实现了。
用数组实现队列,有3个变量,一个是start,一个是end,一个是size,start和end没有关系,仅仅和size有关系,如果size大小等于0,就表示没有东西了,直接报错。如果size大小大于数组长度,说明已经满了,也就直接报错返回,都则就可以循环的使用start和end 。
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返
回栈中最小元素的操作。
【要求】
1.pop、push、getMin操作的时间复杂度都是O(1)。没说空间限制
2.设计的栈类型可以使用现成的栈结构。
设计两个栈,同时压入就可以了。弹出的时候同步弹出。如果栈顶元素比min的栈顶元素小,压入该元素,否则压入min栈的栈顶元素。
还有一种是,如果栈为空,min栈直接压入,否则,如果当前值小于等于min栈顶元素,就压入,否则不压入。弹出的时候是当前数等于min栈栈顶元素的时候同步弹出,否则不弹出。
如何仅用队列结构实现栈结构?
用两个队列来实现。push的时候就一直push,直到要进行pop了,就将前面所有的元素都pop到另外一个队列中,然后队列中仅剩的一个元素返回,如果下一步还是pop,将这个队列中仅剩下一个元素,剩下的元素移动到两外一个队列中,如果是push操作,就直接在后面进行push操作就可以了。在完成一次pop的时候可以使用swap进行交换两个队列,这样逻辑代码就一致了,不用在两个队列之间进行跳转。
如何仅用栈结构实现队列结构?
用两个栈实现队列结构。一个push栈,一个pop栈,压栈的时候只进push栈,出栈的时候将push栈中的数据全部弹出到pop栈中,出栈的时候仅仅从pop栈中弹出,如果pop栈为空了,就从push栈中那数据,这样就可以实现了。
pop栈如果有东西,push栈中一定不要转移数据。
pop栈中为空的时候,才进行数据转移,一次性将push中的数据转移完全。
猫狗队列
同时维护两个dog队列和 cat队列,为了表示宠物的先后顺序,因此应该有一个表示可以表明进入队列的先后顺序,出去的时候比较一下就可以了。
认识哈希函数和哈希表
哈希函数的算法,MD5 SHA1,有很多hash算法。
性质,
输入无限,输出是有限的。
不是随机函数,相同的输入值都是相同的返回值
不同的输入也可能导致相通的输出。
不同的数得到的最后结果是几乎均匀分布的。 打乱输入规律,原来是很相近的,经过hash之后可能千差万别了。如果对最后的结果在进行小范围的求模运算,结果也是很近似的。离散性。
以前是后面是链表,后面变成了红黑树。
hash相关操作都是常数时间的,常数项比较大。
设计RandomPool结构 使用hash来实现的。
【题目】
设计一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入。
delete(key):将原本在结构中的某个key移除。
getRandom():等概率随机返回结构中的任何一个key。
【要求】
Insert、delete和getRandom方法的时间复杂度都是O(1)。
有下面3个元素 map1 map2 int index
当增加一个元素的时候,map1插入 key A value 0, map2 插入key 0 value A,正着插反着插都有。然后index++。然后一直加。
如果没有删除行为,index区域是连续的。然后调用random函数来等概率的返回。
如果有删除行为,就将最后一条记录的元素(通过map2来获得)放到要删除的位置(也就是先删除,然后在插入进去,只是将原来要删除的标识对应的元素替换成为了最后一个元素),然后index--就可以了。这样index还是连续的了。只要保证了index的连续,就可以使用random方法了。
转圈打印矩阵
给定一个整型矩阵matrix,请按照转圈的方式打印它。
例如:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
打印结果为:1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10
【要求】
额外空间复杂度为O(1)。
按圈来进行打印,先打印最外圈,在依次打印里面的圈,不要想着一下子打印出来。
“之”字形打印矩阵
给定一个矩阵matrix,按照“之”字形的方式打印这个矩阵,例如:
1 2 3 4
5 6 7 8
9 10 11 12
“之”字形打印的结果为:1,2,5,9,6,3,4,7,10,11,8,12
【要求】
额外空间复杂度为O(1)。
在行列都排好序的矩阵中找数
【题目】
给定一个有N*M的整型矩阵matrix和一个整数K,matrix的每一行和每一
列都是排好序的。实现一个函数,判断K是否在matrix中。
例如:
0 1 2 5
2 3 4 7
4 4 4 8
5 7 7 9
如果K为7,返回true;如果K为6,返回false。
【要求】
时间复杂度为O(N+M),额外空间复杂度为O(1)。
从右上角开始,当前数大于目标数,向左边移动,当前数小于目标数,向下移动。
一样可以从右下角走
打印两个有序链表的公共部分
【题目】
给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
谁小谁走,相等打印共同走。任意一个越界就停止了。
判断一个链表是否为回文结构
【题目】
给定一个链表的头节点head,请判断该链表是否为回文结构。
例如:
1->2->1,返回true。
1->2->2->1,返回true。
15->6->15,返回true。
1->2->3,返回false。
进阶:
如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)
O(N) 借助于栈的结构 进行实现。
设置两个指针,一个快指针,一个慢指针,快指针指向最后的时候,慢指针指向中点。
然后将慢指针后面的数据全部压入栈中,然后和开头的进行比对。
设置两个指针,一个快指针,一个慢指针,快指针指向最后的时候,慢指针指向中点。
然后将慢指针后面的数据全部逆序,然后相当于是有两个链通向中间节点,从两个端点开始进行遍历判断就可以了。记得将原始数据给逆置回去。
将单向链表按某值划分成左边小、中间相等、右边大的形式
【题目】
给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整
数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于
pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于
pivot的节点。除这个要求外,对调整后的节点顺序没有更多的要求。
例如:链表9->0->4->5->1,pivot=3。
调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总之,满
足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部
分为空),右部分都是大于3的节点即可。对某部分内部的节点顺序不做
要求。
借助与容器。
首先遍历以便链表,找到第一个小于,大于,等于的指针,单独搞出来,然后开始从头遍历,挂到合适的位置上,最后串起来就好了。
将单向链表按某值划分成左边小、中间相等、右边大的形式
【题目】
给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整
数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于
pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于
pivot的节点。除这个要求外,对调整后的节点顺序没有更多的要求。
例如:链表9->0->4->5->1,pivot=3。
调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总之,满
足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部
分为空),右部分都是大于3的节点即可。对某部分内部的节点顺序不做
要求。
上面那种方法就可以保证是相对顺序不变的。
复制含有随机指针节点的链表
【题目】
一种特殊的链表节点类描述如下:
public class Node {
public int value;
public Node next;
public Node rand;
public Node(int data) {
this.value = data;
}
}
Node类中的value是节点值,next指针和正常单链表中next指针的意义一
样,都指向下一个节点,rand指针是Node类中新增的指针,这个指针可
能指向链表中的任意一个节点,也可能指向null。
给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个
函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。
进阶:不使用额外的数据结构,只用有限几个变量,且在时间复杂度为
O(N)内完成原问题要实现的函数。
用map表是最 容易的。
遍历链表,在map中建立新节点和原始节点的对应关系,然后在遍历原始节点,进行指针的拷贝 工作。
如果 不用map应该怎么做?
将新链表就放在老链表的后面的,这样同样可以获得对应的关系。map存放的就是对应关系,现在直接将对应关系映射为下一个,就可以替代map这个结构了。
两个单链表相交的一系列问题
【题目】
在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点
head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数,
如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null
即可。
要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到
O(N+M),额外空间复杂度请达到O(1)。
判断一个链表有环还是无环
用hash表的方法。
将每个节点都加入到一个set中,只有节点,如果有环,可以重复回到一个节点,如果发现之前的节点遍历过,有环,如果走到了空,就是无环的。
不用hash表应该怎么做?
准备两个指针,slow和fast指针,
快指针走到了null肯定无环,如果有环,一定会相遇。
相遇后,快指针回到开头,然后变成每次 走一步,直到和满指针相遇,该节点就是第一个入环节点。
如果一个链表有环,返回第一个入环节点,如果无环,就返回null。
如果是两个无环链表
如果一个有环一个无环 不可能相交
两个有环 有3中情况进行判断
反转单向和双向链表
【题目】
分别实现反转单向链表和反转双向链表的函数。
【要求】
如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)
布隆过滤器和一致性hash
查找一个元素是否存在在集合里面。
布隆过滤器是一个大数组,数组中的每一位是一个bit
准备K个hash函数,如果布隆过滤器的数组 一共有M位,共占用m/8个字节,
每个url经过每个hash函数之后回得到一个数值,然后对m进行取模,就可以得到一个位于0-m-1之间的数据了。然后将相应的位置为1,就是进行描黑。
逐渐变黑的过程就是增加的过程
查找的过程是类似的,也是通过k个hash函数,取出相应的状态,如果全部都是1,那么就认为这个URL绝对加入过,如果有一个不为1,那么就认为绝对没有加入过。
布隆过滤器开多大和单样本的大小是没有关系的,因为hash的接受范围是无穷大,输出是一个有范围的,因此,布隆过滤器的大小是和hash有关系的。
对于布隆过滤器,样本量和失误率是唯一确定的条件,这两个条件可以确定数组的大小以及多少个hash函数。
m = -(n * ln(p)) /(ln2)^2 小数向上取正
k = ln2 * m/n = 0.7 *m/n 小数同样向上取正。
p (真)= (1-e^(-(n*k/m)))^k
面试的时候,空间压缩感很重,单样本的量很大,问一下是否允许失误,一般就是布隆过滤器了。
一致性hash
经典服务器结构
前端是无差别的,
随时找到数据流的中位数
【题目】
有一个源源不断地吐出整数的数据流,假设你有足够的空间来
保存吐出的数。请设计一个名叫MedianHolder的结构,
MedianHolder可以随时取得之前吐出所有数的中位数。
【要求】
1.如果MedianHolder已经保存了吐出的N个数,那么任意时刻
将一个新数加入到MedianHolder的过程,其时间复杂度是
O(logN)。
2.取得已经吐出的N个数整体的中位数的过程,时间复杂度为
O(1)。
用两个堆来实现,一个是大根堆,另外一个是小根堆。
题目二
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为20的
金条,不管切成长度多大的两半,都要花费20个铜板。一群人想整分整块金
条,怎么分最省铜板?
例如,给定数组{10,20,30},代表一共三个人,整块金条长度为10+20+30=60.
金条要分成10,20,30三个部分。
如果,
先把长度60的金条分成10和50,花费60
再把长度50的金条分成20和30,花费50
一共花费110铜板。
但是如果,
先把长度60的金条分成30和30,花费60
再把长度30金条分成10和20,花费30
一共花费90铜板。
输入一个数组,返回分割的最小代价。
哈夫曼编码问题。如何使非叶子节点的和最小。
可以用小根堆完成。
题目三
输入:
参数1,正数数组costs
参数2,正数数组profits
参数3,正数k
参数4,正数m
costs[i]表示i号项目的花费
profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润)
k表示你不能并行、只能串行的最多做k个项目
m表示你初始的资金
说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个
项目。
输出:
你最后获得的最大钱数。
又是用堆来实现,还是大根堆来实现,
选择小于启动资金受益 最大的项目
所有项目按照花费组成一个小根堆,然后受益组成大根堆。
刚开始小根堆是满的,大根堆是空的。
然后将所有满足的投弹出到大根堆中,更新启动资金。继续进行
直到k个条件满足为止。
折纸问题
【题目】
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时
折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从纸条的下边向上方连续对折2
次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。给定一
个输入参数N,代表纸条都从下边向上方连续对折N次,请从上到下打印所有折痕的方向。
例如:N=1时,打印:
down
N=2时,打印:
down
down
up
头结点为下折痕,每颗左子树的头结点是下折痕,每颗右子树的头结点是上折痕,然后进行中序遍历就可以得到结果。
在数组中找到一个局部最小的位置
【题目】
定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为
N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小;如果arr[N-
1]<arr[N-2],那么arr[N-1]是局部最小;如果0<i<N-1,既有
arr[i]<arr[i-1],又有arr[i]<arr[i+1],那么arr[i]是局部最小。
给定无序数组arr,已知arr中任意两个相邻的数都不相等。写一个函数,
只需返回arr中任意一个局部最小出现的位置即可。
用二分来实现。
并查集 高级数据结构
逻辑概念,是一些集合 一开始所有的元素各自一个集合
支持的操作有哪些呢?
isSameSet union
刚开始每个元素各自成为一个集合,每个p指针指向自己。
在进行合并的时候,较小的集合个数的挂在较大的集合个数上面,
当进行查找的时候,从该节点往上找,直到找到自己指向自己的这个节点,这个节点成为这个集合的代表节点。以代表带代表当前集合是什么。
合并和查询的逻辑现在还没有优化,因为可能造成不平衡,不平衡会影响效率。
将这棵树变成高度是1的树。
union操作本身也是要查询是否在一个集合中,如果不在一个集合中才进行合并,否则就不用合并了。如果要合并,就小的挂在大的底下就可以了。
在查询的过程中,原路上节点全部都挂到代表节点上面。
时间复杂度
元素个数是n,查询的次数+合并的次数逼近了O(N)或者以上,单次查询和单词合并是O(1)
只有达到一定的量度以后,平均查询是O(1)。
有什么应用呢?
何为前缀树?
如何生成前缀树?
例子:
一个字符串类型的数组arr1,另一个字符串类型的数组arr2
hash表只能处理完整的字符的数据情况,不能处理前缀这种情况,这种方式用前缀树可以很好的解决。
1)邻接表
2)邻接矩阵 可以存矩阵
如何表达图?生成图?
宽度优先遍历
1,利用队列实现
2,从源节点开始依次按照宽度进队列,然后弹出
3,每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
4,直到队列变空
深度优先遍历
1,利用栈实现
2,从源节点开始把节点按照深度放入栈,然后弹出
3,每弹出一个点,把该节点和下一个没有进过栈的邻接点放入栈
4,直到栈变空
1,拓扑排序算法
适用范围:要求有向图,且有入度为0的节点,且没有环
所有入读为0的节点为开始节点,作为from节点
实际应用,有相互依赖的先后执行就是一种典型的应用。
2,kruskal算法 最小生成树算法
适用范围:要求无向图
首先每个点都是一个集合,然后按照边来进行考察的,按什么顺序考察边么?按照小权重的来考察边,每次都选择最小的边,然后将该边的两个端点进行合并,如果这两个点已经在一个集合中,就不选择该边,继续选择下一条边,直至所有点都在一个集合里面了就停止。(从小边开始,没有形成回路就要它)就是并查集的应用。
3,prim算法 最小生成树
适用范围:要求无向图
按照点来进行考察,任选一个点开始进行考察。
如果这个点没有被包含进来,就将这个节点加进去,然后在优先队列中加入所有的这个节点的边,
当优先队列不为空的时候,就从里面弹出一个最小的边,判断这个边的另一端的节点是否已经在这个集合里面了,如果没有在里面,就将节点加入进去,将边加入进去,同时将这个节点的所有的边都加入到优先队列里面去。
4,Dijkstra算法
适用范围:没有权值为负数的边
原文地址:https://www.cnblogs.com/randyniu/p/9259848.html