Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There are a few things that need to be specified:</p> <ul> <li>Do the input lists contain duplicates?</li> <li>Must the result be ordered?</li> </ul> <p>I'll assume that, using <code>std::list</code>, you can cheaply insert at the head or at the tail.</p> <p>Let's say List 1 has N elements and List 2 has M elements.</p> <hr> <h2>Algorithm 1</h2> <p>It iterates over every item of List 1 searching for it in List 2.</p> <p>Assuming that there may be duplicates and that the result must be ordered, the worse case time for the search is that no element in List 1 exists in List 2, hence it's at least:</p> <ul> <li>O(N × M).</li> </ul> <p>To insert the item of List 1 in the right place, you need to iterate List 2 again until the point of insertion. The worse case will be when every item in List 1 is smaller (if List 2 is searched from the beginning) or greater (if List 2 is searched from the end). Since the previous items of List 1 have been inserted in List 2, there would be M iterations for the first item, M + 1 for the second, M + 2 for the third, etc. and M + N - 1 iterations for the last item, for an average of M + (N - 1) / 2 per item.</p> <p>Something like:</p> <ul> <li>N × (M + (N - 1) / 2)</li> </ul> <p>For big-O notation, constant factors don't matter, so:</p> <ul> <li>N × (M + (N - 1))</li> </ul> <p>For big-O notation, non-variable additions don't matter, so:</p> <ul> <li>O(N × (M + N))</li> </ul> <p>Adding to the original O(N × M):</p> <ul> <li>O(N × M) + O(N × (M + N))</li> <li>O(N × M) + O(N × M + N<sup>2</sup>)</li> </ul> <p>The second equation is just to make the constant factor elimination evident, e.g. 2 × (N × M), thus:</p> <ul> <li>O(N × (M + N))</li> <li>O(N<sup>2</sup> + N × M)</li> </ul> <p>These two are equivalent, which ever you like the most.</p> <p>Possible optimizations:</p> <ul> <li><p>If the result doesn't have to be ordered, insertion can be O(1), hence the worse time case is:</p> <ul> <li>O(N × M) <br/><br/></li> </ul></li> <li>Don't just test each List 1 item in List 2 by equality, test if each item by e.g. greater than, so that you can stop searching in List 2 when List 1's item is greater than List 2's item; this wouldn't reduce the worse case, but it would reduce the average case</li> <li><p>Keep the List 2 iterator that points to where List 1's item was found to be greater than List 2's item, to make the sorted insertion O(1); on insertion make sure to keep an iterator that starts at the inserted item, because although List 1 is ordered, it might contain duplicates; with these two, the worse time case becomes:</p> <ul> <li>O(N × M) <br/><br/></li> </ul></li> <li><p>For the next iterations, search for List 1's item in the rest of List 2 with the iterator we kept; this reduces the worse case, because if you reach the end of List 2, you'll be just "removing" duplicates from List 1; with these three, the worse time case becomes:</p> <ul> <li>O(N + M)</li> </ul></li> </ul> <p>By this point, the only difference between this algorithm and Algorithm 2 is that List 2 is changed to contain the result, instead of creating a new list.</p> <hr> <h2>Algorithm 2</h2> <p>This is the merging of the merge sort.</p> <p>You'll be walking every element of List 1 and every element of List 2 once, and insertion is always made at the head or tail of the list, hence the worse case time is:</p> <ul> <li>O(N + M)</li> </ul> <p>If there are duplicates, they're simply discarded. The result is more easily made ordered than not.</p> <hr> <h2>Final Notes</h2> <p>If there are no duplicates, insertion can be optimized in both cases. For instance, with doubly-linked lists, we can easily check if the last element in List 1 is greater than the first element in List 2 or vice-versa, and simply concatenate the lists.</p> <p>This can be further generalized for any tail of List 1 and List 2. For instance, in Algorithm 1, if a List 1's item is not found in List 2, we can concatenate List 2 and the tail of List 1. In Algorithm 2, this is done in the last step.</p> <p>The worse case, when List 1's items and List 2's items are interleaved, is not reduced, but again the average case is reduced, and in many cases by a big factor that makes a big difference In Real Life™.</p> <p>I ignored:</p> <ul> <li>Allocation times</li> <li>Worse space differences in the algorithms</li> <li>Binary search, because you mentioned <strong>lists</strong>, not arrays or trees</li> </ul> <p>I hope I didn't make any blatant mistake.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload