首页 文章

为什么这个比较器不能正确地分离奇数和偶数

提问于
浏览
-1

下面是我在数组中分离奇数和偶数的尝试(失败和成功) . 预期输出是在数组中奇数之前的所有偶数 . 所以,有效的输出是

2 4 6 8 10 9 7 5 3 1

评论中的一个是不起作用的版本,另一个版本可行 .

在失败的版本中,我想知道为什么它不起作用 . 我正在做的是,如果第一个数字是奇数,第二个数字是偶数,我交换它 . 在所有其他情况下,我不做任何交换,这是可以的,因为如果两个数字都是偶数,或者两个数字都是奇数,或者如果已经偶数在奇数之前,则不需要交换,在这种情况下,它已经保持了预期产出的格局 .

public void evenOddComparator()
    {
        Integer[] a = {4,3,1,2,5,6,8,9,7,10};
//        Integer[] a = {3,4};

        Arrays.sort(a, new Comparator<Integer>()
            {
//                @Override
//                public int compare(Integer a1, Integer a2)
//                {
//                    if( ((a1%2) == 1) && ((a2%2) == 0))
//                        return 1;
//                    return 0;
//                }

                final int BEFORE = -1;
                final int EQUAL = 0;
                final int AFTER = 1;

                @Override
                public int compare(Integer o1, Integer o2) {
                    if (o1 % 2 == 0 && o2 % 2 != 0) {
                        return BEFORE;
                    } else if (o1 % 2 != 0 && o2 % 2 == 0) {
                        return AFTER;
                    } else if (o1 % 2 == 0 && o2 % 2 == 0) {
                        return o1.compareTo(o2);
                    } else if (o1 % 2 != 0 && o2 % 2 != 0) {
                        return o2.compareTo(o1);
                    }

                    return EQUAL;
                }
            });
        print(a);   
    }

你能帮助我理解为什么注释掉的逻辑没有像预期的那样工作吗?

2 回答

  • 0

    您注释的代码的基本问题是您正在破坏所有比较实现的预期一致性条件:

    实现者必须确保所有x和y的sgn(x.compareTo(y))== -sgn(y.compareTo(x)) .

    http://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html#compareTo-T-开始,但 Comparator 中的语言相同 . (该部分还有另外两个类似的条件,您也应该阅读并理解) .

    在你的情况下3比较2 = 1和2比较3 = 0.这使你的实现打破了Java中所有排序算法的假定前提条件,因此它不起作用 .

    这一点很重要的原因在于,否则最终的顺序将取决于形成比较的每一侧的哪些元素 . 如果算法碰巧比较2到3而不是3比2,那么你会以不同的顺序结束 . 文档坚持通过交换边返回完全否定的结果,以便所有实现可以是任意关于哪些元素构成比较中的第一个和第二个参数 . 这允许效率(并且,作为旁边,匹配比较的数学定义) .

    我还注意到,在你的问题中,你暗示实现比较是为了表示在排序操作中要交换的内容和不交换的内容 . 这根本不是真的 . 有许多不同的排序算法,其中许多不交换任何东西 - 它们复制或拆分并重新加入或使用许多其他技术 . 或者比较可以是找到最大或最小的元素 .

    因此,当您定义比较时,您没有指明要交换的内容;而是你要定义什么是更小的和更大的(在这个比较中)并留下它如何用于你正在调用的算法的细节 . 您永远不应该假设您知道该算法是如何工作的(特别是当它将从JVM更改为JVM并从版本更改为版本时) . 只需遵循文档中的条件,无论算法如何变化,都可以保证其工作 .

    另外,您可以使用单个语句来实现结果:

    sort(a, comparingInt(n -> floorMod(n, 2));
    

    这只是将每个数字转换为0或1,具体取决于它是偶数还是奇数,然后将它们进行比较 . 它使用 Math.floorMod ,以便它可以正确处理负数(您的实现不会) .

  • 3

    比较函数用于确定哪个数字更大 . 你必须使任何奇数评估为更大 .

    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Main {
    
        public static void main(String[] args) {
            Integer[] a = {4, 3, 1, 2, 5, 6, 8, 9, 7, 10};
    //        Integer[] a = {3,4};
    
            Arrays.sort(a, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    if (o1%2 == o2%2) {
                        // Both numbers are odd or both numbers are even
                        return 0;
                    }
                    // One is odd, the other one is even
                    if (o1%2 == 0) {
                        return -1;
                    }
                    return 1;
                }
            });
            for(int x:a) {
                System.out.print(x);
            }
        }
    }
    

相关问题