数据结构与算法分析--Java语言描述(第二章(2))

2023-11-08

习题2.23

不用递归,写出快速求幂的程序。

    /**
     * 递归方法
     *
     * @param x
     * @param n
     * @return
     */
    public long pow(long x,int n){
        if(n == 0){
            return 1;
        }
        if(isEven(n)){
            return pow(x * x,n /2);
        }else{
            return pow(x ,n-1) * x;
        }
    }

    /**
     * 非递归方法
     *
     * @param x
     * @param n
     * @return
     */
    public long pow2(long x,int n){
        long pow = 1;
        while(n > 0){
            if(!isEven(n)){
                pow *= x;
            }
            x *= x;
            n = n / 2;
        }
        return pow;
    }

    /**
     * 判断奇偶数
     *
     * @param n
     * @return
     */
    private static boolean isEven(int n) {
        if((n&1) == 0){
            return true;
        }else{
            return false;
        }
    }

习题2.26

大小为N的数组A,其主元素是一个出现超过N/2次的元素(从而这样的元素最多有一个)。例如,数组
3,3,4,2,4,4,2,4,4
有一个主元素4, 而数组
3,3,4,2,4,4,2,4
没有主元素。如果没有主元素, 那么你的程序应该指出来。下面是求解该问题的一个算法的概要:
首先,找出主元素的一个候选元(这是困难的部分)。这个候选元是唯一有可能是主元素的元素。第二步确定是否该候选元实际上就是主元素。
这正好是对数组的顺序搜索。为找出数组A的一个候选元,构造第二个数组B。比较A1和A2。如果它们相等,则取其中之一加到数组B中;
否则什么也不做。然后比较A3和A4,同样,如果它们相等,则取其中之一加到B中;否则什么也不做。
以该方式继续下去直到读完整个的数组。然后,递归地寻找数组B中的候选元;它也是A的候选元(为什么)。

    /**
     * 判断x是否为数组主元素
     *
     * @param A
     * @param N
     * @param x
     * @return
     */
    public boolean isMainElement(int[] A,int N,int x){
        int count = 0;
        for(int i = 0; i < N; i++){
            if(A[i] == x){
                count++;
            }
        }
        if(count > N/2){
            return true;
        }
        return false;
    }

    /**
     * 方法一:递归:
     * 构建数组B,比较A_1,A_2,如果相等则任取其一加到数组B;否则什么都不做。然后比较A_3,A_4,同样,如果相等则任取其一加到数组B;否则什么都不做。
     * 以该方式继续下去直到读完整个数组。然后递归的寻找数组B中的候选元。
     *
     * @param A
     * @param B
     * @param N:数组A长度
     * @param N2:数组B长度
     * @return
     */
    public int findMainElement(int[] A,int[] B,int N,int N2){
        //基准:当数组B中只有1个或0个元素时停止。
        if(N == 0){
            return Integer.MIN_VALUE;
        }
        if(N == 1){
            if(isMainElement(B,N2,A[0])){
                return A[0];
            }else{
                return Integer.MIN_VALUE;
            }
        }
        int count = 0;
        //遍历数组A
        for(int i = 0; i < N - 1; i++){
            if(A[i] == A[i + 1]){
                B[count++] = A[i];
            }
        }
        int res =findMainElement(B,A,count,N);

        //奇数情况:先不考虑最后一个元素,如果前面都没出现主元素,则把最后一个候选元再次进行判断是否为主元素
        if(res == Integer.MIN_VALUE && (N&1) == 1 && isMainElement(A,N,A[N - 1])){
            return A[N - 1];
        }
        return res;
    }

方法二:避免使用附加数组B。

设该数出现的次数为x,则x满足n/2+1<=x<=n;所以可以想象如果该数和其余的数全部相抵消的话,至少还剩1个。我们可以从前往后遍历,设key为第一个数,key出现的次数为count,初始化为1,代表key出现了一次。从前往后,如果某个数不等于key,则它俩抵消,key出现的次数减一;如果等于key,则key出现的次数加1,如果key出现的次数变成了0,则说明key已经用完了,所以需要重新初始化key为另一个数。重复以上步骤,因为一定有一个数大于n/2,所以遍历到最后剩下的数则是要求的数。

    /**
     * 方法二:避免使用附加数组B
     *
     * @param A
     * @return
     */
    public int getMainElement(int[] A){
        int count = 1;
        int result = A[0];
        for(int i = 1; i < A.length; i++){
            if(count == 0){
                result = A[i];
            }else{
                if(result == A[i]){
                    count++;
                }else{
                    count--;
                }
            }
        }
        //无论是否存在主元素,遍历完后的X一定是主元素的候选元。
        //因此如果不确定是否存在主元素,则再次遍历数组,判断X出现的次数是否大于N/2即可。
        boolean isMainEle = isMainElement(A, A.length, result);
        if(isMainEle){
            return result;
        }else {
            return Integer.MIN_VALUE;
        }
    }

习题2.27

输入是一个N×N的数字矩阵并且已经读入内存。每一行均从左到右增加。每一列则从上到下增加。给出一个O(N)最坏情形算法以确定数X是否在矩阵中。

思路:

最好的算法是去矩阵的最右下角或最左上角的元素,该元素在所在列和行为鞍点,即行最大列最小或者列最大行最小。
例如,取最左下角的元素并记为a, 如果a>k,表明a所在的行都大于a,肯定不在此行上,只能往列去寻找;
如果a<k表明a所在的列都小于k,因此肯定不在此列上,,如此计算, 初始点为a[n-1][0],结束条件为i<0或者j>n。

    /**
     * @param arr
     * @param N
     * @param searchKey
     * @return
     */
    public boolean findIfExist(int[][] arr,int N,int searchKey){
        int i = N - 1,j = 0;
        while(i >=0 && j <= N - 1){
            if(arr[i][j] > searchKey){
                i--;
            } else if(arr[i][j] < searchKey){
                j++;
            } else {
                return true;
            }
        }
        return false;
    }

习题2.28

使用正数的数组a设计有效的算法以确定:
 其中j >= i,
a[j] + a[i]的最大值
a[j] - a[i]的最大值
a[j] * a[i]的最大值
a[j] / a[i]的最大值

     /**
     * 方法一:双层for循环,时间复杂度O(N^2)
     *
     * @param a
     * @param operator
     * @return
     */
    public static int getMaxValue(int[] a, String operator) {
        int maxValue = Integer.MIN_VALUE;
        for (int i = 0; i < a.length; i++) {
            int thisValue = 0;
            for (int j = i + 1; j < a.length; j++) {
                switch (operator) {
                    case "+":
                        thisValue = a[j] + a[i];
                        break;
                    case "-":
                        thisValue = a[j] - a[i];
                        break;
                    case "*":
                        thisValue = a[j] * a[i];
                        break;
                    case "/":
                        thisValue = a[j] / a[i];
                        break;
                    default:
                        return Integer.MIN_VALUE;
                }
                if (thisValue > maxValue) {
                    maxValue = thisValue;
                }
            }
        }
        return maxValue;
    }

拓展问题:使用正数的数组a设计有效的算法以确定:其中j >= i,a[j] - a[i]的最大值。

解题思路:

如果当前数作为被减数,那么后面的数和它相减要取得最大值应该拿后面最大的那个数和它减,扫描到当前数后面的最大值可以记录下来。记后面某个数减去当前值的最大值为max,而数组中当前值左边的数的最小值为minLeft。每扫描一个数,计算当前数减去minLeft,并与max进行比较。如果大则更新max,并更新左边部分的数的最小值minLeft。

     /**
     * a[j] - a[i]的最大值,其中j>=i
     * 时间复杂度O(N)
     *
     * @param a
     * @return
     */
    public static int getMinusMax(int[] a) {
        if (a == null || a.length == 0) {
            return Integer.MIN_VALUE;
        }
        if (a.length == 1) {
            return a[0];
        }
        int max = a[1] - a[0];
        int minLeft = a[0];
        for (int i = 1; i < a.length; i++) {
            int temp = a[i] - minLeft;
            if (temp > max) {
                max = temp;
            }
            if (a[i] < minLeft) {
                minLeft = a[i];
            }
        }
        return max;
    }

因此,上面的四种运算最大值,也可以换一种方法解决。首先创建一个枚举类OPerator,内容如下:

/**
 * @description: 运算符
 * @author: 
 * @date: 2018/8/1
 */
@Getter
public enum Operator {
    PLUS("+","加"),
    MINUS("-","减"),
    MULTI("*","乘"),
    DIV("/","除")
;

    private String code;
    private String desc;

