【问题标题】:How to sort some elements and leave others in Java?如何在 Java 中对一些元素进行排序并留下其他元素?
【发布时间】:2018-01-24 15:11:20
【问题描述】:

我希望对数组中的一些元素进行排序,但排除其他元素。

举个简单的例子,一个包含整数的数组,我想对奇数进行排序,但将偶数留在原处。

到目前为止,我有以下内容:

public class MyClass {
    public static void main(String args[]) {
        int temp;
        int array[] = {5, 3, 2, 8, 1, 4};

        int[] sortedArray = new int[array.length];
        for (int j = 0; j < array.length - 1; j++) {
            for (int x = 0; x < array.length - 1; x++) {
                if (array[x] > array[x + 1] && array[x] % 2 != 0 && array[x + 1] % 2 !=0) {
                    temp = array[x];
                    array[x] = array[x + 1];
                    array[x + 1] = temp;
                    sortedArray = array;
                }
            }
        }
        for (int i: sortedArray) {
            System.out.println(i);
        }

    }
}

给定整数数组: 5, 3, 2, 8, 1, 4

代码输出: 3, 5, 2, 8, 1, 4

预期输出: 1、3、2、8、5、4

无法完全弄清楚原始数组中较低的奇数出现在偶数之前的场景所需的逻辑。

【问题讨论】:

  • 请给出您的预期输出..
  • 1, 3, 2, 8, 5, 4 - 奇数排序,偶数保持原位。
  • 我很难想出这种算法的用例。
  • @corsiKa 不失败的课程将是我期望的一个原因。

标签: java arrays sorting lambda


【解决方案1】:

一个简单的蛮力解决方案:

  • 迭代输入数组,检索所有奇数
  • 在一个新的更小的数组中收集奇数
  • 对该数组进行排序
  • 现在再次遍历初始数组:只要找到奇数,就从奇数数组中选择“下一个”条目

以上可能不是最佳解决方案(因为在第二个数组上浪费内存,并且花时间复制前/后值) - 但它应该超级简单 写下来并测试。

理论上,您也可以“就地”执行此操作。含义:您可以创建一个包含 int 数组的类 - 但它为其用户提供了一个仅显示奇数 int 数组的 view

示例实现(感谢Daniel Foerster):

public static int[] sortFiltered(int[] src, IntPredicate predicate) {
  int[] filtered = IntStream.of(src).filter(predicate).sorted().toArray();
  int[] dst = new int[src.length];
  for (int i = 0, j = 0; i < src.length; i++) {
    dst[i] = predicate.test(src[i]) ? filtered[j++] : src[i];
  }
  return dst;
}

使用奇数过滤器调用:

sortFiltered(array, (n) -> n % 2 != 0);

如您所见,此算法不依赖于特定的谓词或数组/列表类型。但由于它使用IntStreamLambda Expressions,它需要Java 8 或更高版本。

【讨论】:

  • 很好的解决方案。我打算同样建议:)
  • 如果您必须强制执行相对标准的排序或使用高于 n log n(其中 n = 输入数组的大小)的预期运行时间,那么您做错了。
  • 非常好的通用实现,我刚刚投了赞成票!但是为了完整起见:Lambda 表达式 是在 Java 8 中引入的,如果需要使用较低版本的 Java,这将不起作用。
  • 此外,此解决方案应该比其他二次 O(n^2) 答案更快。由于 IntStream.sorted(),它应该是 O(n log n) 最坏的情况,请参见此处:stackoverflow.com/a/31302160/8051589。这里也是 Big-O 表示法的一个很好的快速参考:bigocheatsheet.com
【解决方案2】:

给定未排序的数组 A:创建 2 个 Vector 实例 O 和 E,按顺序包含数组的奇数和偶数元素。 (所有代码示例都是伪代码。循环中的“up to”表示不包括限制的右侧,根据标准数组绑定编号)

for i in 0 up to A.length: if A[i] is odd, append it to O, else append it to E

创建一个单独的布尔数组 B,如下所示:

for i in 0 up to A.length: if A[i] is odd, B[i] = true, else B[i] = false

将 O 排序为一个新的向量 S(按升序)。我建议使用 Java 的内置排序,因为它比您使用的其他答案声称的冒泡排序更有效。

S = ascendingSort(O)

定义一个操作pop,返回 Vector 中的第一项,然后将其从 Vector 中删除。

新建一个空向量R,然后实现如下伪代码:

for i in 0 up to B.length: if B[i] is true, then append pop(S) to R, else append pop(E) to R.

