首页 > 代码库 > 一些数据结构的思想(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