    Operator(String code,String desc){
        this.code = code;
        this.desc = desc;
    }
}
    /**
     * 方法二:
     *
     * @param a
     * @return
     */
    public static int getPlusMax(int[] a,String operator) {
        if(a == null || a.length == 0){
            return Integer.MIN_VALUE;
        }
        if(a.length == 1){
            return a[0];
        }
        int max;
        //根据传入的运算符进行操作
        if(Operator.PLUS.getCode().equals(operator)){
            max = a[1] + a[0];
        } else if(Operator.MINUS.getCode().equals(operator)){
            max = a[1] - a[0];
        } else if(Operator.MULTI.getCode().equals(operator)){
            max = a[1] * a[0];
        } else{
            max = a[1] / a[0];
        }
        int left = a[0];
        int temp;
        for(int i = 1; i < a.length; i++){
            //根据传入的运算符进行操作
            if(Operator.PLUS.getCode().equals(operator)){
                temp = a[i] + left;
            } else if(Operator.MINUS.getCode().equals(operator)){
                temp = a[i] - left;
            } else if(Operator.MULTI.getCode().equals(operator)){
                temp = a[i] * left;
            } else{
                temp = a[i] / left;
            }

            if(temp > max){
                max = temp;
            }

            if(Operator.PLUS.getCode().equals(operator) || Operator.MULTI.getCode().equals(operator)){
                if(a[i] > left){
                    left = a[i];
                }
            } else if(Operator.MINUS.getCode().equals(operator) || Operator.DIV.getCode().equals(operator)){
                if(a[i] < left){
                    left = a[i];
                }
            }
        }
        return max;
    }

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构与算法分析--Java语言描述(第二章(2)) 的相关文章

  • JAVA三种多数据源配置详解(一)

    在大型项目的开发中 我们可能因为微服务 分布式 集群等架构的影响 而需要到不同的数据库中去查询需要对应的数据 这时 单一的数据库配置就无法满足业务需求 下面我会介绍集中不同场景下的多数据源配置 大家可以根据自身情况进行选择和实现 yml文件
  • 纪念古龙诞辰:论古龙的江湖为何没有一“tong”?

    古龙 nbsp 原名熊耀华 1938年6月7日生于香港 武侠小说家 新派武侠小说泰斗 代表作品 多情剑客无情剑 绝代双骄 楚留香传奇 武林外史 等 有人喜欢金庸 也有很多人喜欢古龙 他们之间的同与不同 以及那些至今仍然令我们备受感动的地方
  • vue中a标签下载本地文件-未找到【已解决】

    首先看一下我的情况 如下 目录如图 代码如下 a href public kjxz pptx 课件下载 a 一切看起来很正常 但是结果如下 然后我搜了一下发现原来href路径的问题 原来使用 public kjxz pptx 文件会找不到
  • 漫步IOS--三目运算符、switch、枚举

    1 三目运算符 三目运算符的定义 表达式1 表达式2 表达式3 例如 a gt b 2 5 三木运算符也是有返回值的 返回值等于对应的表达式的返回值 2 switch 在c语言中 switch只支持整型 但是这里的整型包括 整型 字符 布尔
  • 如何将文档上传到 ChatGPT

    OpenAI 一直在为 ChatGPT 添加几个有趣的功能 包括对网页浏览和插件的支持 但是 仍然没有办法本地上传文档并根据其上下文提出问题 当然 有些用户可以在他们的数据上训练 AI 聊天机器人 但并不是每个人都了解如何设置工具和库 因此
  • 华为OD机试 C++ 去除多余空格

    题目 你需要写一个功能 它能处理一段文本 去除其中不必要的空格 但是如果这些空格被一对单引号包围起来 就保留它们不变 同时 你还要调整一些特定词汇的位置 这些词汇的位置会以坐标的方式给出 坐标要基于新的文本 特别注意 关键词的位置一定不是空
  • Unity 入门 Input 类

    1 获得键盘 Input GetKey KeyCode A Input GetKeyDown KeyCode A Input GetKeyUp KeyCode A 2 获得鼠标信息 Input mousePosition 鼠标位置 Inpu
  • 关系型数据库是如何运作的

    一说到关系型数据库 我总感觉缺了点什么 如果你尝试透过 关系型数据库是如何运作的 的关键词句来进行搜索 其搜索结果是少量的而且内容是简短的 难道说是由于它已经太老旧而已经不再流行吗 作为一名开发者 我讨厌使用我不明白的技术 此外 关系型数据
  • s、x、t -learner

  • DICOM之Transfer Syntax

    Transfer Syntax A Transfer Syntax is a set of encoding rules able to unambiguously represent one or more Abstract Syntax
  • ChatGPT在线个人小助手应用搭建

    ChatGPT在线个人小助手应用搭建 在线体验 点我在线体验 因为openAI账户申请后会默认有18美元的账户 openAI每次调用大概会花掉0 01美元 所以为了防止恶意刷api 无意义聊天 页面做了密码限制 如果密码不对 是不会启用op
  • mysql存储引擎层和服务器层,MySQL底层架构原理,工作流程和存储引擎的数据结构讲解...

    数据库 DataBase 是存放用户数据的地方 当用户访问 操作数据库中的数据时 需要数据库管理系统的帮助 数据管理系统的全称是DataBase Management System 简称DBMS 通常情况下我们会把数据库和数据库管理系统笼统
  • 网页端无法复制粘贴的解决方案

    由于瑞格系统无法复制粘贴 写java代码比较难受 所以就找了一些方法来解决网页端无法复制粘贴的问题 1 打开浏览器的设置界面 并打开拓展程序 2 在拓展程序中选择左上角的拓展程序 并打开Chrome网上应用商店 3 在Chrome网上应用商
  • 多线程JUC并发篇常见面试详解

    文章目录 1 JUC 简介 2 线程和进程 3 并非与并行 4 线程的状态 5 wait sleep的区别 6 Lock 锁 重点 1 Lock锁 2 公平非公平 3 ReentrantLock 构造器 4 Lock 锁实现步骤 7 syn
  • 百炼成钢;JavaScript逆向九大专题详解

    JavaScript是一种脚本语言 通常用于在Web浏览器中编写交互式前端应用程序 它是一种解释性语言 可以在客户端 浏览器 和服务器端 Node js 上运行 JavaScript可以用于创建动态网页 Web应用程序 游戏 移动应用程序等
  • unity 获取鼠标键盘

    unity 获取鼠标键盘 在做项目中我们经常会用到鼠标键盘 那么怎么去获取鼠标键盘呢 接下里我带大家了解一下 首先是获取鼠标 大家记住无论是获取鼠标还是获取键盘都要用到unity中的一个小小的组件首先在unity上方的选项卡中选择edit

