如今安卓面试,对于数据结构的问题也愈来愈多了,要求也愈来愈多,因此我对于数据结构只能慢慢补起来了。(灬ꈍ ꈍ灬)面试
Android技能书系列:算法
Android基础知识数组
Android技能树 — Android存储路径及IO操做小结3d
数据结构基础知识
算法基础知识
本文主要讲 数组,链表,散列表(哈希表)。
当咱们去看电影的时候,咱们知道电影院门口会有一个储物柜,
上面还会有连续的数字,一个抽屉连着一个抽屉。 而后你就会把你的东西放在相应号码的小抽屉中,而后进去看电影了。
咱们在将数据存储到内存时候,你请求计算机提供存储空间,计算机会给你一个存储地址,而后你把内容存进去。就相似上面的储物柜。
线性表:零个或多个数据元素的有限序列。
若是你有三袋东西,你一个抽屉只能存一袋东西,这时候你就可使用了连续三个柜子。好比你使用了01,02,03号抽屉。
线性表的顺序存储结构:用一段地址连续的存储单元依次存储线性表的数据元素。
而后别人来使用了04号抽屉,这时候你朋友又给你一袋东西,说帮忙也去存一下,可是这时候由于04号抽屉已经被别人使用了,而大家又由于要求你们的东西都按照顺序放在一块儿,因此这时候大家只能从新找连续在一块儿的抽屉,好比08,09,10,11。万一12号被人使用了,而后大家又要再多存一袋物品呢??
这里咱们看出数组的特色:
相似咱们在排队买车票,忽然半路有我的插队,大家全部人都须要日后退后了一位;最前面的人买好票走了一个,大家全部人均可以往前前进一位。
数组 | 时间复杂度 |
---|---|
读取 | O(1) |
插入/删除 | O(n) |
不知道你们有没有看过相似古墓丽影相似的探宝电影。
它们的步骤就是先知道到了一个地点,而后到了第一个目的地A,到了A以后根据线索才知道下一个目的地B在哪里,而后再去B,而后这样下去A-- B-- C --.....这样,一直到最终的藏宝地方。没错,咱们的链表就是相似这种,好比咱们知道一共有四袋物品,可是你不能直接知道最后一个物品在哪里,你只能从第一个开始,一个个找下去。
好比咱们第一个存在了01号抽屉,存储内容为A,同时告诉你们,下一个物品在05号抽屉,里面内容为B,同时再下一个在08号。
由上面的图咱们能够知道,结点由存放数据元素的数据域和存放后继节点地址的指针域组成。
由上面咱们举例的古墓丽影的剧情可知,咱们不能直接知道最后一个线索在哪里,只能一个个从头至尾查过去,因此链表的读取会很慢;可是咱们若是想要插入和删除就很方便。
好比咱们要插入一个新的结点:
好比咱们要删除其中一个结点:
链表 | 时间复杂度 |
---|---|
读取 | O(n) |
插入/删除 | O(1) |
将单链表中终端结点的指针端改成指向头结点,就使整个单链表造成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。
双向链表是在单链表的每一个结点中,再设置一个指向其前驱结点的指针域。
静态链表是为了让没有指针的高级语言也可以用数组实现链表功能。
这个我就直接用网上的截图来讲明了:
静态链表是用相似于数组方法实现的,是顺序的存储结构,在物理地址上是连续的,并且须要预先分配地址空间大小。因此静态链表的初始长度通常是固定的。而后在这个里面存的时候不只存储数据域,同时存入了下一个数组index的位置。至关于咱们上面的指针域换成了数组的index值。
由上面咱们已经能够知道数组和链表各自的优点和缺点了。
操做 | 数组 | 链表 |
---|---|---|
读取 | 擅长(能够随机/顺序访问) | 不擅长(只能顺序访问) |
插入/删除 | 不擅长 | 擅长 |
有了上面的知识,咱们就能够引入散列表了,咱们用具体的故事需求来引入散列表:
若是你有一天开了一家水果店,你会拿一个本子来记各类水果的价格,由于你们知道数组对于读取来讲很方便,因此咱们用一个数组来记录各类水果的价格,而且是按照开头字母来进行顺序写入的。
这时候,若是有人问Apple,你就查询一下价格,可是若是水果不少,甚至不少都是A开头的水果,好比有20个A开头的水果,这时候你只能知道A开头的水果是前面20个,可是具体是哪一个,你又要一个个的查过来,若是咱们立刻就知道Apple对应的数组index值就行了,这样就立刻知道在数组的哪一个位置,而后立刻就能够读取出来了。
好比下图:
这样咱们就在index为2的地方存储了苹果的价格,而后在index为8的地方存储了香蕉的价格,依次类推,全部水果都记录进去,这样顾客问你苹果价格时候,你就立刻知道在index为2的地方去读取。
而把Apple变为2是经过散列函数来实现的。
咱们要实现上面的需求,这个散列函数须要一些基本要求:
若是输入的内容相同时,每次获得的值都相同,好比你每次输入都是Apple,好比每次获得的结果都是2,不能一会儿2,一会儿5。
若是无论输入什么值获得的结果都相同,那么这个函数也没用,你输入Apple和输入Banana获得的值都相同,那么没有任何分辨做用。
散列函数须要返回有效的索引,好比上面咱们的数组的长度只有40,你输入Pair时候输出100,这样是无效索引。
根据上面的状况咱们知道了,咱们输入不一样的值的时候,经过散列函数换算后,最好的结果是每一个值都是不一样,这样的话他们的index 也不一样。
可是若是咱们的数组只有长度为6,可是咱们有7种水果,那么必定会有二个水果获得的index是相同的。这时候咱们称这种状况为冲突。
处理冲突的方式有不少,最简单的办法就是:若是二个键映射到了同一个位置,就在这个位置存储一个链表。
这样,咱们在查询其余水果时候仍是很快,只是在查询index为0的水果时候稍微慢一点,由于要在index为0的链表中找到相应的水果。
散列表操做 | 平均状况 | 最糟状况 |
---|---|---|
查找 | O(1) | O(n) |
插入 | O(1) | O(n) |
删除 | O(1) | O(n) |
咱们能够看到:
因此针对最糟的状况,咱们须要:
这样咱们之后想要知道某个水果价格,只须要输入水果名字,而后经过散列函数返回一个index值就能够去数组中找相应的价格了。
哪里错误请帮忙指正,thanks。
参考:
《大话数据结构》
《算法图解》