《剑指offer》-第2章(2)

来源:互联网 发布:java中的类命名规范 编辑:程序博客网 时间:2024/05/22 15:51

2.3.5 栈和队列

栈:后进先出,不考虑排序的数据结构,要找到最大最小元素需要O(n)时间,若要在O(1)时间内找到最大最小值,需要对栈做特殊的设计。

队列:先进先出。在树的宽度优先遍历中,遍历某一层数的队列里,以便下一层节点的遍历(面试题32)。

面试题9:用两个栈实现队列

       实现两个函数appendTail和deleteHead

测试用例:

1.    往空队列里添加元素、删除元素;

2.    往非空队列里添加元素、删除元素;

3.    连续删除元素直到队列为空;

思路:

1.    题目意图是用两个先进后出的栈stack1、stack2,实现一个先进先出的队列

2.    插入的元素压入stack1,不管stack2

3.    删除元素deleteHead:如果stack2为空,则从stack1依次弹出元素并压入stack2,操作完后stack1是空,弹出stack2顶部元素;如果是stack2不为空,则直接弹出stack2顶部元素;

4.    插入元素appendTail:压入stack1;

5.    考虑是否有问题,如果此时stack2有元素,要弹出元素,则弹出stack2顶部元素,stack2中的元素总是比stack1中的元素先到;因此删除元素时考查stack2,插入元素时直接压入stack1

相关题目:用两个队列实现一个栈

思路:

1.    queue1中依次是插入的元素,queue2是空;

2.    删除元素(删除最后插入的元素):找到不为空的队列(queue1或queue2),依次从队列头弹出元素并压入另一个队列,直到剩下最后一个元素,直接弹出不压入另一个队列;

3.    插入元素:找到不为空的队列(queue1或queue2),将新元素压入队列尾;

2.4 算法和数据操作

重点掌握二分查找、归并排序、快速排序,能够随时正确、完整地写出代码;

如果要在二维数组上搜索路径,可尝试回溯法,考虑用递归、用栈模拟递归;

求某个问题的最优解,且问题可分为多个子问题,尝试动态规划;如果面试官提示在分解子问题的时候是不是存在某个特殊的选择,采用这个特殊选择将一定能得到最优解,考虑贪婪算法;

2.4.1 递归和循环

递归:代码简洁,但函数调用有时间和空间的消耗,且可能发生调用栈溢出,可能很多计算是重复的;性能比循环差。

循环:设置循环的初始值和终止条件即可。

面试题10:斐波那契数列

   输入n,求斐波那契数列的第n项。

解法一:递归,挑剔的面试官会提示有效率问题

分析:用树的结构分析递归过程,发现很多节点是重复的,n很大时重复量也很大

解法二:循环,从下往上计算,根据f(0) f(1)计算f(2),依次计算f(3),f(4)……f(n),

分析:复杂度是O(n)

解法三:时间复杂度O(logn)的解法,代码复杂,一般不用

斐波那契数列的应用:

青蛙跳台阶问题:一直青蛙依次可以跳上1级台阶,也可以跳上2级台阶,求该青蛙跳上衣蛾n级台阶总共有多少种跳法

1级台阶f(1) = 1,2级台阶f(2) = 2,n级台阶;如果第一次跳1级,后面的n-1级台阶的跳法是f(n-1),如果第二次跳2级,后面n-2级台阶的跳法是f(n-2),即f(n) = f(n-1) + f(n-2)

 

2.4.2 查找和排序

顺序查找、二分查找、哈希表查找、二叉排序树查找,要能够随时正确写出

哈希表:能够在O(1)时间内查找到某一元素,但需要额外的空间来实现

要对各种排序算法的特点烂熟于胸:插入排序、冒泡排序、归并排序、快速排序,能从额外空间消耗、平均时间复杂度、最差时间复杂度等方面比较

面试题11:旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转({3,4,5,1,2}是{1,2,3,4,5}的一个旋转,数组的最小值为1)。输入一个数组的旋转,输出该数组的最小值。

测试用例:

1.    输入数组中有重复数字、没有重复数字

2.    输入数组是一个升序序列(升序序列本身也是一个旋转)、只有一个元素的数组

3.    输入null

分析:

1.    从头到尾遍历可以找到最小值,算法复杂度是O(n),这不是想要的解答

2.    旋转后的数组可以分为两个排序子序列,最小的元素是两个序列的分界线

3.    用二分查找的思路,两个指针P1、P2分别指向数组第一个元素和最后一个元素

4.    对于一般的旋转数组*p1>=*p2,找数组中间元素

5.    如果*p1<中间元素,说明中间元素属于第一个子数组,将p1指向中间元素

6.    如果中间元素<*p2,说明中间元素属于第二个子数组,将p2指向中间元素

7.    循环4,5,6,直到p1与p2相邻,则p2所指的元素是最小的元素

8.    对于边界情况即升序序列本身,*p1<*p2时,则p1所值的数组是最小的元素

9.    如果*p1==中间元素==*p2,则无法判断中间元素属于哪个子数组(如10111与11101),此时采用顺序查找

原创粉丝点击