面试时的Java数据结构与算法.pdf





《面试时的Java数据结构与算法.pdf》由会员分享,可在线阅读,更多相关《面试时的Java数据结构与算法.pdf(26页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、面试时的 Java 数据结构与算法 查找和排序算法是算法的入门知识,其经典思想可以用于很多算法当中。因为其实现代码较短,应用较常见。所以在面试中经常会问到排序算法及其相关的问题。但万变不离其宗,只要熟悉了思想,灵活运用也不是难事。一般在面试中最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码。对这两种排序的代码一定要信手拈来才行。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景。还有要会分析算法的时间和空间复杂度。通常查找和排序算法的考察是面试的开始,如果这些问题回答不好,估计面试官都没有继续面试
2、下去的兴趣都没了。所以想开个好头就要把常见的排序算法思想及其特点要熟练掌握,有必要时要熟练写出代码。接下来我们就分析一下常见的排序算法及其使用场景。限于篇幅,某些算法的详细演示和图示请自行寻找详细的参考。冒泡排序 冒泡排序是最简单的排序之一了,其大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。举个栗子,对 5,3,8,6,4这个无序序列进行冒泡排序。首先从后向前冒泡,4 和 6 比较,把 4 交换到前面,序列变成5,3,8,4,6。同理4和8交换,变成5,3,4,8,6,3和4无需交换。5和3交换,变成3,5,4,8,6,3.这样一次冒泡
3、就完了,把最小的数 3 排到最前面了。对剩下的序列依次冒泡就会得到一个有序序列。冒泡排序的时间复杂度为 O(n2)。实现代码:/*Description:冒泡排序算法实现*author 王旭*/publicclassBubbleSort publicstaticvoidbubbleSort(intarr)if(arr=null|arr.length=0)return;for(inti=0;i)for(intj=arr.length-1;ji;j-)if(arrj)swap(arr,j-1,j);publicstaticvoidswap(intarr,inti,intj)inttemp=arri
4、;arri=arrj;arrj=temp;选择排序 选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择。举个栗子,对 5,3,8,6,4 这个无序序列进行简单选择排序,首先要选择 5 以外的最小数来和 5交换,也就是选择 3 和 5 交换,一次排序后就变成了 3,5,8,6,4.对剩下的序列一次进行选择和交换,最终就会得到一个有序序列。其实选择排序可以看成冒泡排序的优化,因为其目的相同,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。选择排序的时间复杂度为 O(n2)。
5、实现代码:/*Description:简单选择排序算法的实现*author 王旭*/publicclassSelectSort publicstaticvoidselectSort(intarr)if(arr=null|arr.length=0)return;intminIndex=0;for(inti=0;i/只需要比较 n-1 次 minIndex=i;for(intj=i+1;j/从 i+1 开始比较,因为 minIndex 默认为 i 了,i 就没必要比了。if(arrjarrminIndex)minIndex=j;if(minIndex!=i)/如果 minIndex 不为 i,说明
6、找到了更小的值,交换之。swap(arr,i,minIndex);publicstaticvoidswap(intarr,inti,intj)inttemp=arri;arri=arrj;arrj=temp;插入排序 插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的的。相信大家都有过打扑克牌的经历,特别是牌数较大的。在分牌时可能要整理自己的牌,牌多的时候怎么整理呢?就是拿到一张牌,找到一个合适的位置插入。这个原理其实和插入排序是一样的。举个栗子,对 5,3,8,6,4 这个无序序列进行简单插入排序,首先假设第一个数的位置时正确的,想一下在拿到第一张牌的时候,没必要整理
7、。然后 3 要插到 5 前面,把 5 后移一位,变成 3,5,8,6,4.想一下整理牌的时候应该也是这样吧。然后 8 不用动,6插在 8 前面,8 后移一位,4 插在 5 前面,从 5 开始都向后移一位。注意在插入一个数的时候要保证这个数前面的数已经有序。简单插入排序的时间复杂度也是 O(n2)。实现代码:/*Description:简单插入排序算法实现*author 王旭*/publicclassInsertSort publicstaticvoidinsertSort(intarr)if(arr=null|arr.length=0)return;for(inti=1;i/假设第一个数位置时
8、正确的;要往后移,必须要假设第一个。intj=i;inttarget=arri;/待插入的/后移 while(j0&target)arrj=arrj-1;j-;/插入 arrj=target;快速排序 快速排序一听名字就觉得很高端,在实际应用当中快速排序确实也是表现最好的排序算法。快速排序虽然高端,但其实其思想是来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。举个栗子:对 5,3,8,6,4 这个无序序列进行快速排序,思路是右指针找比基准数小的,左指针找比基准数大的,交换之。5,3,8,6
9、,4 用 5 作为比较的基准,最终会把 5 小的移动到 5 的左边,比 5 大的移动到 5 的右边。5,3,8,6,4 首先设置 i,j 两个指针分别指向两端,j 指针先扫描(思考一下为什么?)4 比5 小停止。然后 i 扫描,8 比 5 大停止。交换 i,j 位置。5,3,4,6,8 然后 j 指针再扫描,这时 j 扫描 4 时两指针相遇。停止。然后交换 4 和基准数。4,3,5,6,8 一次划分后达到了左边比 5 小,右边比 5 大的目的。之后对左右子序列递归排序,最终得到有序序列。上面留下来了一个问题为什么一定要 j 指针先动呢?首先这也不是绝对的,这取决于基准数的位置,因为在最后两个指
10、针相遇的时候,要交换基准数到相遇的位置。一般选取第一个数作为基准数,那么就是在左边,所以最后相遇的数要和基准数交换,那么相遇的数一定要比基准数小。所以 j 指针先移动才能先找到比基准数小的数。快速排序是不稳定的,其时间平均时间复杂度是 O(nlgn)。实现代码:/*Description:实现快速排序算法*author 王旭*/publicclassQuickSort/一次划分 publicstaticintpartition(intarr,intleft,intright)intpivotKey=arrleft;intpivotPointer=left;while(leftright)whi
11、le(left=pivotKey)right-;while(leftpivotKey)left+;swap(arr,left,right);/把大的交换到右边,把小的交换到左边。swap(arr,pivotPointer,left);/最后把 pivot 交换到中间 returnleft;publicstaticvoidquickSort(intarr,intleft,intright)if(left=right)return;intpivotPos=partition(arr,left,right);quickSort(arr,left,pivotPos-1);quickSort(arr,p
12、ivotPos+1,right);publicstaticvoidsort(intarr)if(arr=null|arr.length=0)return;quickSort(arr,0,arr.length-1);publicstaticvoidswap(intarr,intleft,intright)inttemp=arrleft;arrleft=arrright;arrright=temp;其实上面的代码还可以再优化,上面代码中基准数已经在 pivotKey 中保存了,所以不需要每次交换都设置一个 temp 变量,在交换左右指针的时候只需要先后覆盖就可以了。这样既能减少空间的使用还能降低赋
13、值运算的次数。优化代码如下:/*Description:实现快速排序算法*author 王旭*/publicclassQuickSort/*划分*paramarr*paramleft*paramright*return*/publicstaticintpartition(intarr,intleft,intright)intpivotKey=arrleft;while(leftright)while(left=pivotKey)right-;arrleft=arrright;/把小的移动到左边 while(leftpivotKey)left+;arrright=arrleft;/把大的移动到右
14、边 arrleft=pivotKey;/最后把 pivot 赋值到中间 returnleft;/*递归划分子序列*paramarr*paramleft*paramright*/publicstaticvoidquickSort(intarr,intleft,intright)if(left=right)return;intpivotPos=partition(arr,left,right);quickSort(arr,left,pivotPos-1);quickSort(arr,pivotPos+1,right);publicstaticvoidsort(intarr)if(arr=null|
15、arr.length=0)return;quickSort(arr,0,arr.length-1);总结快速排序的思想:冒泡+二分+递归分治,慢慢体会。堆排序 堆排序是借助堆来实现的选择排序,思想同简单的选择排序,以下以大顶堆为例。注意:如果想升序排序就使用大顶堆,反之使用小顶堆。原因是堆顶元素需要交换到序列尾部。首先,实现堆排序需要解决两个问题:如何由一个无序序列键成一个堆?如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?第一个问题,可以直接使用线性数组来表示一个堆,由初始的无序序列建成一个堆就需要自底向上从第一个非叶元素开始挨个调整成一个堆。第二个问题,怎么调整成堆?首先是将堆顶元素和
16、最后一个元素交换。然后比较当前堆顶元素的左右孩子节点,因为除了当前的堆顶元素,左右孩子堆均满足条件,这时需要选择当前堆顶元素与左右孩子节点的较大者(大顶堆)交换,直至叶子节点。我们称这个自堆顶自叶子的调整成为筛选。从一个无序序列建堆的过程就是一个反复筛选的过程。若将此序列看成是一个完全二叉树,则最后一个非终端节点是 n/2 取底个元素,由此筛选即可。举个栗子:49,38,65,97,76,13,27,49 序列的堆排序建初始堆和调整的过程如下:实现代码:/*Description:堆排序算法的实现,以大顶堆为例。*author 王旭*/publicclassHeapSort/*堆筛选,除了 s
17、tart 之外,startend 均满足大顶堆的定义。*调整之后 startend 称为一个大顶堆。*paramarr 待调整数组*paramstart 起始指针*paramend 结束指针*/publicstaticvoidheapAdjust(intarr,intstart,intend)inttemp=arrstart;for(inti=2*start+1;i)/左右孩子的节点分别为 2*i+1,2*i+2/选择出左右孩子较小的下标 if(i)i+;if(temp=arri)break;/已经为大顶堆,=保持稳定性。arrstart=arri;/将子节点上移 start=i;/下一轮筛选
18、 arrstart=temp;/插入正确的位置 publicstaticvoidheapSort(intarr)if(arr=null|arr.length=0)return;/建立大顶堆 for(inti=arr.length/2;i=0;i-)heapAdjust(arr,i,arr.length-1);for(inti=arr.length-1;i=0;i-)swap(arr,0,i);heapAdjust(arr,0,i-1);publicstaticvoidswap(intarr,inti,intj)inttemp=arri;arri=arrj;arrj=temp;希尔排序 希尔排序
19、是插入排序的一种高效率的实现,也叫缩小增量排序。简单的插入排序中,如果待排序列是正序时,时间复杂度是 O(n),如果序列是基本有序的,使用直接插入排序效率就非常高。希尔排序就利用了这个特点。基本思想是:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序。举个栗子:从上述排序过程可见,希尔排序的特点是,子序列的构成不是简单的逐段分割,而是将某个相隔某个增量的记录组成一个子序列。如上面的例子,第一堂排序时的增量为 5,第二趟排序的增量为 3。由于前两趟的插入排序中记录的关键字是和同一子序列中的前一个记录的关键字进行比较,因此关键
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面试 Java 数据结构 算法

限制150内