最近开始刷《剑指offer》,说实话开始的有点晚了,应该早点开始的。刷题顺序没有按照书上的顺序来,保证刷完即可。
题目:调整数组顺序使奇数位于偶数前面
首先,【1】这个题没有要求奇数相对于原数组奇数的位置不变和偶数相对原数组的位置不变,【2】也没有要求不能重新再创建数组,所以基于上述两方面这个题有多种解法。
1.重新定义一个vector数组,用push_back()先将奇数插入新数组,再用一次push_back()将偶数插入新数组。这样就保证了奇数、偶数相对于原数组的奇数、偶数的顺序不变,满足【1】。
class Solution {
public:
//新创建一个vector数组,保证奇数、偶数的相对顺序不变,时间复杂度O(n),但是空间复杂度
void reOrderArray1(vector<int> &array) {
if(array.empty()) //1.判断vector数组是否为空
{
return ;
}
vector<int> brr; //2.创建新的vector数组
int len=array.size();
for(int i=0;i<len;++i) //3.先遍历数组,遇到奇数则插入新定义的数组中
{
if((array[i] &0x1) == 1)
{
brr.push_back(array[i]);
}
}
for(int i=0;i<len;++i) //4.再遍历数组,遇到偶数则插入新定义的数组中
{
if((array[i] &0x1) != 1)
{
brr.push_back(array[i]);
}
}
//array = brr; //5.将新数组的值赋给原数组,vector支持这样赋值
copy(brr.begin(),brr.end(),array.begin());
//copy也可以,array已经实例化了,有内存,若是新定义的vector<int> array,
//一定不能用array.begin(),它底层还没有内存,编译器会报错
}
};
int main()
{
vector<int> array;
array.push_back (1);
array.push_back (2);
array.push_back (3);
array.push_back (4);
array.push_back (5);
Solution s;
s.reOrderArray1(array);
vector<int>::iterator it =array.begin();//用数组方式遍历也可以
for(it;it!=array.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
2.借鉴冒泡法,如果相邻两个元素“前偶后奇”则交换,时间复杂度为O(n^2)
class Solution
{
void reOrderArray2(vector<int> &array)
{
int len=array.size();
for(int i=0;i<len;++i)
{
for(int j=0;j<len-i-1;++j)
{
if((array[j] & 0x1) == 0 && (array[j+1] & 0x1) == 1)
{
swap(array[j],array[j+1]);
}
}
}
}
};
int main()
{
vector<int> array;
array.push_back (1);
array.push_back (2);
array.push_back (3);
array.push_back (4);
array.push_back (5);
Solution s;
s.reOrderArray1(array);
vector<int>::iterator it =array.begin();//用数组方式遍历也可以
for(it;it!=array.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
3.不重新定义vector数组,利用vector支持[]运算符进行数组访问的特点,定义前后两个指针,遍历,满足“前偶后奇”时交换,但是这样会破坏原数组元素的顺序
class Solution {
public:
void reOrderArray3(vector<int> &array)
{
if(array.empty()) //1.判断vector数组是否为空
{
return ;
}
int len = array.size();
int left=0; //2.记录偶数的下标
int right=len-1; //3.记录奇数的下标,满足“前偶后奇”时交换
while(left < right) //4.遍历array数组
{
while(left < right && (array[left] & 0x1) == 1)//5.如果当前值为奇数,则++,直到遇到第一个偶数暂时跳出循环
{
++left;
}
while(left < right && (array[right] & 0x1) != 1) //6.如果当前值为偶数,则--,直到遇到第一个偶数暂时跳出循环
{
--right;
}
if(left < right ) //7.满足“前偶后奇”并且前下标小于后下标的时候,交换
{
swap(array[left],array[right]);//定义临时变量进行交换也可以
}
}
}
};
4.可扩展性高的解法:传参传的是一维数组,然后将对奇偶数的判断放在一个单独的函数里,再在另一个函数里通过函数指针调用判断函数,这个判断函数可以有多个,分别有不同的判断标准
bool IsEven(int n)//判断一个数是否是偶数
{
return (n & 0x1) == 0;
}
void Reorder(int *p,unsigned int len,bool(*fun)(int))//第三个参数是函数指针
{
if(p == NULL || len<=0)//1.判断数组是否为空
{
return ;
}
int *pleft=p;//2.第一个指针,从头开始向后移动
int *pright=p+len-1;//3.第二个指针,从后向前移动
while(pleft < pright) //4.如果第一个指针在第二个指针的前面
{
while(pleft < pright && !fun(*pleft))//5.如果*pleft为奇数,继续向后移动直到出现偶数
{
++pleft;
}
while(pleft < pright && fun(*pright))//6.如果*pright为偶数,向前移动直到出现奇数
{
--pright;
}
if(pleft < pright)//7.满足“前偶后奇”则交换
{
swap(*pleft,*pright);
}
}
}
void reOrderArray4(int *p,unsigned int len)
{
Reorder(p,len,IsEven);//传入ISEven函数判断一个数是否是偶数的函数
}
int main()
{
int arr[5]={1,2,3,4,5};
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
reOrderArray4(arr,sizeof(arr)/sizeof(arr[0]));
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
return 0;
}
第四种方法的好处是我们可以扩展这个程序
扩展一:比如题目改为把数组中的负数移到非负数前面,可以写出以下判断函数:
bool IsPositive(int n)//是否是正数
{
if(n == 0)
{
throw;
}
return (n > 0)?true:false;//是正数输出true,负数输出false
}
扩展二:比如把题目改为把能被3整除的数放在不能被3整除的数的前面,可以写出以下函数:
bool DivOf3(int n) //判断是否能被3整除
{
return n%3 == 0;//可以被3整除输出true,不能被3整除输出false
}
测试用例:
int main()
{
int arr[]={1,2,3,4,5,6,7};//奇数 偶数交替出现
int brr[]={2,4,6,8,0,1,3,5,7,9};//偶数都在奇数前面
int crr[]={1,3,5,7,9,2,4,6,8,0}; //奇数都在偶数前面
int drr[]={3};//只有一个元素
return 0;
}