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

我个人常用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);
		else
			FlagVec.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++;
		}
		else
			cerr << "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);
	}
}

测试通过。




你可能感兴趣的:(数据结构,算法,vector,测试,iterator)