剑指Offer(四)用两个栈实现一个队列,斐波那契数列及相关问题
题目:
用两个栈实现一个队列
思路:
方法一: 使用一个栈,依次压入,依次弹出,时间空间均为O(n)
方法二:递归,每次输出前,先输出他的下一个节点,然后依次调用.
方法三: 使用头插法,使用头插法去构建一个新的链表,这样最后的元素会在最前.
例子:
1 2 3 4 5 输出 5 4 3 2 1
代码:
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/**
*但该方法若是输入的链表长度非常长时
* 会导致函数调用的层级过深
* 从而导致函数调用栈溢出(java.lang.StackOverflowError)
*/
ArrayList<Integer> res=new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null)
{
this.printListFromTailToHead(listNode.next);
res.add(listNode.val);
}
return res;
}
/**
* 调用栈
* @param listNode
* @return
*/
public ArrayList<Integer> printListFromTailToHead2(ListNode listNode) {
Stack<Integer> stack =new Stack<>();
while(listNode!=null){
stack.push(listNode.val);
listNode=listNode.next;
}
ArrayList<Integer> list=new ArrayList<Integer>();
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
/**
* 使用头插法
* 每次都是将 新的元素作为头节点,
* 原链表中最后的节点就放到了最后,然后返回这个新的链表.
* 注意点:
* * 头结点是在头插法中使用的一个额外节点,这个节点不存储值
* * 第一个节点就是链表的第一个真正存储值的节点
*/
public ArrayList<Integer> printListFromTailToHead3(ListNode listNode) {
ListNode head = new ListNode(-1);
while (listNode!=null)
{
ListNode node =new ListNode(listNode.val);
node.next = head.next;
head.next = node;
listNode = listNode.next;
}
ArrayList<Integer> res=new ArrayList<Integer>();
head = head.next;
while(head!=null)
{
res.add(head.val);
head=head.next;
}
return res;
}
题目1:
斐波那契数列及相关问题
现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
思路:
1 简单递归的方法
f(n) = f(n-1) +f(n-2)
2 动态规划的做法
求解前一项时,保存再来,方便后面的计算,避免了重复求解
把子问题的解缓存起来 这样避免了重复求解子问题
3 时间复杂度为O(nlogn) 的做法
利用一个复杂的数学公式 可以查看这个:https://blog.csdn.net/xuesong218/article/details/81130982
代码:
/**
* 直接递归
* @param n
* @return
*/
public static int fei(int n)
{
if (n<=1)
{
return n;
}
return fei(n-1)+fei(n-2);
}
/**
* 使用累加的方式,算后面的数
*/
public static int fei2(int n)
{
if(n<=1)
{
return n;
}
int pre1=1;//前一项为1
int pre2=0;//前两项为0
int fibN=0;
for(int i=2;i<=n;i++)
{
fibN=pre1+pre2;
pre2=pre1;//前进一位
pre1=fibN;
}
return fibN;
}
public static void main(String[] args)
{
System.out.println(fei(2));
}
题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路:
假如有2 个台阶,那就是跳1级 再跳一级, 这一种. 也可以一次跳2级 .一共有2种跳法
倒着看
跳到 第n 级 时,可以先跳到 n-1 级 ,也可以先跳到 n-2 级
那就是 f(n) = f(n-1) +f(n-2)
且 第一项为1 第二项为 2
代码:
public class Solution {
public int JumpFloor(int target) {
if(target<=2)
{
return target;
}
int pre1=2;
int pre2=1;
int fibN=0;
for(int i=2;i<target;i++)
{
fibN=pre1+pre2;
pre2=pre1;
pre1=fibN;
}
return fibN;
}
}
题目: 变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
代码:
/**
* 当上第三级阶梯是
*
* f 3 = f 2 + f 1 + f 0
* f 2 = f 1 + f 0
* f 1 = f 0
* 双层循环 外循环为 上楼梯的节数
* 内循环为 从 第 0 级上到 第 i 级的阶梯的跳法
Arrays.fill(Object[ ] arr, Object obj);
向数组中传送一个相同对象。
Arrays.fill(int[ ] arr, int value);
向数组中传送一个相同value。
*/
public static int Jump(int n)
{
int dp[] = new int[n+1];
Arrays.fill(dp,1);
for(int i=1;i<n;i++)
//从第一级阶梯往上遍历
{
for(int j=0;j<i;j++)
// 第 i 级 楼梯前 是 i 种走法
// 比如 第 2 级楼体 f(2) = f(1) + f(0)
{
dp[i]=dp[i]+dp[j];
}
}
return dp[n-1];
}
/**
* 由上面的思路 要跳上第n级台阶
* 有:f(n) = f(n-1) + f(n-2) + ... + f(0)
* 要跳上第n-1级台阶
* 有:f(n-1) = f(n-2) + f(n-3) + ... + f(0)
* 两式相减 得:
* f(n)-f(n-1)=f(n-1)
* 即:
* f(n)=2*f(n-1)
* 即
* f(n)=2^(n-1)
*/
public int JumpFloor(int target)
{
return (int)Math.pow(2, target-1);
}
public static void main(String[] args)
{
Jump(2);
}
对于斐波那契数列而言,第一项是0,所以不可以直接判断<=2返回,
// 求台阶 因为台阶第一级是1 而不是0
if(target<=2)
{
return target;
}
for(int i=2;i<target;i++)
//斐波那契数列
if(target<=1)
{
return target;
}
for(int i=2;i<=target;i++)
参考:https://blog.csdn.net/elpsycongr00/article/details/85222800