简体   繁体   English

插入排序LinkedList Java

[英]Insertion Sort LinkedList Java

I'm trying to write an Insertion sort for a LinkedList, I have got a working method but it is incredibly slow. 我正在尝试为LinkedList写一个插入排序,我有一个有效的方法,但是它的运行速度非常慢。 Over an hour to add&sort 50,000 elements. 一个多小时即可添加和排序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);    
}

I know i could use Collections.Sort but for this assignment I am required to write my own LinkedList. 我知道我可以使用Collections.Sort,但是对于此分配,我需要编写自己的LinkedList。 I'm not asking for a full solution just some pointers. 我不要求仅提供一些提示的完整解决方案。

First of all, insertion sort on a List is going to be slow ( O(N^2) ) ... no matter how you do it. 首先,对List插入排序将很慢( O(N^2) )...不管您如何执行。 But you appear to have implemented it as O(N^3) . 但是您似乎已将其实现为O(N^3)

Here is your code ... which will be called N times, to add each list element. 这是您的代码...将被调用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   
}

At "HERE #1" we iterate up to M times where M is the current (partial) list length; 在“ HERE#1”处,我们最多迭代M次,其中M是当前(部分)列表长度; ie O(M) . O(M) This is inherent in an insertion sort. 这是插入排序中固有的。 However, depending on how you implemented the size() method, you could have turned the iteration into a O(M^2) sequence of operations. 但是,取决于您如何实现size()方法,您可能已将迭代转换为O(M^2)个操作序列。 (The LinkedList.size() method just returns the value of a size variable. No problem here. But if size() counted the elements ... ) LinkedList.size()方法仅返回一个size变量的值。在这里没有问题。但是,如果size()算出了元素...)

At "HERE #2" we have a fetch and a comparison. 在“ HERE#2”处,我们进行了获取和比较。 The comparison ( compareTo(...) ) is cheap, but the get(i) operation on a linked list involves traversing the list from the beginning. 比较( compareTo(...) )很便宜,但是对链表的get(i)操作涉及从头开始遍历该列表。 That is an O(M) operation. 那是一个O(M)操作。 And since you make the get(i) call O(M) times per insert call, this makes the call O(M^2) and the sort O(N^3) . 并且由于您使每个insert调用的get(i)调用O(M)次,因此使调用O(M^2)和排序O(N^3)

At "HERE #3" the add(i,e) repeats the list traversal of the previous get(i) call. 在“ HERE#3”处, add(i,e)重复前一个get(i)调用的列表遍历。 But that's not so bad because you only execute that add(i,e) call once per insert call. 但这还不错,因为每个insert调用只执行一次add(i,e)调用。 So the overall complexity is not affected. 因此,总体复杂度不受影响。

At "HERE #4" the add() operation could be either O(1) or O(M) depending on how it is implemented. 在“ HERE#4”处,取决于实现方式, add()操作可以为O(1)O(M) (For LinkedList.add() it is O(1) because the list data structure keeps a reference to the last node of the list.) Either way, overall complexity is not affected. (对于LinkedList.add()它为O(1)因为列表数据结构保留对列表最后一个节点的引用。)两种方式都不会影响总体复杂性。

In short: 简而言之:

  • The code at #2 definitely make this an O(N^3) sort. #2处的代码肯定使它成为O(N^3)排序。

  • The code at #1 could also make it O(N^3) ... but not with the standard LinkedList class. #1处的代码也可以将其设置为O(N^3) ...,但不能使用标准LinkedList类。


So what to do? 那么该怎么办?

  • One approach is to recode the insert operation so that it traverses the list using the next and prev fields, etcetera directly. 一种方法是重新编码insert操作,以便它直接使用nextprev字段等遍历列表。 There should not be calls to any of the "higher level" list operations: size, get(i), add(e) or add(i, e). 不应调用任何“高级”列表操作:大小,get(i),add(e)或add(i,e)。

    However, if you are implementing this by extending or wrapping LinkedList , this is not an option. 但是,如果要通过扩展或包装LinkedList来实现此目的,则不是一种选择。 Those fields are private. 这些字段是私有的。

  • If you are extending or wrapping LinkedList , then the solution is to use the listIterator() method to give you a ListIterator , and use that for efficient traversal. 如果要扩展或包装LinkedList ,则解决方案是使用listIterator()方法为您提供ListIterator ,并将其用于有效的遍历。 The add operation on a ListIterator is O(1) . ListIteratoradd操作为O(1)

  • If (hypothetically) you were looking for the fastest way to sort a (large) LinkedList , then the solution is to use Collections.sort . 如果(假设)您正在寻找对(大型) LinkedList进行排序的最快方法,那么解决方案是使用Collections.sort Under the covers, that method copies the list contents to an array, does an O(NlogN) sort on the array, and reconstructs the list from the sorted array. O(NlogN) ,该方法将列表内容复制到数组,对数组进行O(NlogN)排序,然后从排序后的数组中重建列表。

根据响应,由于性能更好,因此应使用ListIterator.add()而不是List.add

What about using a faster sorting algorithm? 使用更快的排序算法呢?

Here is something known as QuickSort . 这就是所谓的QuickSort Its way faster then normal sorts for larger data sets. 对于大型数据集,其速度比普通排序更快。 QuickSort has a average case of O(nlogn) while insertion only has a average case of O(n^2). QuickSort具有O(nlogn)的平均情况,而插入仅具有O(n ^ 2)的平均情况。 Big difference isn't it? 是不是很大的不同?

Sample implementation 实施范例

QuickSort Class 快速排序类

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;

    }
}

Sample Main 样本主

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));

    }
}

The above example will sort an Int array but you can easily edit it to sort any object(for example Entry in your case). 上面的示例将对一个Int数组进行排序,但是您可以轻松地对其进行编辑以对任何对象进行排序(例如,您的情况下为Entry)。 Ill let you figure that out yourself. 病会让你自己弄清楚。

Good Luck 祝好运

list.add(e) and list.get(e) will take o(n) each time they are called. list.add(e)list.get(e) )每次调用都将占用o(n)。 You should avoid to use them when you travel your list. 您在旅行列表时应避免使用它们。

Instead, if you have to write your own linked list you should keep track of the elements you are traveling. 相反,如果您必须编写自己的链接列表,则应跟踪所旅行的元素。 by replacing the operation i++ and get(i) by elem = elem.next or elem = elem.getnext() , (maybe something else depending on how you implemented your linked list). 通过将操作i ++和get(i)替换为elem = elem.nextelem = elem.getnext() ,(可能还取决于您实现链接列表的方式)。 Then you add an element by doing: 然后通过执行以下操作添加一个元素:

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

here my example works for a doubly linked list and elem represent the element in the linked list you are currently comparing your object you want to add. 在这里,我的示例适用于双向链表,而elem表示当前正在比较要添加的对象的链表中的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM