Java训练题14

一、选择题

1、递归算法一般需要利用哪种数据结构实现(D)
A. 数组
B. 链表
C. 队列
D. 栈
答案解析:D
递归算法在执行的过程中需要用到栈帧来存储每一次递归的函数信息。
2、以下二叉树前序遍历的顺序是(A )
在这里插入图片描述

A. abefd
B. abdef
C. ebfad
D. efbda
答案解析:A
二叉树前序遍历的规则,先遍历根节点,再遍历左子树,最后遍历右子树;
根节点为a,这时可以排除C和D;左子树的遍历应该在柚子树前面,所以右子树d应该在最后一位,那么最终的遍历顺序为:
abefd
3、已知一棵完全二叉树的第6层(设根为第1层)有8个叶结点,则该完全二叉树的结点个数最多是(C )
A. 39
B. 52
C. 111
D. 119
答案解析:C
根据题意知道,这是一颗完全二叉树,那么前6层的结点是满的,前5层结点的个数为:20+21+…+2^4=63
要求完全二叉树结点最多的情况,那么只能是第六层最右边8个结点是叶子结点,即没有孩子,而第六层从左边开始的24个结点
是有叶子结点的,按照最多的结点计算,第七层结点个数为48;
所以总的结点的个数为前六层结点的个数加上第7层结点的个数63+48=111
4、具有 12 个结点的完全二叉树有(B )
A. 5个叶子结点
B. 5个度为2的结点
C. 7个分支结点
D. 2个度为1的结点
答案解析:B
有n层的满二叉树的结点计算公式为:20+21+…+2^(n-1)
根据该公式可知,这棵二叉树最多只有4层(根结点为第1层),第四层只有4个结点。
所以度为二的节点就为5;
5、深度为 7 的二叉树共有 127 个结点,则下列说法中错误的是( A)
A. 该二叉树有一个度为1的结点
B. 该二叉树是满二叉树
C. 该二叉树是完全二叉树
D. 该二叉树有64个叶子结点
答案解析:A
根据满二叉树的计算公式:20+21+…+2^(n-1)
推导出该二叉树总共有7层,并且每一层结点是满的,所以这棵二叉树的所有结点度都为2,A错误;
满二叉树是一棵特殊的完全二叉树;
因为前面推导出该二叉树是一棵满二叉树,所以二叉树的第7层有64 个结点,即叶子结点为64个;

二、编程题

1、用栈操作构建数组
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。请返回构建目标数组所用的操作序列。题目数据
保证答案是唯一的。OJ链接

示例1:
输入:target = [1,3], n = 3
输出:["Push","Push","Pop","Push"]
解释:
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]
示例2:
输入:target = [1,2,3], n = 3
输出:["Push","Push","Push"]

【解题思路】:
定义 List 集合来存储操作的步骤;定义 Stack 栈来存储元素的值。由于需要从 list = {1,2,3…, n} 中依序读取一个数
字,所以要从 1 到 n 进行遍历并定义 flag 为 target 数组的下标。若 flag < target.length,则就将当前遍历的 i 值直
接压入栈中,并将 " Push " 添加到 list 集合中; 然后判断栈顶元素是否与当前遍历的 target 数组中的元素相同。若
不相同,即 stack.peek() != target[flag],则需要将栈顶元素出栈同时向 list 集合中添加 " Pop ";若相同,则直接令
flag++,进行下一次的比较。最终返回 list 集合即可。

class Solution {
    public List<String> buildArray(int[] target, int n) {
        List<String> list=new ArrayList<>();
        Stack<Integer> stack=new Stack<>();
        int flag=0;
        for(int i=1;i<=n;i++){
            if(flag<target.length){
                list.add("Push");
                stack.push(i);
            }
            if(flag<target.length&&stack.peek()!=target[flag]){
                list.add("Pop");
                stack.pop();
                flag--;
            }
            flag++;
        }
        return list;
    }
}

2、下一个更大元素
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。请你找出 nums1 中每个元素在
nums2 中的下一个比其大的值。nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比x 大的元素。如果不存在,对应位置输出 -1 。
OJ链接

示例1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
   对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
   对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
   对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1

【解题思路】:
当题目出现「找到最近一个比其大的元素」的字眼时,自然会想到「单调栈」。
什么是单调栈?
从名字上就听的出来,单调栈中存放的数据应该是有序的,所以单调栈也分为单调递增栈和单调递减栈
单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大
Java 提供了 Deuqe。Deque 是继承自 Queue,而 Stack 是继承自 Vector。Java 中的 Deuqe,即“double
ended queue”的缩写,是 Java 中的双端队列集合类型。Deque 具备普通队列 FIFO 的功能,同时它也具备了
Stack 的 LIFO 功能,并且保留了 push 和 pop 函数,所以使用起来应该是一点障碍都没有。
ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根
据需求自动进行扩容。ArrayDeque 可以作为栈来使用,效率要高于 Stack。ArrayDeque 也可以作为队列来使
用,效率相较于基于双向链表的 LinkedList 也要更好一些。注意,ArrayDeque 不支持为 null 的元素。
比如我们需要依次将数组 [1,3,4,5,2,9,6] 压入单调栈。

  1. 首先压入 1,此时的栈为:[1]
  2. 继续压入 3,此时的栈为:[1,3]
  3. 继续压入 4,此时的栈为:[1,3,4]
  4. 继续压入 5,此时的栈为:[1,3,4,5]
  5. 如果继续压入 2,此时的栈为:[1,3,4,5,2] 不满足单调递减栈的特性, 因此需要调整。如何调整?由于栈只有pop 操作,因此我们只好不断 pop,直到满足单调递减为止。
  6. 上面其实我们并没有压入 2,而是先 pop,pop 到压入 2 依然可以保持单调递减再 压入 2,此时的栈为:[1,2]
  7. 继续压入 9,此时的栈为:[1,2,9]
  8. 如果继续压入 6,则不满足单调递减栈的特性, 我们故技重施,不断 pop,直到满足单调递减为止。此时的栈为:[1,2,6]
    具体的,由于我们目标是找到某个数其在nums2 的右边中第一个比其大的数,因此我们可以对 nums2 进行逆序遍历。
    我们在遍历nums2 时,实时维护一个单调栈,当我们遍历到元素 nums2[i] 时,可以先将栈顶中比 nums2[i] 小的元
    素出栈,最终结果有两种可能:
  9. 栈为空,说明 nums2[i] 之前(右边)没有比其大的数;
  10. 栈不为空, 此时栈顶元素为 nums2[i]在 nums2 中(右边)最近的比其大的数。
    再利用数组中数值各不相同,在遍历 nums2 的同时,使用哈希表记录每个 nums2[i] 对应目标值是多少即可。
    补充:理解为什么「倒序遍历」,这里的「倒序遍历」是为了「正向解决问题」。 由于我们找的是某个数右边第一个比其大的数,因此我们倒序遍历,可以确保需要找的数已经被处理过。
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n=nums1.length,m=nums2.length;
        Deque<Integer> d=new ArrayDeque<>();
        Map<Integer,Integer> map=new HashMap<>();
        for(int i=m-1;i>=0;i--){
            int x=nums2[i];
            while(!d.isEmpty()&&d.peekLast()<=x){
                d.pollLast();
            }
            map.put(x,d.isEmpty()?-1:d.peekLast());
            d.addLast(x);
        }
        int[] ans=new int[n];
        for(int i=0;i<n;i++){
            ans[i]=map.get(nums1[i]);
        }      
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值