将元素插入已排序的向量中

Posted

技术标签:

【中文标题】将元素插入已排序的向量中【英文标题】:Inserting element into sorted vector 【发布时间】:2012-10-05 05:31:19 【问题描述】:

我编写了一个 C++ 程序,目的是快速将一个元素插入一个排序的向量中。它有时有效,但并非一直有效,我无法弄清楚原因。当我用纸和铅笔按照算法进行操作时,它可以解决,但是出了点问题。请帮忙?

#include <time.h>
#include <cstdlib>
#include <vector>
#include <iostream>
using namespace std;

vector<int> sortedVec;

int main() 
    // Random seed
    srand(time(NULL));

    // Put in n random elements
    for (int i = 0; i < 10; i++) sortedVec.push_back(rand()%10);

    // Sort the vector
    bool swapped = true;
    int endDecrement = 0;
    while (swapped) 
        swapped = false;
        endDecrement++;
        for (int i = 0; i < sortedVec.size()-endDecrement; i++) 
            if (sortedVec.at(i) > sortedVec.at(i+1)) 
                int swap = sortedVec.at(i);
                sortedVec.at(i) = sortedVec.at(i+1);
                sortedVec.at(i+1) = swap;
                swapped = true;
            
        
    

    cout<<"Sorted random list:"<<endl;
    for (int i = 0; i < sortedVec.size(); i++) cout<<sortedVec.at(i)<<endl;

    int toInsert = rand()%10;
    cout<<"Random element to insert = "<<toInsert<<endl;

    // Insert a random int to the sorted vector
    int minIndex = 0;
    int maxIndex = sortedVec.size()-1;
    while (true) 
        int mid = (maxIndex-minIndex)>>1;
        if (toInsert == sortedVec.at(mid) || maxIndex-minIndex < 2) 
            sortedVec.insert(sortedVec.begin()+mid, toInsert);
            break;
        
        else if (toInsert < sortedVec.at(mid)) maxIndex = mid;
        else if (toInsert > sortedVec.at(mid)) minIndex = mid;
    

    cout<<"Random list with inserted element:"<<endl;
    for (int i = 0; i < sortedVec.size(); i++) cout<<sortedVec.at(i)<<endl;

    return 0;

【问题讨论】:

你为什么不使用std::set 来为你排序元素?而事件如果你想使用vector,你可以使用std::sort实现排序,并使用std::equal_range算法找到插入的位置,而不是自己写。 如果您打算这样做,是否有理由不使用std::sort 进行排序并使用std::upper_bound 查找插入点? (但如果你想按顺序插入,这真的不是最好的方法,IMO)。 @Jerry Coffin,这是一个虚拟程序,用于在不同的程序中解决相同的问题。另一个程序执行 A* 搜索并将新节点插入到名为树的向量中。问题是我不知道如何将 std::upper_bound 与 Node 类中的值一起使用(我是 C++ 的新手)。 Node 类有一个名为 fVal 的 int,我想用它来确定插入向量的位置。如果我能得到 std::upper_bound 来检查 tree.at(whatever position).fVal 那就太好了。 "a vector named tree" 可能应该是 std::setstd::multiset,具体取决于您的需要 - 尽管我同意,如果您有其他代码假设它是 vector,则迁移可以是也很痛。 @asimes:是的,你可以这样做——upper_bound 允许你指定一个比较函数/函子。在你的情况下,它的逻辑只是return a.fval &lt; b.fval; 【参考方案1】:

正如 cmets 所指出的,您提出的解决基本问题的方法相当复杂。

在随机生成器初始化中将time(NULL) 替换为一个常数,可以使您的问题更有吸引力,该常数可以调试观察到的行为。

没有那个,我会为罪魁祸首出价:

int maxIndex = sortedVec.size()-1;

我觉得应该是

int maxIndex = sortedVec.size();

或者你从不考虑顶部元素。

【讨论】:

不幸的是,它仍然不能每次都工作。这是一个虚拟程序,用于解决类似的棘手问题。另一个问题总是有未知的传入值被插入到一个向量中,该向量充满了具有未知但已排序值的元素。 如果你打算在生产代码中使用它,我也强烈建议使用已经在 STL 中编码的函数。还有第二个罪魁祸首:maxIndex-minIndex &lt; 2 暗示您没有进行最后一次比较。应该是maxIndex == minIndex @LeGEC,我很确定对于每种可能的情况,maxIndex 并不总是等于 minIndex。我确实尝试过进行更改,但仍然无法正常工作。在进行 chac 的更改并使用 maxIndex-minIndex 最后一点,mid 应该是 (maxIndex+minIndex)&gt;&gt;1;(而不是 (maxIndex-minIndex)&gt;&gt;1;)。我坚持:使用标准算法。 std::upper_bound 有一个模板版本,您可以在其中提供比较功能。 @LeGEC:这是真正的错误!当您需要使用 STL 时,您显然是完全正确的。【参考方案2】:

基于 cmets,假设我们有一个类似这样的结构:

struct data 
    int fval;
    // other stuff we don't care about right now
;

而且,我们假设我们有一个向量:

std::vector<data> items;

如果我们想在向量中按顺序插入一个新项目,我们真的想在std::insert之后进行二分搜索。这需要 O(log N) 搜索,然后是 O(N) 插入。相反,我们可以将两者结合起来,因此我们只需要一个 O(N) 操作即可找到正确的位置并进行插入。这是一个简单的版本:

