【剑指OFFER】——Java实现(面试题53-59)

本文深入解析了多个经典算法问题,包括在排序数组中查找数字、寻找数组中唯一出现的数字、求解滑动窗口最大值等,提供了多种解题思路,如二分查找、异或操作、滑动窗口算法等,旨在帮助读者理解和掌握高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试题53:在排序数组中查找数字
题目一:数字在排序数组中出现的次数。
解题方案1:常规解法,从头遍历;
解题方案2:二分查找法,若数字小于中间数字然后在左边继续查找,若大于在右边继续查找,若相等就可以向前向后遍历出现的次数了。
题目二:0~n-1中缺失的数字。
解题方案1:汇总数组中所有数字之和,汇总0~n-1中所有数字之和,两者之差就是缺失值。
解题方案2:二分查找,若中间数字不等于下标,判断它的前一个数字是否和下标相等,相等则此下标就是缺失值,不等继续在左边查找;若中间数字等于下标在右边继续查找。
题目三:数组中数值和下标相等的元素。
解题方案:二分查找,若中间数字等于下标则找到了元素,若大于在左边查找,若小于在右边查找。

面试题54:二叉搜索树的第K大节点
解题方案:中序遍历到K个元素。

面试题55:二叉树的深度
题目一:二叉树的深度。
解题方案:前序遍历。

    private int getNodeLength1(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int left = getNodeLength1(node.getLeft());
        int right = getNodeLength1(node.getRight());
        return left > right ? left + 1 : right + 1;
    }

题目二:平衡二叉树,输入根节点,判断该树是不是平衡二叉树。
解题方案1:递归

    public void fiftyFifth1() {
        TreeNode rootNode = TreeNode.createSelectTree();
        System.out.println(isBalance(rootNode));
    }

    private boolean isBalance(TreeNode node) {
        if (node == null) {
            return true;
        }
        int left = getNodeLength1(node.getLeft());
        int right = getNodeLength1(node.getRight());
        if (Math.abs(left - right) > 1) {
            return false;
        }
        return isBalance(node.getLeft()) && isBalance(node.getRight());
    }

    private int getNodeLength1(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int left = getNodeLength1(node.getLeft());
        int right = getNodeLength1(node.getRight());
        return left > right ? left + 1 : right + 1;
    }

解题方案2:非递归

    public void fiftyFifth2() {
        TreeNode rootNode = TreeNode.createSelectTree();
        getNodeLength2(rootNode);
        System.out.println(flag);
    }

    private static boolean flag = true;

    private static int getNodeLength2(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int left = getNodeLength2(node.getLeft());
        if (flag) {
            int right = getNodeLength2(node.getRight());
            if (flag) {
                flag = Math.abs(left - right) <= 1;
                return left > right ? left + 1 : right + 1;
            }
        }
        return 0;
    }

面试题56:数组中数字出现的次数
题目一:数组中只出现一次的两个数字,其他数字均出现两次,要求时间复杂度为O(n),空间复杂度为O(1)。
解题方案:将所有数字进行一次异或,可以得到这两个数字的异或值。找到这个异或值转二进制后的低位1的位置,将数组按着这个低位1的位置是否为1分为两个子数组,然后对这两个数组分别进行异或,即可得到两个只出现一次的数字。

    public void fiftySixth() {
        int[] arrays = new int[]{1, 2, 3, 4, 55, 66, 55, 66, 5, 1, 2, 3};
        findNumberApperOnce(arrays);
    }

    private void findNumberApperOnce(int[] arrays) {
        if (arrays == null) {
            return;
        }
        int result = 0;
        for (int i = 0; i < arrays.length; i++) {
            // 异或
            result ^= arrays[i];
        }
        // result为两个数异或的结果,从低位开始找到第一次出现的1所在的位
        int bit = 1;
        while ((result & bit) == 0 && bit <= result && bit > 0) {
            bit = bit << 1;
        }
        int number1 = 0;
        int number2 = 0;
        for (int i = 0; i < arrays.length; i++) {
            if ((arrays[i] & bit) == bit) {
                number1 ^= arrays[i];
            } else {
                number2 ^= arrays[i];
            }
        }
        System.out.println(number1);
        System.out.println(number2);
    }

题目二:数组中唯一只出现一次的数字,其他数字均出现三次,要求时间复杂度为O(n),空间复杂度为O(1)。
解题方案:把所有数字按照数字转二进制的位数分别进行累加,每位的和如能被三整除说明那个数字在这一位是0,否则是1。

面试题57:和为S的数字
题目一:和为S的两个数字,输入一个递增排序的数组,输出任意一对即可。
解题方案:定义一个指针指向开头,定义另一个指针指向末尾,两个指针逐渐向中间靠拢。
题目二:和为S的连续正整数序列,输入一个正整数,打印所有和为S的连续正数序列(至少包含两个数字)
解题方案:也是定义两个指针,一个指向1,一个指向2,判断两个指针之间数字之和,小于S将指针2向后移动一位,大于将指针1向后移动一位,相等就打印并将指针1向后移动一位继续判断,一直将指针向后移动到(S+1)/2为止。

    public void fiftySeventh() {
        findNumber(9);
        System.out.println("--------");
        findNumber(15);
    }

    private void findNumber(int sum) {
        if (sum < 3) {
            return;
        }
        int mid = sum >> 1;
        int begin = 1;
        int end = 2;
        int temp = begin + end;
        while (begin <= mid) {
            if (temp == sum) {
                printNum(begin, end);
                end++;
                temp += end;
            } else if (temp < sum) {
                end++;
                temp += end;
            } else if (temp > sum) {
                temp -= begin;
                begin++;
            }
        }
    }

    private void printNum(int begin, int end) {
        for (int i = begin; i <= end; i++) {
            System.out.print(i);
        }
        System.out.println();
    }

结果:

234
45


12345
456
78

面试题58:翻转字符串
题目一:翻转单词顺序,输入“I am a student.” 输出 “student. a am I”
解题方案:先反转整个句子,然后再以空格隔开句子,分别再次翻转单词。
题目二:左旋转字符串,输入“abcdefg”,左旋两位,输出“cdefgab”。
解题方案:前两位翻转,后面的数字也翻转,得到“bagfedc”,再翻转这个字符串。

面试题59:队列的最大值
题目一:滑动窗口的最大值。利用一个辅助双向队列存储数组的下标,向右移动一格,将新数据入队列,并移除队列中的元素(超过窗口值的元素、小于新数据的元素)
题目二:队列的最大值,要求push、pop、max时间复杂度都是O(1)。
解题方案:利用一个辅助队列,push时将队列中所有小于插入值的元素出辅助队列,然后把插入值入辅助队列;pop时,若辅助队列尾值等于pop值,就将尾值出辅助队列。

面试题64:求1+2+3+…+n
题目:不能使用循环等条件表达式。
解题方案:递归循环,无法解决输入值小于0的情况

    public void sixthForth() {
        System.out.println(getSum(100));
    }

    private int getSum(int num) {
        int sum = num;
        boolean flag = num > 1 && (sum += getSum(num - 1)) > 0;
        return sum;
    }
资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 HttpServletRequestWrapper 是 Java Servlet API 中的一个工具类,位于 javax.servlet.http 包中,用于对 HttpServletRequest 对象进行封装,从而在 Web 应用中实现对 HTTP 请求的拦截、修改或增强等功能。通过继承该类并覆盖相关方法,开发者可以轻松地自定义请求处理逻辑,例如修改请求参数、添加请求头、记录日志等。 参数过滤:在请求到达处理器之前,可以对请求参数进行检查或修改,例如去除 URL 编码、过滤敏感信息或进行安全检查。 请求头操作:可以修改或添加请求头,比如设置自定义的 Content-Type 或添加认证信息。 请求属性扩展:在原始请求的基础上添加自定义属性,供后续处理使用。 日志记录:在处理请求前记录请求信息,如 URL、参数、请求头等,便于调试和监控。 跨域支持:通过添加 CORS 相关的响应头,允许来自不同源的请求。 HttpServletRequestWrapper 通过继承 HttpServletRequest 接口并重写其方法来实现功能。开发者可以在重写的方法中添加自定义逻辑,例如在获取参数时进行过滤,或在读取请求体时进行解密。当调用这些方法时,实际上是调用了包装器中的方法,从而实现了对原始请求的修改或增强。 以下是一个简单的示例,展示如何创建一个用于过滤请求参数的包装器: 在 doFilter 方法中,可以使用 CustomRequestWrapper 包装原始请求: 这样,每当调用 getParameterValues 方法时,都会先经过自定义的过滤逻辑。 HttpServletRequestWrapper 是 Java Web 开发中一个强大的工具,它提供了灵活的扩展性,允许开发者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值