返回 R 作为结果。


为什么会这样:

首先,将赔率与偶数分开以将偶数从排序中隐藏,保持其原始顺序(向量 O 和 E)。保持原数组中奇/偶项的位置(向量B)。

对赔率进行排序(向量 S)。

排序后,将偶数和排序后的赔率按顺序缝合,保持偶数的原始顺序。通读向量 B,从向量 E 和 S 中按顺序获取元素。这保持了偶数的顺序,因为它们首先按顺序放入向量 E,然后按顺序放回。

【讨论】:

  • 我还应该补充一点,我的答案比其他人发布的二次(和更慢)答案运行得更快(通过算法分析)。特别是,每个循环操作在最坏的情况下以线性时间(与输入数组的大小)运行,排序操作以 n log n 时间运行(使用生产级排序算法,如快速排序或归并排序)甚至线性时间如果您使用数据相关的排序,例如基数排序。
  • 非常类似于 GhostCat 对这个问题的回答的实现:stackoverflow.com/a/45714248/8051589,但没有使用 Java 8 的功能。由于有用的解释,我只是赞成它!
【解决方案3】:

您已经实现了某种(不是超级高效的)冒泡排序。您的代码的问题在于,即使只有一个是偶数,它也将这两个元素都排除在排序之外。基于您的解决方案,您可以按如下方式调整代码:

public class MyClass {
    public static void main(String args[]) {
        int temp;
        //odd numbers are sorted, even number stay where they are
        //Expected output: 1, 3, 2, 8, 5, 4
        int array[] = {5, 3, 2, 8, 1, 4};
        int[] sortedArray = new int[array.length];
        for (int j = 0; j < array.length - 1; j++) {
            for (int x = 0; x < array.length - 1; x++) {

                while(array[x] % 2 == 0 && x < array.length-1){
                    x++;
                }

                int y = x+1;

                if(y < array.length) {
                    while (array[y] % 2 == 0 && y < array.length - 1) {
                        y++;
                    }

                    if (array[x] > array[y] && array[y] % 2 == 1 && array[x] % 2 == 1) {
                        temp = array[x];
                        array[x] = array[y];
                        array[y] = temp;
                        sortedArray = array;
                    }
                }
            }
        }
        for (int i: sortedArray) {
            System.out.println(i);
        }
    }
}

这会导致:

1 3 2 8 5 4

【讨论】:

  • 尝试排序 int array[] = {5, 3, 2, 8, 6, 4};
  • 4 个嵌套循环 --> 类似:O(n^4)。非常不高效!尽管如此,它仍然有效。
  • @AndreKampling 我认为你误解了while-loops - 它们都在恒定时间内完成(它们运行一次或根本不运行) - 应该是O(n^2)
【解决方案4】:

您的代码是一种冒泡排序,但您只是比较每个直接邻居。这是使用 2 个 for 循环并就地工作的工作代码。它不一定会比较直接邻居,而是查找下一个奇数以与当前奇数进行比较。

此外,将array 复制到sortedArray 时出现了一点错误,因为您这样做:sortedArray = array; 在您的循环中,它只是复制了数组引用。在下面的工作代码中,我选择了您的解决方案并进行了更改。我也只是在排序开始之前复制数组,以便源数组不变。

内部for循环以外部for循环的索引加1开始。此外,内部循环只循环到结束前的一个元素。在最坏的情况下,它将需要 n-1 * n/2 = n^2/2 - n/2 操作,这会导致 O(n^2) 像常规冒泡排序一样。

代码和working example on ideone:

public class MyClass
{
   public static void main(String args[])
   {
      int array[] = {5, 3, 2, 8, 1, 4};

      int[] sortedArray = new int[array.length];
      System.arraycopy(array, 0, sortedArray, 0, array.length);

      for (int i = 0; i < sortedArray.length - 1; i++)
      {
         /* is current odd, if so search next odd */
         if (sortedArray[i] % 2 != 0)
         {
            /* search for next odd and compare */
            for (int j = i + 1; j < sortedArray.length; j++)
            {
               if ((sortedArray[j] % 2 != 0) && (sortedArray[i] > sortedArray[j]))
               {
                  int temp = sortedArray[i];
                  sortedArray[i] = sortedArray[j];
                  sortedArray[j] = temp;
               }
            }
         }
      }
      for (int i: sortedArray)
      {
         System.out.println(i);
      }
   }
}

输出:

1
3
2
8
5
4

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 2017-04-05
    • 1970-01-01
    • 2020-06-24
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多