【Q&A】去除stl vector中特定位置的多个元素

来源:互联网 发布:linux如何安装java8 编辑:程序博客网 时间:2024/05/10 19:18

我个人常用stl vector来构建基础数据结构,实现一些算法。有时候会有这样的需求,就是我要删除满足某些条件的vector中的元素,例如:删除所有大于零的元素。这就需要遍历整个数组,找到这些元素,并且删除。有两种策略。直观的一种是在遍历的过程中,判断当前元素是否符合删除条件,如果符合,则调用vector的erase函数进行删除,注意这时候数组内容发生变化,iterator原则上失效,会影响到遍历过程。

不过我更在意这种策略的效率。调用erase函数的时候,所有当前删除位置之后的元素都向前移动一个位置。当数组元素是n个、待删除元素是m个的时候,算法复杂度是O(mn)。有点高,不利索。

另一种策略,就是今天列出来的,简单地说就是用空间来换时间。用一个n大小的vector存放0-1变量,1表示目标vector的这个位置的元素要删除,0表示要保留。同时遍历目标vector和这个标志vector,一次性删除所有要删除的元素。在时间上,因为在生成标志vector的时候需要遍历目标vector,在删除的时候,又要遍历一遍,所以总共遍历两遍,复杂度是O(2n)=O(n)。当待删除元素数目比较大的时候,这个算法还是比较有效率的。需要注意的是:1. 在删除过程中要同时遍历两个vector;2. 对于目标vector,需要维护两个下标,一个是遍历的当前位置下标,一个是写元素位置下标

核心代码如下:

bool RmSomeElementsInVec (vector<int> & NumVec, vector<bool> & FlagVec){int iSize = (int)NumVec.size();  //目标数组大小if (iSize != (int)FlagVec.size())  //防御性代码return false;int iWritePos = 0;  // 写位置下标,数组后续元素不断向前移动,这个是下一个要移动到的位置for (int iIterPos = 0; iIterPos < iSize; iIterPos++){if (!FlagVec.at(iIterPos))  // flag是0,表示保留当前位置的元素在数组中{if (iWritePos != iIterPos)  // 将该元素移动到正确的位置上,同时避免在同一个位置上写操作,以提高效率NumVec.at (iWritePos) = NumVec.at (iIterPos);iWritePos++;}}if (iSize != iWritePos)  //缩减数组大小到实际大小NumVec.resize (iWritePos);return true;}

写个程序做了两个假的输入数组NumVec和FlagVec,用来测试上面的代码,测试通过。代码如下:

void InitNumVecAndFlagVec (vector<int> & NumVec, vector<bool> & FlagVec){for (int i=0; i<1000; i++){NumVec.push_back (Array[i]);if (0 == i%2)FlagVec.push_back (1);elseFlagVec.push_back (0);}}

只保留偶数位置上的元素。Array是一个已经初始化好的数组,大小是1000。


还有更进一步的做法。如果生成n大小的flag vector比较浪费空间的话,可以只把需要删除的位置记录下来,记录到一个小于n的vector中。在删除的过程中,类似上面的过程,同时遍历两个vector,进行元素删除,代码如下:

bool RmSomeElementsInVec2 (vector<int> & NumVec, vector<int> & PosVec){int iNumSize = (int)NumVec.size ();if (0 == iNumSize)return false;int iPosSize = (int)PosVec.size();if (0 == iPosSize)return true;for (int i=0, j=0, iWritePos=0; i<iNumSize, j<iPosSize; ){if (i < PosVec.at(j)){if (i != iWritePos)NumVec.at (iWritePos) = NumVec.at (i);i++;iWritePos++;}else if (i == PosVec.at (j)){i++;j++;}elsecerr << "Never get here" << endl;}return true;}

由于遍历的两个vector长度不一样,就需要多一个下标变量。思路和上面的差不多,我就没有补充注释。同样,伪造了NumVec和PosVec,用来测试,代码如下:

void InitNumVecAndPosVec (vector<int> & NumVec, vector<int> & PosVec){for (int i=0; i<1000; i++){NumVec.push_back (Array[i]);if (0 == i%2)PosVec.push_back (i);}}

测试通过。




原创粉丝点击