1.剑指 Offer 10- I. 斐波那契数列(递归&循环)
- 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
1.解题思路(递归)——但是会超出时间限制
- 1.斐波那契数列是,从第2项开始,后一项是前两项的加和
f(n) = f(n-1) + f(n-2) ; f(0)=0,f(1)=1,n>1.
class Solution {
public int fib(int n) {
if (n==0) return 0;
if (n==1) return 1;
return (fib(n-1)+fib(n-2))%1000000007;
}
}
2.解题思路(循环)
- 1.因为当前项是相邻前2项的加和,所以可以维持2个实时更新的变量,prepre, pre;
class Solution {
public int fib(int n) {
0.特殊值处理
if (n==0) return 0;
if (n==1) return 1;
int prepre = 0,pre = 1;
int res=0;
1.开启循环
for (int i = 2; i <= n ; i++) {
res = (prepre + pre)%1000000007;
2个变量更新
prepre = pre;
pre = res;
}
2.返回值
return res;
}
}
2.剑指 Offer 10- II. 青蛙跳台阶问题(动态规划)
1.解题思路
- 1.青蛙跳上n级台阶,那么青蛙跳最后一步有两种跳法,跳1级 或者 跳2级;
- 2.青蛙最后一步跳1级,则前边的n-1级有 f(n-1) 种跳法;
- 3.青蛙最后一步跳2级,则前边的n-2级有 f(n-2) 种跳法;
- 4.所以f(n) = f(n-1) + f(n-2) ,又回到了斐波那契数列问题。
2.递归与循环
1.递归解法会超时,因为他会重复求某些解
2.循环解法 则跟上一题思路差不多。
3.剑指 Offer 16. 数值的整数次方
- 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
1.解题思路
1.1.递归思路(会超时)
- 1.考虑递归,次方不就是 乘多少个x,但是可能会有超时的问题;
- 2.优化递归,当次方是2、3、4等数的整数倍时,可以考虑一次性就乘2个x、3个x、4个x。
class Solution {
public double myPow(double x, int n) {
if (n==0) return 1;
if (x==0) return 0;
0.处理负数的情况
if (n<0)
1.为什么这里要 单独乘个x?
return 1/(myPow(x,-n-1)*x);
2.正数的情况
return n%2==0? myPow(x*x,n/2) : myPow(x*x,n/2)*x;
}
}
1.2.循环思路(不优化 会超时)
- 1.优化前
class Solution {
public double myPow(double x, int n) {
0.特殊情况处理
if (x==0 || n==1) return x;
if (n==0) return 1;
0.5 到这里说明n ≠ 0
double res = 1;
1.处理负值
int m = n<0? -1*n :n;
for (int i = 0; i < m; i++) {
res *= x;
}
2.处理负值的情况
return n<0? 1/res :res;
}
}
- 2.优化后(for (int i = n; i!=0; i/=2 ,x*=x) )这部分看不懂
class Solution {
public double myPow(double x, int n) {
0.特殊情况处理
if (x==0 || n==1) return x;
if (n==0) return 1;
到这里说明n!=0
double res = 1;
1.这里看不懂!!!!
for (int i = n; i!=0; i/=2 ,x*=x) {
if (i%2!=0)
res *=x;
}
2.处理负值的情况
return n<0? 1/res :res;
}
}
21. 合并两个有序链表
- 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
1.解题思路
1.循环解法
- 1.新建一个暂时性队首节点prehead,一个动态临时节点prev,循环遍历两条链表L1、L2 的队首节点值,取小的放入即可。
class Solution5 {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
0.新链表的暂时性队首节点prehead
ListNode prehead = new ListNode(-1);
1.动态变化的临时性节点prev
ListNode prev = prehead;
2.当两个链表均不为空时,才能进行节点值比较
并取出较小的节点值放入新链表的下一个
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
3.合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 == null ? l2 : l1;
4.要返回暂时性队首prehead的下一个节点
return prehead.next;
}
}
2.递归解法
- 1.直接把它当成一个功能完备的算法,在这里是它可以把2个升链表给合成1个升序链表;
- 2.当L1.val<L2.val时,说明此时的L1是合并完后总链表的头节点,那么把L1.next 和 L2送入递归,
递归完,就直接返回L1即可。 - 3.当L2.val<=L1.val时,说明此时的L2是合并后的总链表的头节点,那么把L2.next和L1送人递归,
递归结束,就直接返回L2即可。
★★★★★:其实步骤2、3,就是一步步找出各子部分的头结点,然后再连成串!
- 4.特殊情况的处理,在考虑进来即可。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
0.特殊情况的处理
if (l1==null)
return l2;
0.特殊情况的处理
else if (l2==null)
return l1;
1.当l1是头节点时
else if (l1.val<l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
2.当l2是头结点时。
}else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
4. 两数相加(腾讯50题)
两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
1.解题思路
- 1.直接把该方法当成“功能完善的方法”;新建当前递归的头节点head,每个递归都会新建一个head,待递归结束时,所有头节点就会串起来了。
- 2.情况分析:
1. 当前位,2数相加sum <10, 那直接相加即可,不需考虑进位问题;
2. 当前位,2数相加sum>=10,那就取余数存入当前位,可知(sum/10)只能是1,可以把(sum/10=0?)作为一种判断条件,不为0,则向下进位 1.
3. 假设一直以L1.next.val,承接进位,就要考虑L1.next是否为空的问题,为空则new出新节点,并存入进位1即可。 - 3.特殊情况的处理:
1.L1、L2都是null,很简单,直接返回null;
2.L1为null,L2不为null,但是L2.val>=10,此时还需要继续处理,需要new一个新的L1,只不过节点值为0。
3.L1为null,L2不为null,且L2.val<10,此时直接返回L2即可。
4.L2为null的情况与L1相同,不再赘述。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
0.特殊情况处理
if (l1==null && l2==null) return null;
0.1.说明l2不需要进位处理,直接return
if (l1==null && l2.val<10) return l2;
0.2.说明l2的节点值>=10,需要处理进位
此时l1虽然为空,但还是要参与接下来的处理,所以new出新的l1,节点值设为0
if (l1==null) l1=new ListNode(0);
0.3.这种与2相同,不再赘述
if (l2==null && l1.val<10) return l1;
if (l2==null) l2=new ListNode(0);
1. 建立头节点,取%10余数 作为新的头节点的值
int num = (l1.val+ l2.val)%10;
ListNode head = new ListNode(num);
2.当前节点需要进位时
if ((l1.val+ l2.val)/10!=0) {
2.1.考虑l1.next是否为空的问题
if (l1.next != null)
l1.next.val = l1.next.val + 1;
else
l1.next = new ListNode(1);
}
3.调用递归
head.next = addTwoNumbers(l1.next, l2.next);
return head;
}
}