目录
一、重建二叉树
解题思路:
由于在HashMap中,每个键值对都会映射到一个唯一的哈希值,且HashMap允许有空键和空值。而本题中无重复值的元素,且二叉树中会有空值,所以利用HashMap来存储树中元素。
前序遍历:根 ——> 左 ——> 右
1 , 2 , 4 , 7 , 3 , 5 , 6 , 8
l1 l1+1 l1+(i-l2) l1+(i-l2)+1 r1
中序遍历:左 ——> 根 ——> 右
4 , 7 , 2 , 1 , 5 , 3 , 8 , 6
l2 i-1 i i+2 r2
这是我们可以得到两个更小的子集
左子树前序遍历:2,4,7 左子树中序遍历:4,7,2
右子树前序遍历:3,5,6,8 右子树中序遍历:5,3,8,6
在这道题中,前序遍历数组preOrder中的第一个元素为根节点的值,而中序遍历数组vinOrder中根节点的值在数组中间位置。因此,在建立二叉树的过程中,需要根据中序遍历数组中根节点的位置来划分左子树和右子树的节点个数,然后递归地构建左子树和右子树。
在递归构建左子树和右子树的过程中,需要根据前序遍历数组preOrder和中序遍历数组vinOrder的范围来确定每个子树的根节点和子树的节点个数。通过不断递归构建左右子树,最终可以得到完整的二叉树结构。
因此,在代码中采用中序遍历的方式来构建二叉树,根据中序遍历数组中根节点的位置将其分为左右子树,然后递归构建左子树和右子树。最终返回根节点即可得到完整的二叉树。
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param preOrder int整型一维数组
* @param vinOrder int整型一维数组
* @return TreeNode类
*/
Map<Integer, Integer> map = new HashMap();
public TreeNode reConstructBinaryTree (int[] preOrder, int[] vinOrder) {
if(preOrder == null || preOrder.length <= 0){
return null;
}
for(int i = 0; i < vinOrder.length ;++i){
map.put(vinOrder[i], i);
}
TreeNode root = f(preOrder,0,preOrder.length - 1,vinOrder,0,vinOrder.length - 1);
return root;
}
TreeNode f(int[] preOrder,int l1,int r1,int[] vinOrder,int l2, int r2){
if( l1 > r2 && l2 >r2){
return null;
}
TreeNode root = new TreeNode(preOrder[l1]);//前序遍历最左面第一个元素是根结点,获取根结点的值
int i = map.get(preOrder[l1]);//获取根节点下标
root.left = f(preOrder, l1 + 1, l1 + (i - l2), vinOrder, l2, i - 1);
root.right = f(preOrder, l1+(i - l2)+1, r1,vinOrder, i+1, r2);
return root;
}
}
二、用两个栈实现队列
解题思路:
其中stack1
用于入队操作,stack2
用于出队操作。
入队操作:将元素压入stack1
中即可。
出队操作:
- 如果
stack2
为空,将stack1
中的所有元素弹出并压入stack2
中,然后弹出stack2
的栈顶元素即可。 - 如果
stack2
不为空,直接弹出stack2
的栈顶元素即可。
import java.util.*;
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
//如果stack2为空,将stack1中的所有元素弹出并压入stack2中,然后弹出.
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
三、斐波那契数列
斐波那契数列可以用多种不同的解题思路来实现。以下是几种常见的解题思路:
-
递归:这种方法简单明了,但由于递归会重复计算相同的结果,效率较低,容易导致性能问题。
-
动态规划:动态规划是将问题分解成更小的子问题,并保存子问题的解,以避免重复计算。这种方法效率较高,适用于大规模计算。
-
递推:通过一个或多个初始条件以及一定的规则来逐步推导出问题的解,递推通常用于描述数列或函数的规律
-
矩阵快速幂算法:这种方法的时间复杂度为O(logn),效率非常高,适用于大规模计算。
解题思路一:递归 + 动态规划
由于递归会重复计算相同的结果,所以创建一个数组来保存 f(n) 的值,所以每次计算之前判断一下 f(n) 是否计算过,若之前计算过,直接返回即可。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return int整型
*/
int[] arr = new int[41]; //数组空间开辟为41,是因为n的数据范围<=40,数组下标从0开始,所以需要开辟一个41的数组空间。
public int Fibonacci (int n) {
if (n <= 1) return n;
if (arr[n] != 0) return arr[n];
arr[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
return arr[n];
}
}
时间复杂度O(n), 空间复杂度O(C)
解题思路二:递推
使用三个变量,从前往后算一遍即可。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return int整型
*/
public int Fibonacci (int n) {
if (n <= 1) {
return n;
}
int a = 0, b = 1, c = 0;
for(int i = 2;i <= n; ++i){
c = a + b;
a = b;
b = c;
}
return b;
}
}
时间复杂度O(n), 空间复杂度O(1)
解题思路三(字节考题):矩阵快速幂
什么是快速幂?
快速幂是一种用于计算幂运算的优化算法,其主要思想是利用指数的二进制形式来减少乘法操作次数,从而提高计算效率。
具体而言,快速幂算法将指数n表示为其二进制形式,然后通过递归或迭代的方式将幂运算拆解成若干个平方和乘法的运算,以实现快速求解幂运算的目的。该算法的时间复杂度为O(logn),远远优于朴素的幂运算算法时间复杂度为O(n)。
矩阵快速幂是一种用来快速计算矩阵的高次幂的方法,特别适用于计算大整数的幂次运算。它在很多领域有着广泛的应用,比如图论、数论、组合数学、动态规划等。以下是一些矩阵快速幂的解题应用:
在动态规划中的应用:矩阵快速幂可以用来优化某些动态规划算法的时间复杂度,比如: Fibonacci 数列的计算、矩阵的乘法、字符串的匹配等问题。
在图论中的应用:矩阵快速幂可以用来计算图的邻接矩阵的高次幂,从而求解图的路径计数、最短路径等问题。
在数论中的应用:矩阵快速幂可以用来计算大整数的幂次运算,比如:计算超大素数的高次幂。
在密码学中的应用:矩阵快速幂可以用来加速计算密码算法中的某些复杂运算,比如:RSA 加密算法中的幂次运算。
计算 = ?
首先,将十进制数b,用二进制来表示。
以 b = 11为例,
所以想要计算,我们只需计算四个数:,,,
, ,
所以只需要知道 即a,便可以求出,甚至。
根据题目的递推关系fib(x) = fib(x - 1) + fib(x - 2)。
最终得到
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return int整型
*/
int[][] mul(int[][] a, int[][] b) { //矩阵乘法
int r = a.length, c = b[0].length, z = b.length;
int[][] ans = new int[r][c];
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
for (int k = 0; k < z; k++) {
ans[i][j] += a[i][k] * b[k][j];
}
}
}
return ans;
}
public int Fibonacci(int n) {
if (n <= 1) return n;
int[][] M = new int[][]{
{0, 1},
{1, 1}
};
int[][] ans = new int[][]{
{0},
{1}
};
int x = n - 1; //矩阵M的次幂数
while (x != 0) {
if ((x & 1) != 0) //次幂数为奇数
ans = mul(M, ans);
M = mul(M, M); //次幂数为偶数
x >>= 1;
}
return ans[1][0];
}
}
时间复杂度O(logn), 空间复杂度O(1)
总结一下:这道题看似是个入门级别的题目,但解法很多,涉及到的知识点很多,需要严谨的数学逻辑进行反复推敲。
这是我当时做这道题时提交的次数,希望你们也能多试着用不同方法写几遍,并能从中有所收获。
感谢您的观看,希望你今天特别特别开心,加油~