【问题标题】:Insertion Sort LinkedList Java插入排序 LinkedList Java
【发布时间】:2014-03-15 23:24:11
【问题描述】:

我正在尝试为 LinkedList 编写插入排序,我有一个工作方法,但速度非常慢。一个多小时来添加和排序 50,000 个元素。

public void insert(Custom c) 
{
    int i = 0;
    for (i = 0; i < list.size(); i++)
    {
        if(list.get(i).compareTo(c) > 0  )
        {
            list.add(i,c);
            return;
        }
    }
    list.add(c);    
}

我知道我可以使用 Collections.Sort,但对于这项任务,我需要编写自己的 LinkedList。我不是要求一个完整的解决方案,只是一些指针。

【问题讨论】:

  • 这可能是因为你一直在调用list.get(i),这是一个 O(n) 操作。
  • 多久发送一次list.add(e);?如果它经常发生,您可以尝试先将 e 与最后一个条目进行比较。
  • @MrLore 8 次 5000 个元素
  • @user3424480 无论如何,这可能值得先尝试一下,这仍然是对compareTo() 的调用减少了 39,992 次。

标签: java sorting insertion-sort


【解决方案1】:

首先,List 上的插入排序会很慢(O(N^2))......不管你怎么做。但是您似乎已将其实现为O(N^3)

这是您的代码...将被调用 N 次,以添加每个列表元素。

public void insert(Entry e) 
{
    int i = 0;
    for (i = 0; i < list.size(); i++)       // HERE #1
    {
        if(list.get(i).compareTo(e) > 0  )  // HERE #2
        {
            list.add(i,e);                  // HERE #3
            return;
        }
    }
    list.add(e);                            // HERE #4   
}

在“HERE #1”处,我们迭代最多 M 次,其中 M 是当前(部分)列表长度;即O(M)。这是插入排序所固有的。但是,根据您实现size() 方法的方式,您可以 将迭代转换为O(M^2) 操作序列。 (LinkedList.size() 方法只返回一个size 变量的值。这里没问题。但是如果size() 计算元素...)

在“HERE #2”中,我们进行了获取和比较。比较 (compareTo(...)) 很便宜,但链表上的get(i) 操作涉及从头开始遍历列表。那是一个O(M) 操作。并且由于您每次调用insert 时都调用get(i) 调用O(M) 次,因此调用O(M^2) 和排序O(N^3)

在“HERE #3”处,add(i,e) 重复前一个 get(i) 调用的列表遍历。但这并不是那么糟糕,因为每次 insert 调用只执行一次 add(i,e) 调用。所以整体复杂度不受影响。

在“HERE #4”处,add() 操作可能是 O(1)O(M),具体取决于其实现方式。 (对于LinkedList.add(),它是O(1),因为列表数据结构保持对列表最后一个节点的引用。)无论哪种方式,整体复杂性都不会受到影响。

简而言之:

  • #2 处的代码绝对使它成为O(N^3) 排序。

  • #1 的代码也可以使其成为O(N^3) ...但不能使用标准的LinkedList 类。


那该怎么办?

  • 一种方法是重新编码insert 操作,使其直接使用nextprev 字段等遍历列表。不应调用任何“更高级别”的列表操作:size、get(i)、add(e) 或 add(i, e)。

    但是,如果您通过扩展或包装 LinkedList 来实现此功能,则不能这样做。这些字段是私有的。

  • 如果你正在扩展或包装LinkedList,那么解决方案是使用listIterator() 方法给你一个ListIterator,并使用它来进行有效的遍历。 ListIterator 上的 add 操作是 O(1)

  • 如果(假设)您正在寻找对(大)LinkedList 进行排序的最快方法,那么解决方案是使用Collections.sort。在幕后,该方法将列表内容复制到一个数组中,对数组进行O(NlogN) 排序,并根据排序后的数组重建列表。