随机推荐

  • RocketMQ(三) broker启动

    RocketMQ源码版本V5 0 0 可兼容之前的版本 因为整理资料的时候 之前的版本 和V5版本有所出入 核心流程基本还是大同小异的 此前已经总结了NameServer的启动流程源码 现在来了解Broker的启动流程 在RocketMQ启
  • 第一章 基础算法(一)ACwing 快速,归并,二分

    第一章 基础算法 一 一 内容概述 主要思想掌握 深刻的理解 代码模板理解以及背过 掌握思想 模板题目练习 理解 记忆 1 排序 快排 归并排序 2 二分 整数二分 浮点数二分 二 快速排序 快速排序的主要思想是基于分治的 第一步就是是确定
  • gd32F450单片机 ADC+DMA

    接触国产单片机不久 好多配置的东西记不住 写下来分享然后也方便自己以后拿来看看 欢迎大家把踩坑的部分分享一下 本次是ADC配置和DMA采集的配置部分 某些参数错误会导致内存溢出 影响到其他变量或者参数表的值 引脚为PB0和PB1两个 一 相
  • 三款强大的 AI 编程工具,可以轻松替换 Github Copilot

    大家好 提起Github Copilot 相信很多读者朋友们都听说过甚至使用过 作为Github研发的一款先进的编程辅助插件 它可以在我们日常编写代码的过程中 根据代码的上下文内容 注释等信息自动推断生成高质量的代码 很大程度上提升我们的代
  • Linux中一个网络包的发送/接收流程

    如果你对Linux是如何实现 对用户原始的网络包进行协议头封装与解析 为什么会粘包拆包 期间网络包经历了哪些缓冲区 经历了几次拷贝 CPU DMA TCP又是如何实现滑动 拥塞窗口 这几个话题感兴趣的话 不妨看下去吧 1 Linux发送HT
  • linux系统下重启网络服务的两种方法

    linux系统下重启网络服务的两种方法 发布时间 2020 04 02 11 25 25 来源 亿速云 阅读 207 作者 小新 今天小编给大家分享的是linux系统下重启网络服务的两种方法 很多人都不太了解 今天小编为了让大家更加了解li
  • 【android系统】android系统升级流程分析(二)---update升级包分析

    接下来我们将通过几篇文章来分析update zip包在具体Android系统升级的过程 来理解Android系统中Recovery模式服务的工作原理 今天让我先来分析下升级包update zip 一 目录结构 update zip包的目录结
  • Linux 线程创建

    如何创建线程 看来多线程还是有很多好处的 接下来我们来看一下 如何使用线程来干一件大事 假如说 现在我们有 N 个非常大的视频需要下载 一个个下载需要的时间太长了 按照刚才的思路 我们可以拆分成 N 个任务 分给 N 个线程各自去下载 我们
  • unittest笔记+用ddt后找不到用例的坑

    unittest notes what is unittest unittest 是python单元测试框架 类似于JUnit框架 4 important concepts test fixture 测试脚手架 对一个测试用例环境的搭建和销
  • 安卓前台服务的使用(简单)

    首先是 AndroidManifest xml 文件
  • 数据结构:力扣OJ题(每日一练)

    题一 有效的括号 给定一个只包括 的字符串 s 判断字符串是否有效 有效字符串需满足 左括号必须用相同类型的右括号闭合 左括号必须以正确的顺序闭合 每个右括号都有一个对应的相同类型的左括号 示例 2 输入 s 输出 true 思路一 第一步
  • spring如何开启允许循环依赖

    如何解决spring循环依赖 在Spring框架中 allowCircularReferences属性是用于控制Bean之间的循环依赖的 循环依赖是指两个或多个Bean之间相互依赖的情况 其中一个Bean依赖于另一个Bean 同时另一个Be
  • 人工智能用哪个版本linux,Linux各个版本应用在哪些场景?你都了解吗?

    Linux是非常热门的技术 随着应用领域不断拓展 越来越多的人都想要加入Linux行业中 当我们进入行业确定好自己发展路线之后 就是选择一个合适的Linux版本 但是对于很多人都是比较头疼的问题 Linux各个版本应用在哪些场景 为大家介绍
  • Gof23设计模式之命令模式

    1 概述 将一个请求封装为一个对象 使发出请求的责任和执行请求的责任分割开 这样两者之间通过命令对象进行沟通 这样方便将命令对象进行存储 传递 调用 增加与管理 2 结构 命令模式包含以下主要角色 抽象命令类 Command 角色 定义命令
  • 解决导入torchvision(import torchvision)库执行时报错,但是导入torch库(import torchvision)执行却正常的问题。

    参考了网上各种说法 最终采用了torchvision和torch库版本不兼容的说法 完美运行 解决办法如下 1 卸载原torchvision pip uninstall torchvision 2 重新安装低版本的torchvision p
  • C++ Pat甲级1007 Maximum Subsequence Sum (25 分)(dp)

    1007 Maximum Subsequence Sum 25 分 Given a sequence of K integers N 1 N 2 N K A continuous subsequence is defined to be N
  • 什么是Web3 ?它是如何工作的?

    Web3提供了一种潜在的解决方案 可以更容易地在万维网上找到内容的原始来源 我们将讨论Web 3是什么以及它是如何工作的 万维网一直以来都是一个不受限制地创造和分享信息和思想的平台 虽然这让世界感觉更小 但它也有它的缺点 即难以确定内容的原
  • 计算机图形系统相关的输入设备,计算机图形学题目及答案

    5 20 边标志填充算法是用什么作为标志 如何实施填充的 边标志填充算法利用边界色作为标志来进行填充 当扫描线从左到右扫描时碰到边界色 立刻改变标志的状态 再根据标志的状态决定某象素点是否填充 21 将扫描线种子填充算法由实面积填充改为图案
  • 【VMware/Linux】虚拟机根目录扩容

    大家好 我是好学的小师弟 最近IT给配的虚拟机 目录容量太小了 不符合环境部署条件 所以想让IT扩下容 IT给我加了一块磁盘 然后让我自己lvm扩容去 这里记录下扩容方法 1 先添加一块磁盘 这里添加了一块磁盘sdr fdisk l 查看新
  • 数据结构与算法分析--Java语言描述(第二章(2))

    习题2 23 不用递归 写出快速求幂的程序 递归方法 param x param n return public long pow long x int n if n 0 return 1 if isEven n return pow x