【问题标题】:Sort ArrayList but keep order of the original indexes对 ArrayList 进行排序,但保持原始索引的顺序
【发布时间】:2021-04-15 22:10:50
【问题描述】:

有两个 int 数组和一个名为 cost 和 shipping 的数组列表。我想要一个总价从最低到最高的 ArrayList(例如 cost[0] + shipping[0]),但重新排序两个 int 数组以匹配 ArrayList:

Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] costs = new int[n];
int[] shipping = new int[n];
ArrayList<Integer> totalCosts = new ArrayList<>();

例如,假设成本为 [1, 5, 2, 3],运费为 [2, 3, 2, 4],那么 totalCosts 将是 {3, 8, 4, 7} 并排序为{3、4、7、8}。我希望重新排序费用和运费,以便它们与总数相对应,因此费用为 [1, 2, 3, 5],运费为 [2, 2, 4, 3]。

【问题讨论】:

  • 你想要那个,你尝试过什么来实现它?
  • 这很难做到,因为您需要根据排序移动列表元素的方式来移动数组元素。创建一个包含成本、运费和总价的新类更容易。然后列出这些并对其进行排序。
  • 一般情况下,有multiple collections that must be maintained in parallel is a bad idea。我将创建一个List&lt;Order&gt;,其中 Order 类有 2 个字段,成本和运费,以及一个方法 getTotal 将它们相加。然后使用List.sort 进行排序变得微不足道,并且由于数据在逻辑上分组为对象,因此会自动维护索引。如果你真的需要的话,你可以把它再次分解成新的数组
  • 这里使用 HashMap 可能会更好,然后在你的地图上调用 sort,因为默认排序是稳定的,应该没有任何问题!

标签: java arrays sorting arraylist


【解决方案1】:
public List<Integer> sortPrices(int[] cost, int[] shipping) {
    List<Integer> result = new ArrayList<>();
    TreeMap<Integer, List<int[]>> map = new TreeMap<>();
    int length = cost.length;
    int[] origCost = new int[length];
    int[] origShipping = new int[length];
    for (int i = 0; i < length; i++) {
        int price = cost[i] + shipping[i];
        int[] arr = {i, i};
        map.putIfAbsent(price, new ArrayList<>());
        map.get(price).add(arr);
        origCost[i] = cost[i];
        origShipping[i] = shipping[i];
    }
    int j = 0;
    for (int price : map.keySet()) {
        for (int[] arr : map.get(price)) {
            int ci = arr[0];
            int si = arr[1];
            cost[j] = origCost[ci];
            shipping[j] = origShipping[si];
            j++;
            result.add(price);
        }
    }
    return result;
}

【讨论】:

  • 以上应该可以。该地图是一个树形地图,其键为总价格,值是长度为 2 的数组列表。必须使用列表,因为我们可能不止一次拥有相同的总价格。其余的,我相信,是可以自我解释的。
【解决方案2】:

我用python解决了

只要了解循环逻辑,你就可以做到。

for i in range(len(total)):
  for j in range(len(total)):
    if sorted_total[i]==total[j]:
        new_costs[i]=costs[j]
        new_shipping[i]=shipping[j]
        print("i ",i)
        print("j ",j)
        break

【讨论】:

    【解决方案3】:

    这是一个不使用地图的解决方案。相反,我们有一个自定义比较器,它使用成本和运费的总和来对数组位置进行排序。然后我们创建新数组并从旧数组中的排序位置中选择值。

    public static void main(String[] args) {
        int n = 4;
        int[] costs = {1, 5, 2, 3};
        int[] shipping = {2, 3, 2, 4};
        Integer[] totals = new Integer[n];
        for(int i = 0; i < n; ++i) {
            totals[i] = i;
        }
    
        Arrays.sort(totals, Comparator.comparingInt(k -> (costs[k] + shipping[k])));
    
        int[] newCosts = new int[n];
        int[] newShipping = new int[n];
        for(int i = 0; i < n; ++i) {
            newCosts[i] = costs[totals[i]];
            newShipping[i] = shipping[totals[i]];
        }
    
        for(int i = 0; i < n; ++i){
            System.out.println((i + 1) + ": Cost=" + newCosts[i] + ", Shipping=" + newShipping[i] + ", Total=" + (newCosts[i] + newShipping[i]));
        }
    }
    

    说明

    totals 数组包含从 0 到 n(示例中为 4)的索引列表。 Arrays.sort() 使用 Comparator 对索引进行排序,给定索引,提取该位置的总数。 totals 数组在排序后将按顺序列出成本和运费总和的索引。

    之后,我们可以使用排序索引创建新的成本和运费数组。

    编辑

    听从 ttzn 的 cmets 建议,你可以把代码压缩成这样:

    public static void main(String[] args) {
        int n = 4;
        int[] costs = {1, 5, 2, 3};
        int[] shipping = {2, 3, 2, 4};
    
        int[] totals = IntStream.range(0, n).boxed().sorted(Comparator.comparingInt(k -> (costs[(int)k] + shipping[(int)k]))).mapToInt(Integer::intValue).toArray();
    
        int[] newCosts = IntStream.of(totals).map(i -> costs[i]).toArray();
        int[] newShipping = IntStream.of(totals).map(i -> shipping[i]).toArray();
    
        for (int i = 0; i < n; ++i) {
            System.out.println((i + 1) + ": Cost=" + newCosts[i] + ", Shipping=" + newShipping[i] + ", Total=" + (newCosts[i] + newShipping[i]));
        }
    }
    

    【讨论】:

    • 既然您将使用comparingInt 使用Java 8,那么您不妨使用int[] totals = IntStream.range(0, n)int[] newCosts = IntStream.of(totals).map(i -&gt; costs[i]).toArray();
    • 好点。我同意这是一个更好的选择,但如果您不熟悉流可能会更难理解。
    【解决方案4】:

    这是一个基于流的解决方案,无需构建地图。

    1. 构建了一个Stream&lt;int[]&gt;,将总数和索引存储在int[]数组中,

    2. 使用这些数组的自定义比较器对流进行排序(首先按总数,然后按索引)

    3. 故意将peekAtomicInteger 一起使用以重新排序输入costsships 数组
      注意:但是,这可能会被视为一种“滥用”并加重- 数据流外部的效果更新

    4. 构建并返回总计数组。

    public static int[] getTotals(int[] costs, int[] ships) {
        assert costs.length == ships.length;
        int[] copyCosts = Arrays.copyOf(costs, costs.length);
        int[] copyShips = Arrays.copyOf(ships, ships.length);
        
        AtomicInteger ix = new AtomicInteger(0);
        
        return IntStream.range(0, costs.length)
                        .mapToObj(i -> new int[]{ costs[i] + ships[i], i })
                        .sorted(Comparator.<int[]>
                            comparingInt(p -> p[0])
                            .thenComparingInt(p -> p[1])
                        ) // get sorted Stream<int[]>
                        .peek(p -> {
                            costs[ix.get()] = copyCosts[p[1]];
                            ships[ix.getAndIncrement()] = copyShips[p[1]];
                        })
                        .mapToInt(p -> p[0])
                        .toArray();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-12
      • 1970-01-01
      • 2017-04-04
      • 1970-01-01
      • 1970-01-01
      • 2013-11-29
      • 1970-01-01
      相关资源
      最近更新 更多