【讨论】:

    【解决方案2】:

    根据this 的响应,您应该使用ListIterator.add() 而不是List.add,因为性能更好。

    【讨论】:

    • 现在添加了一个迭代器。性能大大提高。现在大约 40 秒内 50,000 个
    【解决方案3】:

    使用更快的排序算法怎么样?

    这里是 QuickSort。对于更大的数据集,它的速度比普通排序更快。 QuickSort 的平均情况为 O(nlogn),而插入的平均情况为 O(n^2)。差别很大不是吗?

    示例实现

    快速排序类

    import java.util.*;
    
    public class QuickSort{
    
    
        public static void swap(int A[] , int x, int y){
    
            int temp = A[x];
            A[x] = A[y];
            A[y] = temp;
    
        }
    
    
    
        public static int[] QSort(int A[],int L, int U){
            Random randomGenerator = new Random();
    
            if ( L >= U){
                return A;
            }
    
            if (L < U) {
                /*                                                                                                                                                                                                                                                                
                  Partion the array around the pivot, which is eventually placed                                                                                                                                                                                                  
                  in the correct location "p"                                                                                                                                                                                                                                     
    
                */
    
                int randomInt = L + randomGenerator.nextInt(U-L);
                swap(A,L,randomInt);
                int T = A[L];
                int p = L;
    
                for(int i= L+1; i<= U; i++){
                    if (T > A[i]){
                        p = p+1;
                        swap(A,p,i);
    
                    }
    
    
                }
    
    
    
            /* Recursively call the QSort(int u, int l) function, this deals with                                                                                                                                                                                                 
               the upper pointer first then the lower.                                                                                                                                                                                                                            
            */
                swap(A,L,p);
                QSort(A,p+1,U);
                QSort(A,L, p-1);
    
            }
            return A;
    
        }
    }
    

    示例主要

    import java.util.*;
    
    public class Main{
        public static void main(String [] args){
    
            int[] intArray =  {1,3,2,4,56,0,4,2,4,7,80,120,99,9,10,67,101,123,12,-1,-8};
    
            System.out.printf("Original Array was:\n%s\n\n",Arrays.toString(intArray));
    
            System.out.printf("Size of Array is: %d\n\n",intArray.length);
    
            QuickSort.QSort(intArray, 0, intArray.length - 1);
            int num = Integer.parseInt(args[0]);
            System.out.println("The sorted array is:");
            System.out.println(Arrays.toString(intArray));
    
        }
    }
    

    上面的示例将对一个 Int 数组进行排序,但您可以轻松地对其进行编辑以对任何对象进行排序(例如您的情况中的 Entry)。我会让你自己弄清楚。

    祝你好运

    【讨论】:

    • 应该提到数据集必须足够大。所以你说的是数百万个条目而不是数千个。
    • 这可能用于需要插入排序的作业。
    • 是的,可能是,但他没有提到他被限制在 50,000 个元素...也许他只是放弃了,因为 50,000 个元素太长了
    • @SeahawksRdaBest - 这取决于 JVM 版本。在 Java 7 中,它是一种用于基元的快速排序和用于对象的 TimSort -stackoverflow.com/questions/4018332/…。但重点是算法比O(N^2)好。
    • @StephenC 我在我的数组实现中再次使用我自己的插入排序。
    【解决方案4】:

    list.add(e)list.get(e) 每次被调用时都会花费 o(n)。您应该避免在旅行时使用它们。

    相反,如果您必须编写自己的链表,您应该跟踪您正在旅行的元素。通过将操作 i++ 和 get(i) 替换为 elem = elem.nextelem = elem.getnext(),(可能还有其他内容,具体取决于您实现链表的方式)。然后你添加一个元素:

    elem.next.parent = e;
    e.next = elem.next;
    elem.next = e;
    e.parent = elem; 
    

    这里我的示例适用于双向链表,elem 表示您当前正在比较要添加的对象的链表中的元素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 2015-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多