编程题(二)

目录

一、重建二叉树

二、用两个栈实现队列

三、斐波那契数列

一、重建二叉树

点击这里去做题

解题思路:

由于在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中即可。

出队操作:

  1. 如果stack2为空,将stack1中的所有元素弹出并压入stack2中,然后弹出stack2的栈顶元素即可。
  2. 如果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();
    }
}

三、斐波那契数列

点击这里去做题

斐波那契数列可以用多种不同的解题思路来实现。以下是几种常见的解题思路:

  1. 递归:这种方法简单明了,但由于递归会重复计算相同的结果,效率较低,容易导致性能问题。

  2. 动态规划:动态规划是将问题分解成更小的子问题,并保存子问题的解,以避免重复计算。这种方法效率较高,适用于大规模计算。

  3. 递推:通过一个或多个初始条件以及一定的规则来逐步推导出问题的解,递推通常用于描述数列或函数的规律

  4. 矩阵快速幂算法:这种方法的时间复杂度为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)。

矩阵快速幂是一种用来快速计算矩阵的高次幂的方法,特别适用于计算大整数的幂次运算。它在很多领域有着广泛的应用,比如图论、数论、组合数学、动态规划等。以下是一些矩阵快速幂的解题应用:

  1. 在动态规划中的应用:矩阵快速幂可以用来优化某些动态规划算法的时间复杂度,比如: Fibonacci 数列的计算、矩阵的乘法、字符串的匹配等问题。

  2. 在图论中的应用:矩阵快速幂可以用来计算图的邻接矩阵的高次幂,从而求解图的路径计数、最短路径等问题。

  3. 在数论中的应用:矩阵快速幂可以用来计算大整数的幂次运算,比如:计算超大素数的高次幂。

  4. 在密码学中的应用:矩阵快速幂可以用来加速计算密码算法中的某些复杂运算,比如:RSA 加密算法中的幂次运算。

计算 _{_{}^{}}^{}a^{^{b}} = ?

首先,将十进制数b,用二进制来表示。

以 b = 11为例,11 = 2^{3}*1+2^{2}*0+2^{1}*1+2^{0}*1

a^{11_{(10)}} = a^{1011_{(2)}} = a^{1*2^{3}+0*2^{2}+1*2^{1}+1*2^{0}}

所以想要计算a^{11},我们只需计算四个数:a^{2^{3}}a^{2^{2}}a^{2^{1}}a^{2^{0}}

a^{2^{0}} * a^{2^{0}} = a^{2^{1}}, a^{2^{1}} * a^{2^{1}} = a^{2^{2}}, a^{2^{2}} * a^{2^{2}} = a^{2^{3}}

所以只需要知道 a^{2^{0}} 即a,便可以求出a^{11},甚至a^{b}

根据题目的递推关系fib(x) = fib(x - 1) + fib(x - 2)。

解题思路讲解,一定要看!!!

最终得到 \begin{bmatrix} f(n - 1)\\ f(n)\end{bmatrix} = M^{n - 1} * \begin{bmatrix} f(0)\\ f(1)\end{bmatrix}

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)

总结一下:这道题看似是个入门级别的题目,但解法很多,涉及到的知识点很多,需要严谨的数学逻辑进行反复推敲。

这是我当时做这道题时提交的次数,希望你们也能多试着用不同方法写几遍,并能从中有所收获。

感谢您的观看,希望你今天特别特别开心,加油~

最常见的Java面试题大全的程序面试程序,很有影响力的哦!好好珍惜这个资料吧!有Java的常见面试题的冒泡,常见的算法,继承,多态 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,是数据抽象。 2.继承:  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装:  封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性:  多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值