0
点赞
收藏
分享

微信扫一扫

暴躁算法(剑指系列)-每日练习4.15

未定义变量 2022-04-16 阅读 23

每日练习-2022.4.15

2022.4.15

剑指 Offer 06. 从尾到头打印链表

难度简单

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

思路:

  • 建立一个数组,倒着遍历数组,顺着遍历链表,达到反转的作用。(会浪费空间)

  • 使用arraylist,每遍历一个节点,add一次。最后建立一个数组,反着遍历。(对于数据量较小的数据会更加节省空间)。

    arraylist转化为int数组

这俩一个时间超时,一个空间超时

算是意料之中。

所以:

  • 使用栈,先进后出,达到反转的目的

    执行结果:通过

    执行用时:1 ms, 在所有 Java 提交中击败了69.83%的用户

    内存消耗:41.9 MB, 在所有 Java 提交中击败了41.54%的用户

    斯哈——。

public int[] reversePrint(ListNode head) {
   //建立一个栈,存放节点
       Stack<ListNode> stack = new Stack<ListNode>();
       ListNode cur = head;
   //依次push
       while (cur != null) {
           stack.push(cur);
           cur = cur.next;
       }
   //用栈的大小创建数组,避免空间浪费
       int size = stack.size();
       int[] ans = new int[size];
   //依次弹出去,pop返回值存入数组
       for (int i = 0; i < size; i++) {
           ans[i] = stack.pop().val;
       }
       return ans;
   }

栈和递归通常相关联(递归的本质就是栈)

递归实现:

    int[] res;
    int i = 0;
    int j = 0;
    public int[] reversePrint(ListNode head) {
        solve(head);
        return res;
    }
	public void solve(ListNode head){
        if(head == null){
            res = new int[i];
            return;
        }
        i++; 
        solve(head.next);
        res[j] = head.val;
        j++;
    }

执行结果:通过

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:42.2 MB, 在所有 Java 提交中击败了8.48%的用户


阿巴阿巴阿巴

剑指 Offer 10- I. 斐波那契数列

难度简单342

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

非递归与递归两个写法

public int fib(int n) {
        if(n==0){
            return 0;
        }
        if(n==1 || n==2){
            return 1 ;
        }
        long[] fbs = new long[n+1];
        fbs[0] = 0L;
        fbs[1]=1L;
        fbs[2]=1L;
        for(int i =3;i<fbs.length;i++){
            fbs[i]=fbs[i-1]+fbs[i-2];
        }
        return (int)(fbs[n]%1000000007);
    }
//递归算法
    public static int fb2(int n){
        if(n==2||n==1){
            return 1;
        }
        return fb2(n-1)+fb2(n-2);
    }
  • 实际运行中,递归写法会栈溢出,不推荐,而且会有很多重复计算。

  • 非递归写法记得要判断特殊情况,n<2的时候

  • 数组越界问题,long只能满足90左右的n值,所以,可以考虑先对1000000007求模后再去存储

    这点很重要

    public int fib(int n) {
            if(n==0){
                return 0;
            }
            if(n==1 || n==2){
                return 1 ;
            }
            long[] fbs = new long[n+1];
            fbs[0] = 0L;
            fbs[1]=1L;
            fbs[2]=1L;
            for(int i =3;i<fbs.length;i++){
                fbs[i]=(fbs[i-1]+fbs[i-2])%1000000007;
            }
            return (int)fbs[n];
        }
    

    这是考虑到数组越界的问题后的算法。

    这样就可以通过所有的样例了

    执行结果:

    通过

    执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

    内存消耗:38.5 MB, 在所有 Java 提交中击败了5.33%的用户

    这样数组的内存开销会很大,所以需要进一步优化:
    我们只存储3个数,pre,cur,next,每一次计算新的值,就更迭以下数字。

     public int fib(int n) {
            if(n==0){
                return 0;
            }
            if(n==1 || n==2){
                return 1 ;
            }
            long pre = 1L;
            long cur = 1L;
            long next = 2L;
            long temp;
            while(n>3){
                temp =(cur+next)%1000000007;
                pre = cur;
                cur = next;
                next=temp;
                n--;
            }
            return (int)next;
        }
    

    优化之后明显好了很多

    执行结果:

    通过

    执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

    内存消耗:37.9 MB, 在所有 Java 提交中击败了72.40%的用户

  • 再次优化思路:这样交替更新显得就很耿直。。可以往轮转方向去

剑指 Offer 07. 重建二叉树

难度中等

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

思路:根据前序与中序遍历的顺序,重新构造二叉树。并且进行输出。

重构二叉树的方法:

前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。

遍历数组:找根节点的左孩子,找到根节点的右孩子。

分别把根节点的左孩子与右孩子当作新的根节点。

(这里很明显可以用递归。很明显,对吧对吧)

。先贴一个官解

class Solution {
    private Map<Integer, Integer> indexMap;

    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return null;
        }

        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);
        
        // 先把根节点建立出来
        TreeNode root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造哈希映射,帮助我们快速定位根节点
        indexMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
}

太困了,自己的代码差点儿,明天在写。。。
狗命要紧。

举报

相关推荐

0 条评论