【问题标题】:Inserting element into sorted vector将元素插入已排序的向量中
【发布时间】:2025-11-27 18:55:01
【问题描述】:

我编写了一个 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;

标签: c++ sorting vector


【解决方案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} 的向量。当我更改程序以使用带有空向量的代码时,它只会引发错误。