剑指offer之3-10题解
前言:
最近开始刷牛客的剑指offer,把刷过的题,代码总结一下。有参考网上的代码,一起加油。
目录
3. 从尾到头打印链表
(一)题目描述
(二) 思路
- 使用栈:栈具有先进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。
(三)代码实现
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while (listNode!=null){
stack.add(listNode.val);
listNode = listNode.next;
}
ArrayList<Integer> list = new ArrayList<>();
while (!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
4. 重建二叉树
(一)题目描述
(二) 思路
- 前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结构,右部分为右子树中序遍历结果。
(三)代码实现(报错,先跳过)
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.HashMap;
import java.util.Map;
public class Solution {
public class Solution {
private Map<Integer,Integer> indexForInOrders = new HashMap<>();
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
for (int i = 0; i < in.length; i++) {
indexForInOrders.put(in[i],i);
}
return reConstructBinaryTree(pre,0,pre.length-1,0);
}
public TreeNode reConstructBinaryTree(int[] pre, int preL,int preR,int inL) {
if (preL>preR)
return null;
TreeNode root = new TreeNode(pre[preL]);
int inIndex = indexForInOrders.get(root.val);
int leftTreeSize = inIndex-inL;
root.left = reConstructBinaryTree(pre,preL+1,preL+leftTreeSize,inL);
root.right = reConstructBinaryTree(pre,preL+leftTreeSize+1,preR,inL+leftTreeSize+1);
return root;
}
}
5. 用两个栈实现队列
(一)题目描述
(二) 思路
- push操作:直接push。
- pop操作:将stack1的数据全部push进stack2,返回stack2栈顶元素即可。
(三)代码实现
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack1.isEmpty() && stack2.isEmpty()) {
throw new RuntimeException("队列为null");
} else if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
6. 旋转数组的最小数字
(一)题目描述
(二) 思路
- 我的思路是直接找到最小值返回即可。代码通过。
- 大佬的思路是通过二分查找算法进行求解(l代表low,m代表mid,h代码high):
- 当nums[m]<=nums[h]时,表示[m,h]区间内的数组是非递减数组,[l,m]区间内是旋转数组,此时令h = m;
- 否则[m+1,h]区间内是旋转数组,令 l =m + 1
(三)代码实现
大佬代码:
public class Solution {
public int minNumberInRotateArray(int[] nums){
if (nums.length==0){
return 0;
}
int l = 0;
int h = nums.length-1;
while (l<h){
int m = l+(h-l)/2;
if (nums[m]<=nums[h]){
h = m;
}else {
l = m+1;
}
}
return nums[l];
}
}
小白代码:
public class Solution {
public int minNumberInRotateArray(int [] array) {
if (array.length==0){
return 0;
}
int minIndex = 0;
for (int i = 1; i < array.length; i++) {
if (array[minIndex]>array[i]){
minIndex = i;
}
}
return array[minIndex];
}
}
7. 斐波那契数列
(一)题目描述
斐波那契数列公式:
(二)思路
- 使用递归求解,但是会重复计算一些子问题。例如,计算f(4)需要计算f(3)和f(2),计算f(3)需要计算f(2)和f(1),可以看到f(2)被重复计算了。
- 动态规划:考虑到第i项只与第i-1和第i-2项有关,因此只需要存储前两项就能求解第i项,从而将空间复杂度由O(N)降为O(1);
(三)代码实现
- 递归版一
public class Solution {
public static int Fibonacci(int n) {
if (n <= 1) {
return n;
}
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
- 递归版二
public class Solution {
public int Fibonacci(int n) {
if (n <= 1) {
return n;
}
int[] fib = new int[n + 1];
fib[1] = 1;
for (int i = 2; i <= n; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
return fib[n];
}
}
- 动态规划版
public class Solution {
public static int Fibonacci(int n) {
if (n <= 1) {
return n;
}
int pre2 = 0;
int pre1 = 1;
int fib = 0;
for (int i = 2; i <= n; i++) {
fib = pre2 + pre1;
pre2 = pre1;
pre1 = fib;
}
return fib;
}
}
8. 跳台阶
(一)题目描述
(二)思路
- 当n=1时,有一种跳法
- 当n=2时,有两种跳法
- 跳n阶台阶,可以先跳1阶台阶,再跳n-1阶台阶;或者先跳2阶台阶,再跳n-2阶台阶。而n-1和n-2阶台阶的跳法可以看成子问题,该问题的递推公式为:
(三)代码实现
- 递归版
public class Solution {
public int JumpFloor(int target) {
if (target <= 2) {
return target;
} else {
return JumpFloor(target - 1) + JumpFloor(target - 2);
}
}
}
- 动态规划版
public class Solution {
public int JumpFloor(int target) {
if (target <= 2) {
return target;
}
int pre1 = 2;
int pre2 = 1;
int result = 0;
for (int i = 3; i <= target; i++) {
result = pre1 + pre2;
pre2 = pre1;
pre1 = result;
}
return result;
}
}
9. 变态跳台阶
(一)题目描述
(二)思路
- 跳上n-1级台阶,可以从n-2级跳1级上去,也可以从n-3级跳2级上去…那么
f(n-1) = f(n-2) + f(n-3) + … + f(0)
- 同样,跳上n级台阶,可以用n-1级跳1级上去,也可以从n-2级跳2级上去…那么
f(n) = f(n-1) + f(n-2) + … + f(0)
- 综上可得
f(n) = 2 * f(n-1)
(三)代码实现
public class Solution {
public int JumpFloorII(int target) {
if (target <= 2) {
return target;
}
return 2 * JumpFloorII(target - 1);
}
}
10. 矩阵覆盖
(一)题目描述
(二)思路
- 当n为1时,只有一种覆盖方法:
- 当n为2时,有两种覆盖方法:
- 要覆盖2n的大矩阵,可以先覆盖21的矩形,再覆盖2*(n-1)的矩形;或者先覆盖22的矩形,再覆盖2(n-2)的矩形。而覆盖2*(n-1)和2*(n-2)的矩形可以看成子问题。该问题的递归公式如下:
(三)代码实现
- 递归版
public class Solution {
public int RectCover(int target) {
if (target <= 2) {
return target;
}
return RectCover(target - 1) + RectCover(target - 2);
}
}
- 动态规划版
public class Solution {
public int RectCover(int target) {
if (target <= 2) {
return target;
}
int pre2 = 1;
int pre1 = 2;
int result = 0;
for (int i = 3; i <= target; i++) {
result = pre1 + pre2;
pre2 = pre1;
pre1 = result;
}
return result;
}
}