1、已知整数数组a[n]中有重复的元素,试实现函数 void purge( int * a, int &n ); 该函数Post 条件为a[n] 中个元素相异。
常规思路:对表中任一个元素a[i],令 j 从 i+1 到 n-1,将a[i]和a[j]进行比较,若相等,则从顺序表中删除该元素,然后从 j+1 到 n-1 的元素均向前移动一个位置。
弊端: 在删除元素时引起的一连串的元素向前移动非常费时,而且在上述算法中"每发现一个和a[i]相同的元素, 就删除元素+移动数组的做法会使那些值和a[i]不同的元素重复多次移动操作。 时间度为 O(n*n)。
改进:假设我们建一个新表,对原顺序表中每一个当前考察的数据元素,在"新表"中进行查找,如果有相同的则舍弃之,否则就插入到"新表"中。这样就省去了大量的移动操作,虽然外层循环还是 n, 时间复杂度也为 O(n*n),但操作步骤却大大减少了。而且,因为这里是“删除”操作,直接对原数组进行操作,所以那个我们假设的“新表”其实就是被操作的数组,只需要新建一个“指针”(这里为数组下标)即可。
Code:
void purge( int *a, int & n )
{
int k = -1;
for( int i = 0; i<n; ++i )
{
int j = 0;
while( j<=k && a[j] != a[i] )
++j;
if( k==-1 || j>k )
{ ++k; a[k] = a[i]; }
}
n = k+1;
}
2、实现函数 void exchange( int *a, int p1, int n ) , 该函数把数组a[n] 的前 p1 个元素和 剩下的后 n-p1个元素互换位置。 比如原数组为 { 1, 2, 3, 4, 5, 6, 7 } ,执行 exchange( a, 3, 7 ) 后变为 { 4,5,6,7,1,2,3 }。
常规思路: 把后面的最后一个数插到第一个,然后平移数组。依次到把后面元素全部放到新数组的前面。时间复杂度为 O( p1*(n-p1) ).
改进: 为了消除平移数组的巨大消耗,考虑用倒置数组或倒置数组的一部分。导致能把后面一段整体移到前端,再对移到前端的那部分做倒置,又回复了原来的数序。所以可以通过三次倒置(1次全倒+2次部分倒)能达到目的。
推广:同样的方法,可以让数组的任何两段互换位置。
Code:
// 倒置从下标 p1 到 p2 的数组
void invert( int a[], int p1, int p2 )
{
int temp;
for(int i =p1; i<=(p1+p2)/2; ++i )
{ temp = a[i]; a[i] = a[p2+p1-i]; a[p2+p1-i]=temp; }
}
void exchange( int a[], int p1, int n )
{
if( p1>0 && p1<n)
{
int p2 = n-p1;
invert(a, 0, n-1);
invert(a, 0, p2-1);
invert(a, p2, n-1);
}
}