site.base_url }}/">The Bear's Den -->

The Bear's Den

Enter at your own risk

Sorting Without Sort

Task 2: Relative Sort

Submitted by: Mohammad Sajid Anwar


You are given two list of integers, @list1 and @list2. The elements in the @list2 are distinct and also in the @list1.

Write a script to sort the elements in the @list1 such that the relative order of items in @list1 is same as in the @list2. Elements that is missing in @list2 should be placed at the end of @list1 in ascending order.

Example 1

Input: @list1 = (2, 3, 9, 3, 1, 4, 6, 7, 2, 8, 5)
       @list2 = (2, 1, 4, 3, 5, 6)
Ouput: (2, 2, 1, 4, 3, 3, 5, 6, 7, 8, 9)

Example 2

Input: @list1 = (3, 3, 4, 6, 2, 4, 2, 1, 3)
       @list2 = (1, 3, 2)
Ouput: (1, 3, 3, 3, 2, 2, 4, 4, 6)

Example 3

Input: @list1 = (3, 0, 5, 0, 2, 1, 4, 1, 1)
       @list2 = (1, 0, 3, 2)
Ouput: (1, 1, 1, 0, 0, 3, 2, 4, 5)

Solution

This is a follow-up to my post for challenge 284.

While solving that task, I became obsessed with the idea of collecting the list items in a binary search tree (BST, more specifically: a balanced BST), while classifying these as members of partition 1 or partition 2. In week 284 I didn’t find a solution with an acceptable performance. Afterwards I discovered the exellent module Tree::RB::XS as the perfect solution.

We create two BSTs: one is pre-initialized with the members of @list2 and with recent tracking enabled and a second empty one. While scanning through @list1, its members are counted in the first tree if the key exists or in the second otherwise. The way I chose to count the elements is highly questionable. I asked about it at PerlMonks and I’m afraid it is not commonly accepted as good practice.

In a second pass, the first tree is traversed in order of initialization and the second in key-order. Elements are repeated according to their count.

use strict;
use warnings;
use Tree::RB::XS ':cmp';
use experimental 'signatures';

sub relative_sort ($list1, $list2) {
    my $part1 = Tree::RB::XS->new(compare_fn => CMP_INT, track_recent => 1);
    my $part2 = Tree::RB::XS->new(compare_fn => CMP_INT);
    $part1->insert($_, 0) for @$list2;
    ${\($part1->get($_) // $part2->get_or_add($_))}++ for @$list1;

    [
        map +($_->key) x $_->value,
            $part1->iter_newer->next('*'),
            $part2->iter->next('*')
    ];
}

See the full solution to task 2.


If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.