void insert(std::vector<int> &vec, int new_val)  
    if (vec.empty()) 
        vec.push_back(new_val);
        return;
    

    vec.resize(vec.size()+1);
    std::vector<int>::reverse_iterator pos = vec.rbegin();

    for ( ; *(pos+1) > new_val && (pos+1) != vec.rend(); ++pos)
        *pos = *(pos+1);
    *pos = new_val;

在您的情况下,您想插入一个data 结构,并比较(pos+1)-&gt;fval &gt; new_val.fval,但基本思想几乎相同。实际上,这应该可能实现为通用算法,但我目前没有时间。

【讨论】:

【参考方案3】:

标准库中有用于排序的工具:

#include <algorithm>
#include <vector>

template <typename T, typename A, typename C>
class SortedVector 
public:
    SortedVector() 

    // Initialization
    template <typename It>
    SortedVector(It begin, It end): _data(begin, end) 
        std::sort(_data.begin(), _data.end(), _comparator);

        // if we wanted unicity
        _data.erase(std::unique(_data.begin(), _data.end(), _comparator), _data.end());
    

    // Addition of element (without checking for unicity)
    void add(T const& element) 
        _data.push_back(element);
        std::inplace_merge(_data.begin(), prev(_data.end()), _data.end(), _comparator);
        // or simply: std::sort(_data.begin(), _data.end(), _comparator);
        // it is surprisingly efficient actually because most sort implementations
        // account for partially sorted range. It is not, however, stable.
    

    // Addition of element with unicity check
    bool add(T const& element) 
        typename std::vector<T, A>::iterator it =
            std::lower_bound(_data.begin(), _data.end(), element, _comparator);
        if (it != _data.end() and not _comparator(element, *it)) 
            return false;
        
        size_t const n = it - _data.begin();

        _data.push_back(element);
        std::copy(_data.begin() + n, _data.end() - 1, _data.begin() + n + 1);
        // C++11: std::move
        _data[n] = element;
    

private:
    std::vector<T, A> _data;
    C _comparator;
;

【讨论】:

+1 但我认为删除n 会更好,因为std::copy(it, std::prev(_data.end()), std::next(it)); *it = element; 也会这样做,而且会是“std-cleaner”。而且我认为您需要在第一个add 中包含&lt;iterator&gt;std-qualify prev。好吧,也许没有 C++11,但it 还是比_data.begin()+n 好。 @ChristianRau:实际上,itpush_back 调用(可能)无效。 @ChristianRau:我不会说你愚蠢,当搞砸这么容易时,界面有点糟糕......【参考方案4】:

我写了一个 C++ 程序,目的是快速将一个元素插入一个排序的向量中

你不应该那样做。 std::vector 不适合快速插入到自定义位置。 编辑:顺便说一句,您正在更换,而不是插入。因为使用矢量是可以的。

您的代码也因不使用标准函数而受到影响。 std::sort,如果你真的想手动交换元素,你也有 std::swap。

这也是不必要的:

int mid = (maxIndex-minIndex)>>1;

编译器可以并且很容易将 /2 优化为 >>1;

编辑:

您的代码已损坏,希望这会告诉您原因: http://ideone.com/pV5oW

【讨论】:

【参考方案5】:

我更改了它,并且很确定它现在适用于所有情况:

if (toInsert < sortedVec.at(0)) sortedVec.insert(sortedVec.begin(), toInsert);
else if (toInsert > sortedVec.at(sortedVec.size()-1)) sortedVec.push_back(toInsert);
else   
    int minIndex = 0;
    int maxIndex = sortedVec.size();
    while (true) 
        int mid = (maxIndex+minIndex)>>1;
        if (toInsert == sortedVec.at(mid) || maxIndex-minIndex < 2) 
            sortedVec.insert(sortedVec.begin()+mid+1, toInsert);
            break;
        
        else if (toInsert < sortedVec.at(mid)) maxIndex = mid;
        else if (toInsert > sortedVec.at(mid)) minIndex = mid;
    

@LeGEC 和@chac,感谢您研究我发布的算法,它帮助很大。 @Jerry Coffin,我确实让 std::upper_bound 可以为此工作,但不能为我的 Node 类的变量工作,不过也谢谢你。

【讨论】:

它不起作用。先插入 1 而不是 0 将产生向量 (1,0) 这里你可以找到下界cplusplus.com/reference/algorithm/lower_bound的正确实现@ 您能进一步解释一下吗?你的意思是如果你从一个空的vector开始,插入1,然后是0?我尝试这样做时遇到错误。当我写这篇文章时,我假设列表中已经包含元素。 是的,在开始时它不适用于空向量时就是这种情况。 我想我的意思是你是如何让它产生一个元素 V = 1, 0 的向量。当我更改程序以使用带有空向量的代码时,它只会引发错误。

以上是关于将元素插入已排序的向量中的主要内容,如果未能解决你的问题,请参考以下文章

将元素插入到有序向量中的最有效方法是什么?

折半(二分)插入排序

插入排序(Insertion Sort)

排序算法2--插入排序--折半插入排序

将元素随机插入向量时随机减速

插入排序及其优化