【数组】优美的排列 II

本文通过分析示例,揭示了一道编程题目的数学规律,即如何构造一个列表,使得相邻元素之差的列表中只有k个不同整数。解题思路包括准备两个列表,计算分割数字的长度,然后按照特定顺序合并。代码实现中首先使用了ArrayList,随后优化为直接合并数组,以提高效率。文章强调了解决这类问题需寻找数学规律,之后进行代码实现和性能优化。

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

题目描述

给你两个整数 n 和 k ,请你构造一个答案列表 answer ,该列表应当包含从 1 到 n 的 n 个不同正整数,并同时满足下述条件:

假设该列表是 answer = [a1, a2, a3, ... , an] ,那么列表 [|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|] 中应该有且仅有 k 个不同整数。
返回列表 answer 。如果存在多种答案,只需返回其中 任意一种 。

示例 1:

输入:n = 3, k = 1
输出:[1, 2, 3]
解释:[1, 2, 3] 包含 3 个范围在 1-3 的不同整数,并且 [1, 1] 中有且仅有 1 个不同整数:1

解题思路

这道题实际是一道数学题目,找数学规律;下面就是分析这个题目的过程:

假设n=4,分别求解k=[1,2,3]的answer列表:

假设n=5,分别求解k=[1,2,3,4]的answer列表:

根据这个规律可以有下面的思路:

  • 准备2个list,分别是l1和l2;
  • 计算分割时需要的数字长度:move=k/2;
  • l1记录从1到n-move的数字,l2记录从n到n-move+1的数字;
  • 判断是l1的数字作为第一个,还是l2的数字作为第一个;判断逻辑如下:boolean isLeftFirst = k % 2 == 1;
  • 开始merge操作,将结果merge到 int res[] = new int[n]中;
  • 返回res。

代码实现如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution1 {
    public int[] constructArray(int n, int k) {
        int move = k / 2;

        int left = 1;

        List<Integer> l1 = new ArrayList<>(n - move);
        for (int i = left; i <= n - move; i++) {
            l1.add(i);
        }
        List<Integer> l2 = new ArrayList<>(move);
        for (int i = n; i > n - move; i--) {
            l2.add(i);
        }

        int leftIndex = 0;
        int rightIndex = 0;
        int index = 0;
        int[] res = new int[n];

        boolean isLeftFirst = k % 2 == 1;

        while (leftIndex < l1.size() && rightIndex < l2.size()) {
            if (isLeftFirst) {
                res[index] = l1.get(leftIndex);
                index++;
                leftIndex++;
                res[index] = l2.get(rightIndex);
                index++;
                rightIndex++;
            } else {
                res[index] = l2.get(rightIndex);
                index++;
                rightIndex++;
                res[index] = l1.get(leftIndex);
                index++;
                leftIndex++;
            }
        }

        while (leftIndex < l1.size()) {
            res[index] = l1.get(leftIndex);
            index++;
            leftIndex++;
        }
        while (rightIndex < l2.size()) {
            res[index] = l2.get(rightIndex);
            index++;
            rightIndex++;
        }

        return res;
    }

    public static void main(String[] args) {
        Solution1 solution = new Solution1();
        System.out.println(Arrays.toString(solution.constructArray(4, 1)));
        System.out.println(Arrays.toString(solution.constructArray(4, 2)));
        System.out.println(Arrays.toString(solution.constructArray(4, 3)));


        System.out.println(Arrays.toString(solution.constructArray(5, 1)));
        System.out.println(Arrays.toString(solution.constructArray(5, 2)));
        System.out.println(Arrays.toString(solution.constructArray(5, 3)));
        System.out.println(Arrays.toString(solution.constructArray(5, 4)));
    }
}

按照上述思路,能够解决问题,但是耗时方面还可以进一步优化;

这里不再使用l1 和 l2 做list的合并,改成直接merge数组,int res[] = new int[n]; 按照这个思路改写完成后代码如下:


import java.util.Arrays;

class Solution {
    public int[] constructArray(int n, int k) {
        int move = k / 2;
        boolean isLeftFirst = k % 2 == 1;

        int left = 1;
        int right = n;
        int countIndex = 0;
        int[] newRes = new int[n];

        while (countIndex < n) {

            if (isLeftFirst) {
                if (left <= n - move) {
                    newRes[countIndex] = left;
                    left++;
                    countIndex++;
                }

                if (right > n - move) {
                    newRes[countIndex] = right;
                    right--;
                    countIndex++;
                }
            } else {
                if (right > n - move) {
                    newRes[countIndex] = right;
                    right--;
                    countIndex++;
                }

                if (left <= n - move) {
                    newRes[countIndex] = left;
                    left++;
                    countIndex++;
                }
            }
        }
        return newRes;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(Arrays.toString(solution.constructArray(4, 1)));
        System.out.println(Arrays.toString(solution.constructArray(4, 2)));
        System.out.println(Arrays.toString(solution.constructArray(4, 3)));


        System.out.println(Arrays.toString(solution.constructArray(5, 1)));
        System.out.println(Arrays.toString(solution.constructArray(5, 2)));
        System.out.println(Arrays.toString(solution.constructArray(5, 3)));
        System.out.println(Arrays.toString(solution.constructArray(5, 4)));
    }
}

 这道题优化结果如下:

总结

最近做这些题目发现都是找数学规律,找到数学规律后再做代码实现,最后是做代码耗时优化,后续可以多看看数学相关的书籍。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值