如何在Java中将整数插入到排序列表中?

时间:2016-12-10 12:11:33

标签: java list sorting

我想编写一个函数insertAndSort(),它将整数“num”和整数列表“list”作为参数。我们假设List欲望已经排序了。

该算法应该以这种方式工作:

  1. 将“num”添加到L并在添加“num”后对List进行排序。
  2. 返回已排序的列表。
  3. 警告:我们不知道列表是按ASC还是DESC排序。这正是我的问题!

    示例:

    • 如果“num”= 4且L = {1,3,5,7},则最终排序列表为{1,3,4,5,7}
    • 如果“num”= 4且L = {7,5,3,1},则最终排序列表为{7,5,4,3,1}
    • 我不能使用Collections.sort或Collections.reverseOrder等排序API ......

    到目前为止,我已经制作了这段代码:

    public static void main(String[] args) {
    
        int num = 4;
        List<Integer> myList = Arrays.asList(1, 3, 5, 7);
        List<Integer> newList = new ArrayList<Integer>();
        newList = insertAndSort(myList, num);
        System.out.println(newList);
    
    }
    
    public static List<Integer> insertAndSort(List<Integer> list, int num) {
            List<Integer> listSecond = new ArrayList<Integer>(list.size()+1);
            for(int i = 0; i <= list.size() ; i++) {
                if(num < list.get(i)) {
                    if(!listSecond.contains(num)){
                        listSecond.add(num);
                    } else {
                        listSecond.add(list.get(i-1));
                        listSecond.add(list.get(i));
                        break;
                    }            
                } else {
                    listSecond.add(list.get(i));
                }
            }
            return listSecond;
        } 
    

    问题在于,这似乎与单一类型的List一起使用:升序列表。 当我采用排序的降序列表时,它不再起作用。

    您是否有任何想法可以使用这两种类型的List?

    谢谢和问候。

5 个答案:

答案 0 :(得分:4)

首先,您需要检测现有列表中的排序顺序。

  1. 如果列表为空,您无需关心,只需添加元素,就不能破坏任何现有的排序顺序。
  2. 如果列表包含一个元素,我无法告诉您该怎么做。选项包括投掷硬币和抛出异常,但还有更多。
  3. 如果列表包含两个或更多元素,则除了最后一个元素之外,迭代它们,每次将当前元素与下一个更高索引中的元素进行比较。一旦遇到一对不相等的元素,就会知道排序顺序。如果你只遇到相同的元素,列表中的所有元素都是相同的,我再也不能告诉你该怎么做。
  4. 一旦你检测到排序顺序,我建议使用一个很大的if-else语句来分别处理这两个案例。

    您已经拥有升序案例的代码。除了它并不总是有效。如果我尝试将4插入{1,3,5},我会得ArrayIndexOutOfBoundsException。如果我尝试使用{1,3,5,7,9},则会丢失9。你可能应该找到一个解决方法。

    您应该能够处理降序情况,与升序情况类似。仅使用num > list.get(i)代替num < list.get(i)

答案 1 :(得分:1)

首先,您需要检查列表是否在ASC或DESC中排序。这很简单:比较前两个元素。

if(list.size() >= 2 && list.get(0) < list.get(1))
    //ASC
else
    //DESC

现在,对于按升序排序的列表,请将list转换为ArrayList。然后,将num添加到ArrayList并将其转换为TreeSet,因为TreeSet会按升序对其元素进行排序。最后,重新分配ArrayList以保存TreeSet的内容并将其返回。

对于按降序排序的列表,首先,将TreeSet转换为ArrayList,然后以相反的顺序迭代此ArrayList并将元素添加到新的ArrayList。返回第二个ArrayList。

public List<Integer> insertAndSort(List<Integer> list, int num){

    ArrayList<Integer> a = new ArrayList<Integer>(list);
    a.add(new Integer(num));
    TreeSet t = new TreeSet(a);
    a = new ArrayList<Integer>(t);

    if(list.size() >= 2 && list.get(0) < list.get(1))
        return a;
    else{            
        ArrayList<Integer> a2 = new ArrayList<Integer>();
        for(int i = a.size() - 1; i >= 0; i--)
            a2.add(a.get(i));
        return a2;
    }
}

答案 2 :(得分:0)

我会继续使用下面的代码。一些评论:

  • 当且仅当至少有1个元素且第一个和最后一个元素具有不同的值时,才可以决定排序顺序。
  • int oo = list.get(p2) - list.get(p1);计算 最后一个元素和第一个元素之间的差异(负= DESC, positive = ASC)
  • 当且仅当变量ox为正时 添加的元素位于变量p3指向的元素之后 无论订单如何。
  • 通过在p3p1之间选择p2并确定该元素是在p3之前还是之后,通过二元搜索算法找到位置。

这与您提供的示例无法完全测试:

// static List<Integer> list = new ArrayList<>(Arrays.asList(7, 5, 3, 1));
static List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 5, 7));

static void add(int x)
{
    // fixed code below (see Ole V.V. comment)
}

static public void main(String [ ] args)
{
    add(4);

    for(int x: list)
        System.out.println(x);
}

