首页 > 代码库 > 一些数据结构的思想(2)
一些数据结构的思想(2)
- 1000瓶水中找 出有毒的那瓶,毒性一周后发作,一周内最少需要多少只老鼠
这个题是对bit位的应用,1000接近1024,所以需要10个bit位,对瓶子进行编号,从0到999,这样需要10只老鼠。瓶子的编号分别为:
00000,00000
00000,00001
00000,00010
00000,00011
00000,00101
00000,00111
。。。。。。
11111,00111
同时给老鼠编号,从1,2,...10,从低位开始,让第n个老鼠喝下第n个bit位为1瓶子中的药水。一周后,若所有的老鼠都没有发病,那么是第一个瓶子有毒,如果有一些老鼠发病,那么从低到高的bit位置成1,其他的还是0。变成整数后,对应的数字即为有毒药水的编号。
- 给定一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的兄弟单词,例如单词army和mary互为兄弟单词。现在给定一个字典,用户输入一个单词,如何根据字典找出这个单词有哪些兄弟单词?要求时间和空间效率尽可能的高。
解法一:
使用hash_map和链表。
首先定义一个key,使得兄弟单词有相同的key,不是兄弟的单词有不同的key。例如,将单词按字母从小到大重新排序后作为其key,比如bad的key为abd,good的key为dgoo。
使用链表将所有兄弟单词串在一起,hash_map的key为单词的key,value为链表的起始地址。
开始时,先遍历字典,将每个单词都按照key加入到对应的链表当中。当需要找兄弟单词时,只需求取这个单词的key,然后到hash_map中找到对应的链表即可。这样创建hash_map时时间复杂度为O(n),查找兄弟单词时时间复杂度是O(1)。
解法二:
同样使用hash_map和链表。
将每一个字母对应一个质数,然后让对应的质数相乘,将得到的值进行hash,这样兄弟单词的值就是一样的了,并且不同单词的质数相乘积肯定不同。
使用链表将所有兄弟单词串在一起,hash_map的key为单词的质数相乘积,value为链表的起始地址。
对于用户输入的单词进行计算,然后查找hash,将链表遍历输出就得到所有兄弟单词。
这样创建hash_map时时间复杂度为O(n),查找兄弟单词时时间复杂度是O(1)。
- 判断两个数组中是否有相同的数字
首先要说声不好意思,因为这个题说的很模糊,但是也算是两个方向吧。也就是说,给定的两个数组是否有序,如果无序的话那么解决这个问题所用的时间复杂度就是nlog(n),如果给定的两个数组有序,那么解决方法的时间复杂度会是O(n)。下面会给出两个解法。(假设数组是有序的,因为如果无序的话直接用快速排序或者堆排序在nlog(n)就可以解决)。
第一种方法,就是应用折半查找,依次从第一个数组中取出一个元素,到第二个数组中应用折半查找方法去寻找。这样一个循环内层加折半查找的嵌套,总得时间复杂度应该是mlog(n)(m,和n分别是两个数组的长度)。
第二种方法就是设定两个指针,分别指向第一个数组和第二个数组。取出指向的元素进行比较,如果ai<bj(因为数组是有序的),所以说明如果存在ak=bj,那么k应该在i之后,所以i得加1,如果i加1了以后,bj < a(i+1),那么说明和a(i+1)相等的b数组中的元素要在bj之后,所以j要加1。依次类推,直到找到ai=bj,或者不存在,循环结束为止。这个算法的时间复杂度是Om+n).(m和n分别指两个数组的长度)。
- 同时寻找一个数组中的最大元素和最小元素
给定一个数组a,含有n,寻找这个数组中的最大元素和最小元素,刚看到这个题目的时候,觉得这个问题没什么思考性,因为这个问题很简单,就是设定一个初始最大值和初始最小值,然后循环遍历数组,进行比较,至多会在2(n-1)的时间内找到最大值和最小值。下面给出一种分析的方法,使得这个问题能够在3*(n/2)内结束。首先要判定n是奇数还是偶数,
如果是奇数的话,假设min=max=a[1].然后遍历数组,但是这时候每次取两个数字,并把这两个数字进行比较,将较大的值和max进行比较,较小的值和min进行比较。这样循环 会在(n-1)/2或者(n-2)/2次后结束,这样比较的次数就变为每次循环要比较三次。这个方法对于n值很小的数组来说,没什么太大的影响,但是如果数组的n值很大的话,那时间减少了相当于原来的四分之一。
- 长度为1的线段,随机在其上选择两点,将线段分为三段,问这3个字段能组成一 个三角形的概率是多少
把x,y的取值放在坐标轴上进行考虑,从0到1的那段。那三条边的长度就为x,y-x,1-y。
假设我们选择的两个点的坐标是x和y(先假设x < y):
那么由三角形两边和大于第三边的性质,有:
x+y-x>1-y
x+1-y>y-x
1-y+y-x>x
由上述不等式得到:x < 0.5, 0.5 < y < x+0.5
然后画个图就能得到联合概率为1/8,不要忘了这个结果是在假设x < y时得来的,所以再乘以2,得到1/4。
- 单链表的反转
(1)设置三个变量,p,q,t,分别指向三个节点,然后遍历一遍单链表,改变各个节点的指针的指向,但是这个方法会很绕,如果没有清晰的思路很容易把自己绕晕。
(2)使用栈。可以一次遍历链表,把链表中的各个节点存入到堆栈中,然后在操作栈,因为栈的特性是后进先出,那么这样很简单的就可以把链表反转。
(3)重新设置一个头节点,让其代表一个新的链表,然后依次遍历之前给定的链表,每次遍历取出一个节点,插入到新链表的开头,那么这样一次遍历之后就重新建立了一个链表,而且是之前链表的反转,
- 位操作交换两数
void Swap(int &a, int &b) { if (a != b) { a ^= b; b ^= a; a ^= b; } }
- 二进制中1的个数
统计二进制中1的个数可以直接移位再判断,当然像《编程之美》书中用循环移位计数或先打一个表再计算都可以。本文详细讲解一种高效的方法。以34520为例,可以通过下面四步来计算其二进制中1的个数。
第一步:每2位为一组,组内高低位相加
10 00 01 10 11 01 10 00
-->01 00 01 01 10 01 01 00
第二步:每4位为一组,组内高低位相加
0100 0101 1001 0100
-->0001 0010 0011 0001
第三步:每8位为一组,组内高低位相加
00010010 00110001
-->00000011 00000100
第四步:每16位为一组,组内高低位相加
00000011 00000100
-->00000000 00000111
这样最后得到的00000000 00000111即7即34520二进制中1的个数。类似上文中对二进制逆序的做法不难实现第一步的代码:
x = ((x & 0xAAAA) >> 1) + (x & 0x5555);
- 如何将一棵树转换成二叉树?
解答:
1. 将 节点的孩子 放在左子树;
2. 将 节点的兄弟 放在右子树。
延伸:
任何一棵树都可以表示成二叉树,并不是任何一棵二叉树都可以表示成树。那么树多还是二叉树多?
1. 任何一棵树都可以表示成二叉树,结合以上题目很容易理解。
2.不是任何一棵二叉树都可以表示成树:
当根节点包含右子树的时候,就无法表示成树了。
3. 树多还是二叉树多的问题:
二叉树也是树的一种,如果按照包含关系来说,树肯定包含二叉树了,树多一些
- 从19本书中选取五本,并且要求这五本互相不相邻,一共有多少种方法?
题目:
从19本书中选取五本,并且要求这五本互相不相邻,一共有多少种方法?
解决方案一:挡板问题——插空法
假设当前在书架上已经放好14本书,那么只需要再把剩下五本书插入这些空中即可。
14本书有15个可以插入的空,因此,总共方法有:C(15,5)。
解决放啊二:二进制
转化成二进制方式,0表示选中国,1表示未选中。则题意转变成,要求00不能相邻。
可以编程遍历来实现。
- 求一个有序整数数组中和为K的数的对数
题目:
求一个有序整数数组中和为K的数的对数。
解决方案:
两个指针,一个在头,一个在尾;
大则-,小则加。
延伸题目:
(1)求整数数组中和为K的对数。
先排序,O(N*logN),在按照以上算法查找O(N)。
(2)求一个整数数组差为K的数的对数。
先排序,O(N*logN),然后,用两个指针均从头部开始,一个先走一个后走,差过小则前指针++,差过大则后指针++。
这里需要考虑一个问题,如果数组中有重复元素的话,需要做处理。
我是天王盖地虎的分割线
参考:http://blog.csdn.net/yahohi