华为OD机试真题【乱序整数序列两数之和绝对值最小】

1、题目描述

【乱序整数序列两数之和绝对值最小】
给定一个随机的整数(可能存在正整数和负整数)数组nums,请你在该数组中找出两个数,其和的绝对值(|nums[x]+nums[y1])为最小
值,并返回这个两个数(按从小到大返回)以及绝对值。
每种输入只会对应一个答案。
但是,数组中同一个元素不能使用两遍。
【输入描述】
一个通过空格分割的有序整数序列字符串,够1000个整数,且整数数值范围是[-65535, 65535]。
【输出描述】
两数之和绝对值最小值
【示例1】
【输入】

-1-37 511 15
【输出】
-352
说明
因为|nums[0] + nums[2]|=|-3+5|=2最小,所以返回-3 52。

2、解题思路

第一种方法是运用暴力递归的方式,依次将每两个数字求其和的绝对值,得到最小的一组即为答案。

两个数字的之和绝对值最小-定满足以下特征之-
●两个数字都负数,那么是最大的俩负数的和的绝对值最小。
●两个数字都是正数,那么是最小的俩整数的和的绝对值最小。
●一个数负数-个数是正数,此时情况需要特殊处理。因为需要绝对值最小,那么对于一个负数-N显然想找+N , 我们遇到一个负数x,要找非须数区的最接近于|x的的那个数。

所以解题思路了就很清晰了:将输入的整数数组排序Q,然后将问题转化为在数组的正数部分找到一个最接近给定负数的数的问题。这
样的问题可以通过二分查找来解决。从数组的负数部分开始遍历,然后在数组的正数部分使用一分查找找到一个最接近给定负数的正数。
后,我们需要比较所有可能情况得到的绝对值和,返回最小的一个。

3、参考代码

import java.util.Arrays;
import java.util.Scanner;

/**
 * @Author 
 * @Date 2023/6/11 23:15
 */
public class 乱序整数序列两数之和绝对值最小 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            int[] nums = Arrays.stream(in.nextLine().split(" "))
                    .mapToInt(Integer::parseInt).toArray();

            getMinAbsSum(nums); // 方法一
            getMinAbsSum2(nums); // 方法二
        }
    }

    // 暴力解法
    public static void getMinAbsSum(int[] nums) {
        int min = Integer.MAX_VALUE;
        int[] minArr = new int[2];
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                int sum = Math.abs(nums[i] + nums[j]);
                if (sum < min) {
                    min = sum;
                    minArr[0] = nums[i];
                    minArr[1] = nums[j];
                }
            }
        }

        Arrays.sort(minArr);
        for (int arr : minArr) {
            System.out.print(arr + " ");
        }
        System.out.println(min);
    }

    public static void getMinAbsSum2(int[] nums) {
        Arrays.sort(nums);
        int munAbsSum = Integer.MAX_VALUE;
        int num1 = 0;
        int num2 = 0;

        if (nums[nums.length - 1] <= 0) {  // 全是负数
            num1 = nums[nums.length - 2];
            num2 = nums[nums.length - 1];
        } else if (nums[0] >= 0) {  // 全为正数
            num1 = nums[0];
            num2 = nums[1];
        } else {

            int zPos = findPos(nums);  // 找到第一个大于等于0的下标
            for (int i = 0; i < zPos; i++) {
                int j = findPos(nums, zPos, nums.length - 1, Math.abs(nums[i]));
                if (munAbsSum > Math.abs(nums[i] + nums[j])) {
                    munAbsSum = Math.abs(nums[i] + nums[j]);
                    num1 = nums[i];
                    num2 = nums[j];
                }
            }
        }

        System.out.println(num1 + " " + num2 + " " + Math.abs(num1 + num2));
    }

    // 找到第一个大于等于0的下标
    public static int findPos(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] >= 0) {
                return i;
            }
        }
        return -1;
    }

    // 找到接近k的位置
    public static int findPos(int[] arr, int left, int right, int k) {
        if (left > right) {
            return -1;
        }
        if (left == right) {
            return arr[left];
        }
        int j = left;
        while (left <= right) {
            int mid = left + ((right - left) / 2);
            if (arr[mid] <= k) {
                j = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        if (j == left) {
            return j;
        } else {
            return k - arr[j] <= k - arr[j + 1] ? j : j + 1;
        }
    }
}

你可能感兴趣的:(华为0D机试真题,算法,java,数据结构,二分法,OD)