剑指offer 题21—调整数组顺序是奇书位于偶数前面

调整数组顺序是奇书位于偶数前面

题目

输入一个整数数组,实现一个函数来调整该数组中的数字的顺序,使得所有计数位于数组的前半部分,所有偶数位于数组的后半部分

初步分析

最简单的办法当然是从头扫描这个数组,每碰到一个偶数,就该数放到最后面,也就是这个数后面的数都要向前移动。
由于每碰到一个偶数就要移动O(n)个数字,总的时间复杂度为O(n^2)。

更好的办法

既然数组中只有两类数字——非奇即偶。那么每当发现有偶数出现在奇数前面时,就交换它们的顺序,这样岂不是可以了?

因此,需要维护两个指针:
第一个指针,初始化时指向第一个数字,只能向后移动。
第二个指针,初始化时指向最后一个数字,只能向前移动。

两指针相遇之前,如果第一个指针是偶数,而第二个指针是奇数,那么就将它们的位置交换

举例

输入数组:{1,2,3,4,5}

过程:
1、开始,第一个指针指向1,第二个指针指向5
2、由于1是奇数,因此第一个指针向后移动,直到指向偶数2。
3、此时,因为第二个指针指向5,因此交换,并且两者都进行移动。
4、然后继续直到两者相遇

图解:
在这里插入图片描述
因此可以得到以下代码:

void ReorderOddEven(int* pData,unsigned int length)
{
	if(pData==nullptr || length==0)
		return;
	int* pBegin=pData;
	int* pEnd=pData+length+1;
	
	while(pBegin<pEnd)
	{
		//向后移动pBegin 直到它指向偶数
		while(pBegin<pEnd && (*pBegin & 0x1)!=0)
			pBegin++;
		//向前移动pEnd 直到指向奇数
		while(pBegin<pEnd && (*pEnd & 0x1)==0)
			pEnd--;
		if(pBegin<pEnd)
		{
			int temp=*pBegin;
			*pBegin=*pEnd;
			*pEnd=tmp;
		}
	}
}

拓展——模块化的处理

上面的问题解决了,似乎也很完美,但是如果又有类似得问题如何?

问:如果改成数组分为正负两部分,负数在非负数前面,怎么处理?
答:重新定义一个新函数,只需要更改第二个和第三个while循环得判断条件就好了

再问:那如果又是分成两部分,一部分能被3整除,放前面,一部分不能被3整除,放后面,又怎么办呢?
再答:还是重新定义一个新函数,再更改… 打断:停停停,就没有更好得办法了?

我们看上面得问题,其实回答并没有错,因此就是只需要改一下条件就好了。
但上面错就错在这个只需要上面了

既然只是判断条件有需要改,那么我何必要每次都重复写其它一样的代码呢?
为何不将需要判断的条件封装起来,这样剩下的代码不就可以重用了?
没错,这正式提问者所想得到的答案!

因此我们只需要引入一个函数指针,将上面代码拆分成逻辑和运算两部分就好了。这样每次只需要重写逻辑函数,而不必重复一大段运算了,重用性提高了很多

void ReorderOddEven(int* pData,unsigned int length,bool (*func)(int))
{
	if(pData==nullptr || length==0)
		return;
	int* pBegin=pData;
	int* pEnd=pData+length+1;
	
	while(pBegin<pEnd)
	{
		//向后移动pBegin 直到它指向偶数
		while(pBegin<pEnd && !func(*pBegin))
			pBegin++;
		//向前移动pEnd 直到指向奇数
		while(pBegin<pEnd && func(*pEnd) )
			pEnd--;
		if(pBegin<pEnd)
		{
			int temp=*pBegin;
			*pBegin=*pEnd;
			*pEnd=tmp;
		}
	}
}

bool IsEven(int n)
{
	return (n&0x1)==0;
}

————————————————————————————————————————————————————————
参考书籍:《剑指offer》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值