面试题
原数组pArr为[‘A’,’B’,’C’,’D’,’E’],现在给定新位置pPos[3,0,1,4,2],要求只能采用交换的方式并按照新位置进行排序,要求空间复杂度为O(1)。排好序后的序列为
D A B E C.
问题分析
如果不给限制条件这道题还是非常简单的,现在给定了空间复杂度的限制为O(1),也就是说我们不能再原有数组的基础上再来开辟新的空间,这道问题就变得有些复杂了。
第一种解法:不全面
我们先给出问题的第一种解法,它利用了快速排序挖坑法的思想。
具体的步骤和算法见下图:
代码如下:
void SwapSort(vector<char>& pArr,vector<int>& pPos)
{
assert(pArr.size()>0);
assert(pPos.size()>0);
assert(pArr.size() == pPos.size());
char tmp = pArr[0];
size_t counts = 1;
int index = 0;
while (counts < pArr.size()) //循环终止条件
{
if (index != pPos[index])
{
pArr[index] = pArr[pPos[index]];
index = pPos[index];
counts++;
}
else
{
index++;
counts++;
}
}
//最后一次填坑需要进行测试
if (index != pPos[index])
{
pArr[index] = tmp;
}
}
但是这种解法其实是有缺陷的,当我们给出的新位置的元素如果两次如果是之前已经交换过的元素的下标,那么排序就会出现问题,如我们给出pPos=[3 1 0 2 4]结果就会出现问题,因为当我们排道pArr=[D B A C E]时,坑已经在正确的位置上了,所以才会导致我们后面填坑时又重新出现了A导致排序序列不正确。
改进算法
为了防止pArr中已经“坑”已经被放在正确位置又在后面重复填充,所以每次我们将坑填完后需要重新挖坑,新挖的坑为填坑后的下标出的第一个index!=pPos[index]的下标,同时每次交换元素时我们要把当前下标更新为pPos里的下标。
代码如下:
void NewSwapSort(vector<char>&pArr, vector<int>& pPos)
{
assert(pArr.size()>0);
assert(pPos.size()>0);
assert(pArr.size() == pPos.size());
char tmp = pArr[0]; //挖坑
int tmpPos = 0; //拿到所挖的坑位置的下标
size_t counts = 1;
int index = 0;
while (counts < pArr.size())
{
while (index == pPos[index])
{
index++;
counts++;
}
/*if (index == pArr.size())
break;*/
//在index处挖坑
tmp = pArr[index];
tmpPos = index;
while (1)
{
//完成交换的条件
if (index == pArr.size())
break;
if (index != pPos[index])
{
if (tmpPos == pPos[index])
{
//如果当前pPos中的下标与当前坑的下标相同
//把坑的元素填入pArr中
pArr[index] = tmp;
pPos[index] = index;
index++;
break;
}
//如果当前pPos中的下标与当前坑的下标不同
//则交换pArr中pPos[index]中的元素给当前位置
pArr[index] = pArr[pPos[index]];
//更新pPos中的对应下标和当前下标
int j = pPos[index];
pPos[index] = index;
index = j;
counts++;
}
else
{
index++;
counts++;
}
}
}
}