编辑:完成(见Ole V.V.评论)

  • 当要在第一个或最后一个之前插入新元素时,可以执行两个简单的O(1)测试;一般情况下具有O(log N)复杂度,这比遍历整个列表O(N)更有效。
  • 在比较元素以推断排序顺序时也要小心,可能有几个相等的值;最好是比较第一个和最后一个元素,这是O(1) - 再次优于O(N)。

算法的复杂性对我来说是一个重要的问题,并且是我帖子的主要目标。 add函数的代码变为:

static void add(int x)
{
    int p1 = 0;
    int p2 = list.size() - 1;

    if(list.size() == 0 || list.get(p1) == list.get(p2))
        throw new IllegalStateException("Unable to decide sort order");

    int oo = list.get(p2) - list.get(p1);

    if(oo * (x - list.get(p1)) <= 0)       // new element is before first
        list.add(0, x);
    else if(oo * (x - list.get(p2)) >= 0)  // new element is after last
        list.add(x);
    else
    {
        while (p1 + 1 < p2)
        {
            int p3 = (p1 + p2) / 2;

            int ox = (x - list.get(p3)) * oo;

            if(ox >= 0)  // new element is after p3
                p1 = p3;

            if(ox <= 0)  // new element is before p3
                p2 = p3;
        }

        list.add(p2, x);
    }
}

注意:可能还有一些未处理的副作用。如果提问者感兴趣,我准备提供进一步的帮助 - 请问。

答案 3 :(得分:0)

您的代码和您所说的是两回事。 根据您编写的代码,我发现List不能包含多个相同值的实例。 在这种情况下,main方法应如下所示:

public static void main(String[] args){

  int num = 4;
    List<Integer> myList = Arrays.asList(1, 3, 4, 5, 7);
    List<Integer> newList;
    if (!myList.contains(num)) {
        newList = insertAndSort(myList, num);
    } else {
        newList = copyList(myList);
    }

    System.out.println(newList);

}

如果不是这种情况:

public static void main(String[] args){

  int num = 4;
    List<Integer> myList = Arrays.asList(1, 3, 4, 5, 7);
    List<Integer> newList;
    newList = insertAndSort(myList, num);

    System.out.println(newList);

}

主要方法使用的方法:

假设我们只能知道列表是如何按列表中的值排序的,那么当只有1个值或所有值都相同时,我们必须决定默认的排序方法。我选择ASC作为默认值。如果元素可以具有相同的值,并且我们确定列表已按其最佳排序,则将列表中的最小值与最高值进行比较。使用这种方法,我们只需比较两个元素一次。

所以查看列表是否已排序的方法DESC看起来像这样:

private static boolean isDesc(List<Integer> list) {
    return list.get(0) > list.get(list.size() - 1);
}

如果方法返回true,则其已排序的DESC。返回false时,其ASC。如果要更改默认值,则当值不告诉我们默认值如何排序并且我们希望将其设为DESC时,可以将'>'符号替换为'> ='

private static boolean isDesc(List<Integer> list) {
    return list.get(0) >= list.get(list.size() - 1);
}

代码insertAndSort:

public static List<Integer> insertAndSort(List<Integer> list, int num) {
    List<Integer> listSecond = new ArrayList<Integer>(list.size() + 1);
    boolean isDescSortOrder = isDesc(list);
    if (isDescSortOrder) {
        int i = 0;
        while ((i < list.size()) && (list.get(i) > num)) {
            listSecond.add(list.get(i));
            i++;
        }
        listSecond.add(num);
        while (i < list.size()) {
            listSecond.add(list.get(i));
            i++;
        }
    } else { // is asc sort order
        int i = 0;
        while ((i < list.size()) && (list.get(i) < num)) {
            listSecond.add(list.get(i));
            i++;
        }
        listSecond.add(num);
        while (i < list.size()) {
            listSecond.add(list.get(i));
            i++;
        }

    }
    return listSecond;
}

取决于将其排序的代码分为2个块​​的方式。正如我们所不知道的,在每次迭代中插入num值的位置都是正确的,最好在while循环中使用。使用for循环并每次检查list中是否已存在num值都会适得其反。最重要的是,我不知道您的应用程序是否应允许列表中使用相同的值。如果我认为应该这样做,则不能添加num值(如果它已存在于for循环的列表中,并使用包含每次迭代检查)。

仅当list已包含元素并且我们不想添加已经包含的元素时复制表:

public static List<Integer> copyList(List<Integer> list) {
        List<Integer> listSecond = new ArrayList<Integer>(list.size());
        for (int i = 0; i < list.size(); i++) {
            listSecond.add(list.get(i));
        }
        return listSecond;
    }

基于良好的编程习惯,我建议根据方法的用途来命名方法。

调用方法insertAndSort正在引起麻烦。如果我看到了,我会说它会改变我们在参数中给出的列表。然后对其进行排序。 因此,将未排序的列表放在其中会给我们带来不必要的结果。 而且我仍然会问自己,当我们向现有列表中插入一个项目时为什么它返回一个列表?我更愿意命名:

public static List<Integer> copySortedWithNewValue(List<Integer> sortedList, int newValue)

答案 4 :(得分:0)

尝试使用PriorityQueue。该集合包含可以重复的元素排序序列。