c – 快速排序的最大元素交换数

我正在提高我的算法知识,阅读Robert Sedgewick的“算法”一书并完成练习.我遇到的困难是:

What is the maximum number of times during the execution of Quick.sort()
that the largest item can be exchanged, for an array of length N ?

我已经通过实验确定,假设阵列中的所有元素都是不同的,则最大项目的最大交换次数为floor(N / 2).我如何以数学方式证明这一点?如果我错了,我的错是什么?

我已经找到了几个关于这个问题的提及(例如this one),但是,答案与我的结果不符.这个答案表明最大数字是N-1,但我无法找到这样一个数组,当我使用我的快速排序版本(见下文)对它进行排序时,这将给出我最大项目的N-1交换.

我使用的快速排序代码:

template<typename BiDirIterator, typename Compare = std::less<typename BiDirIterator::value_type>>
BiDirIterator partition(BiDirIterator begin, BiDirIterator end, Compare compare = Compare())
{
    auto partition_item = begin;
    while (true)
    {
        while (++begin != end && !compare(*partition_item, *begin));
        while (begin != end && !compare(*--end, *partition_item));

        if (begin == end)
            break;

        std::iter_swap(begin, end);
    }

    if (partition_item != --begin)
        std::iter_swap(partition_item, begin);

    return begin;
}

template<typename BiDirIterator, typename Compare = std::less<typename BiDirIterator::value_type>>
void quicksort(BiDirIterator begin, BiDirIterator end, Compare compare = Compare())
{
    if (begin == end || std::next(begin) == end)
        return;

    auto pos = partition(begin, end, compare);
    quicksort(begin, pos, compare);
    quicksort(++pos, end, compare);
}

我用来计算最大项目的交换次数的代码:

struct exchange_counter
{
    exchange_counter(int value)
        : value(value)
    {
    }

    int value;
    int number_of_exchanges = 0;

    exchange_counter(const exchange_counter& other) = default;
    exchange_counter& operator=(const exchange_counter& other) = default;
    exchange_counter(exchange_counter&& other) = default;

    exchange_counter& operator=(exchange_counter&& other)
    {
        value = other.value;
        number_of_exchanges = other.number_of_exchanges + 1;
        return *this;
    }

    friend bool operator<(const exchange_counter& left, const exchange_counter& right) noexcept
    {
        return left.value < right.value;
    }

    friend bool operator==(const exchange_counter& left, const exchange_counter& right) noexcept
    {
        return left.value == right.value;
    }
};

for (int i = 1; i != 15; ++i)
{
    std::vector<exchange_counter> values;
    for (int j = 0; j != i; ++j)
        values.emplace_back(j);

    auto max_element = i - 1;
    auto max_number_of_exchanges = 0;
    do
    {
        for (auto& value : values)
            value.number_of_exchanges = 0;

        auto copy = values;
        quicksort(copy.begin(), copy.end());
        max_number_of_exchanges = (std::max)(max_number_of_exchanges,
            std::find(copy.begin(), copy.end(), max_element)->number_of_exchanges);
    }
    while (std::next_permutation(values.begin(), values.end()));

    std::cout << "Elements: " << i << "; max exchanges: " << max_number_of_exchanges << std::endl;
}

PS.如果我使用相同的方法在Visual Studio 2015中测试std :: sort(实现为quicksort),则最大项的交换次数为N-1.

最佳答案 每次我们对数组进行分区时,最大项必须移动2个位置,以便最大次数交换它.它不能仅移动1个位置,因为在这种情况下它将成为枢轴元素并将移动到其最终位置.例如,请考虑以下数组:

4 10 3 x x x ...
P i  j

在对数组进行分区后,最大元素(10)向右移动1个位置

3 4 10 x x x ...
    P

但现在最大的项目成为一个枢轴元素,并将移动到数组的末尾,只添加一个交换.

相反,我们需要安排项目,以便最大项目移动2个位置,保持前面的1个项目成为枢轴元素:

2 10 4 1 x x x ...
P i    j

分区后:

1 2 4 10 x x x ...
    P i    j

最大项目每次移动2个位置,因此交换次数为楼层(N / 2).

示例(N = 10)

2 10 4 1 6 3 8 5 7 9

在这种情况下,最大项目(10)的最大交换次数是5.

点赞