大厂面试题(四)数据结构和算法

       数据结构和算法博大精深,博客中写不完的。《剑指offer》这本书中列出了很多面试中算法问题的解决方案,很有学习价值。

1.  链表与数组。 

数组,在内存上给出了连续的空间.链表,内存地址上可以是不连续的,每个链表的节点包括值和下一个节点的位置(单向的一个,双向链表的话,会有两个).

数组优于链表的地方:

1.存储相同的值,数组内存空间占用的少,因为链表节点会附加上一块或两块下一个节点的信息.

2.数组内的数据可随机访问.但链表不具备随机访问性.链表在内存地址可能是分散的.所以必须通过上一节点中的信息找能找到下一个节点.

3.查找速度上数组占优势.这个也是因为内存地址的连续性的问题.

链表优于数组的地方:

1.插入与删除的操作.如果数组的中间(末尾除外)插入一个元素,那么这个元素后的所有元素的内存地址都要往后移动.删除的话同理.链表只需要更改有必要更改的节点内的节点位置信息就够了.并不需要更改节点的内存地址.

2.内存地址的利用率方面,如果没办法一次性给出数组所需的要空间,那就会提示内存不足.而链表可以是分散的空间地址. 只要存储足够不会有大小的限制。

3.链表的扩展性比数组好.因为一个数组建立后所占用的空间大小就是固定的.如果满了就没法扩展,不适合动态存储,不方便动态添加.


2. 队列和栈,出栈与入栈。 

栈是一种特殊的线性表,插入和删除数据元素的操作只能在线性表的一端进行。后进先出(Last In First Out)。顺序栈有"上溢"和"下溢"的概念。当栈的容量有限已经满并且还要入栈时就是"上溢","上溢"也就是栈顶指针指出栈的外面,显然是出错了。反之,当栈中空了还要取数据时就是"下溢"。"下溢"本身可以表示栈为空栈,因此可以用它来作为控制转移的条件。若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构(一个无头结点的单链表)。链栈则没有上溢的限制。

队列(Queue)也是一种线性表,允许删除的一端称为队尾(rear),允许插入的一端称为队头 (Front),队列的操作原则是先进先出的。


3. 链表的删除、插入、反向。 


4. 字符串操作。 

String类:

1、indexOf(String s)

    返回参数字符串s在指定字符串中首次出现的索引位置(从1开始数),如果没有检索到字符串s,该方法返回-1

2、lastIndexOf(Stringstr)

返回字符串最后一次出现的索引位置。如果没有检索到字符串str,该方法返回-1.如果lastIndexOf方法中的参数是空字符串"" ,,则返回的结果与length方法的返回结果相同。

3、charAt()方法可将指定索引处的字符返回

4、substring()方法

对字符串进行截取。这些方法的共同点就是都利用字符串的下标进行截取,且应明确字符串下标是从0开始的。在字符串中空格占用一个索引位置。

5、trim()方法

返回字符串的副本,忽略前导空格和尾部空格

6、replace(StringoldString,String newString)方法

将指定的字符或字符串替换成新的字符或字符串。替换后是一个新的字符串,原字符串不变。

7、startsWith()方法与endsWith()方法

分别用于判断字符串是否以指定的内容开始或结束。这两个方法的返回值都为boolean类型。

8、equals(Stringotherstr),equalsIgnoreCase(String otherstr)

如果两个字符串内容完全相同,则使用equals()方法比较时,返回true。同时equals()方法比较时区分大小写。equalsIgnoreCase()在比较时忽略了大小写。

9、compareTo()方法

按字典顺序比较两个字符串,该比较基于字符串中各个字符的Unicode值,如果参数字符串位于此字符串之后,则比较结果为一个负整数;如果参数字符串位于此字符串之前,则比较结果为一个正整数;如果这两个字符串相等,则结果为0.

10、toLowerCase()方法可将字符串中的所有字符从大写字母改写为小写字母,而tuUpperCase()方法可将字符串中的小写字母改写为大写字母。

11、split()方法可以使字符串按指定的分隔字符或字符串对内容进行分割,并将分割后的结果存放在字符数组中。分隔字符或字符串可以使用正则表达式。重载的方法可以限定分割次数。


5. Hash表的hash函数,冲突解决方法有哪些。 

HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。
HashtableHash 也是基于“拉链法”实现的散列表。它一般用于多线程程序中。

HashMap可以接受null(HashMapallows one null key and any number of null values.,而Hashtable则不行

HashSet实现了Set接口,它不允许集合中有重复的值,仅仅存储值。当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。

当程序试图将一个 key-value对放入 HashMap 中时,程序首先根据该 key 的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但 key 不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。

当向 HashMap 中添加 key-value对,由其 key 的 hashCode() 返回值决定该 key-value 对(就是 Entry 对象)的存储位置。当两个 Entry 对象的 key 的 hashCode() 返回值相同时,将由 key 通过 eqauls() 比较值决定是采用覆盖行为(返回 true),还是向 Entry 链添加(返回 false)。

http://www.cnblogs.com/devinzhang/archive/2012/01/13/2321481.html

http://blog.csdn.net/speedme/article/details/22485681

http://blog.csdn.net/chenssy/article/details/22896871


6. 各种排序:冒泡、选择、插入、希尔、归并、快排、堆排、桶排、基数的原理、平均时间复杂度、最坏时间复杂度、空间复杂度、是否稳定。 

一、冒泡排序 

基本思想是:两两比较相邻记录的关键字,如果反序则交换   冒泡排序时间复杂度最好的情况为O(n),最坏的情况是O(n^2)    改进思路1:设置标志位,明显如果有一趟没有发生交换(flag = flase),说明排序已经完成   改进思路2:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok 

二、直接插入排序 

将一个记录插入到已经排好序的有序表中, 从而得到一个新的,记录数增1的有序表,时间复杂度也为O(n^2), 比冒泡法和选择排序的性能要更好一些 

三、简单选择排序 

通过n-i次关键字之间的比较,从n-i+1 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换之,尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序 

四、希尔排序 

先将整个待排元素序列分割成若干子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。其时间复杂度为O(n^3/2),要好于直接插入排序的O(n^2) 

五、归并排序 

假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(不小于n/2的最小整数)个长度为2或1的有序子序列,再两两归并,...如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。时间复杂度为O(nlogn),空间复杂度为O(n+logn),如果非递归实现归并,则避免了递归时深度为logn的栈空间空间复杂度为O(n) 

六、堆排序

堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶  的根结点.将它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次大值.如此反复执行,便能得到一个有序序列了。时间复杂度为 O(nlogn),好于冒泡,简单选择,直接插入的O(n^2) 

七、快速排序 

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。时间复杂度为O(nlogn)


7. 快排的partition函数与归并的Merge函数。 

挺简单的代码,不在写出来了。

8. 对冒泡与快排的改进。 

改进思路1:设置标志位,明显如果有一趟没有发生交换(flag = flase),说明排序已经完成  

改进思路2:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok


9. 二分查找,与变种二分查找。 

//二分法查找

              publicint binarySearch(int[] numbers,int target) {

                     intlow = 0;

                     inthigh = numbers.length-1;

                     while(low<=high){

                     intmid = (high-low)/2+low;//直接使用(high+low)/2可能导致溢出

                            if(numbers[mid]==target) {

                                   returnmid;

                            }else{

                                   if(numbers[mid]>target) {

                                          high= mid-1;

                                   }else{

                                          low= mid+1;

                                   }

                            }

                     }

                     return-1;

              }

10. 二叉树、B+树、AVL树、红黑树、哈夫曼树。 

11. 二叉树的前中后续遍历:递归与非递归写法,层序遍历算法。 

12. 图的BFS与DFS算法,最小生成树prim算法与最短路径Dijkstra算法。 

13. KMP算法。 

14. 排列组合问题。 

15. 动态规划、贪心算法、分治算法。(一般不会问到) 

16. 大数据处理:类似10亿条数据找出最大的1000个数.........等等 

算法是面试准备的重中之重,不是解决上面几个问题就可以了的,牛客网上刷一些题很有效。

 

你可能感兴趣的:(大厂面试题(四)数据结构和算法)