【剑指offer】调整数组顺序使奇数位于偶数前面

文章目录

  • 题目
  • 思路
  • 相对位置可以改变的思路
  • 相对位置不能改变的思路


题目

题目链接入口:调整数组顺序使奇数位于偶数前面
【剑指offer】调整数组顺序使奇数位于偶数前面_第1张图片

示例1:
输入:[1,2,3,4,5,6]
结果:[1,3,5,2,4,6]

示例2:
输入:[1,2,2,3,4,4,5,6,7]
结果:[1,3,5,7,2,2,4,4,6]

思路

1.核心考点:数组操作,排序(插入排序)思想的扩展使用
2.原题变形:这道题目加了一个条件:要求相对位置不变。这里先讲讲相对位置可以改变的情况,这样能更好的理解“什么叫相对位置不变”。

相对位置可以改变的思路

(1)使用双(头尾)指针法,一个指向数组的第一个元素,一个指向数组的最后一个元素。
【剑指offer】调整数组顺序使奇数位于偶数前面_第2张图片

(2)当begin指向的元素是偶数,begin停下,当end指向的元素是奇数,end也停下,然后交换begin和end指向的元素。

【剑指offer】调整数组顺序使奇数位于偶数前面_第3张图片
(3)注意临界条件:我们不仅要在最外层循环添加条件begin

大家可以发现,[1,2,3,4,5,6]若相对位置可以发生改变,得到的结果是[1,5,3,4,2,6],原来3本来在5的前面,结果5在交换过后,跑到3前面去了,那么这就是相对位置发生改变。
若要求相对位置不变,结果是[1,3,5,2,4,6](示例中),结果3依然是在5前面的,保持了相对位置不发生改变。

这种方法的代码如下:

#include
#include
#include
using namespace std;
class Solution {
public:
	void reOrderArray(vector<int> &array) {
		if (array.size() == 0)
		{
			return;
		}
		int begin = 0;
		int end = array[array.size() - 2];

		while (begin < end)
		{
			while (begin < end&&array[begin] & 1)//是奇数则++,遇到偶数才停下
			{
				begin++;
			}
			while (begin < end && (!(array[end] & 1)))//是偶数则--,遇到奇数才停下
			{
				end--;
			}
		    //交换
			if(begin < end)
			{
				swap(array[begin], array[end]);
			}
		}
	}
};
int main()
{
	Solution s;
	vector<int> array{ 1,2,3,4,5,6};
	s.reOrderArray(array);

	return 0;
}

相对位置不能改变的思路

(1)这道题的关键点在于:奇数要位于偶数前面,并且!奇数偶数的相对位置相较于原数组的顺序不能发生改变。
(2)采用插入排序的思想:找到奇数依次插入到前面,偶数整体后移。

定义i=0,用于遍历原数组(找到奇数),然后用tmp临时变量保留奇数tmp=array[i],遍历找到的第一个奇数肯定放在下标为0的位置,第二个奇数肯定放在下标为1的位置,所以我们还要定义一个变量k,去记录已经插入的奇数的位置array[k++]=tmp(用于将奇数前插置k下标位置处,这种做法就保证了奇数的相对位置不会改变)。那么偶数如何做到整体后移,不改变相对位置的顺序呢?

由于i用来遍历数组,所以i不能被改变,我们用变量j进行操作,使j=i,这个时候array[j]是我们找到的奇数,现在前面的偶数整体向后移动,即array[j]=array[j-1],也就是说我们当前找到的奇数就被覆盖了(这个操作也保证了偶数的相对位置不发生改变),这也正是我们为什么要用临时变量tmp保存奇数的原因。

解释代码的两个点
1.我们用array[i]&1来判断是否是奇数,原因在于二进制序列的第一位是2的0次方就是1,其它位置都是偶数,所以若第一位是1,那么这个数必是奇数。

2.偶数后移的循环条件j>k:由于k记录奇数已经插入的奇数的位置,所以j必须>k,保证前面已经调整好的奇数不被影响。

建议大家通过编译器调试窗口对各个变量进行解读,通过监视窗口,以及各个变量的变化,更能看出奇数前插,偶数整体后移的过程。我在这里大概写一下。

【剑指offer】调整数组顺序使奇数位于偶数前面_第4张图片

代码如下:

C++版本:

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        if(array.size()==0)
        {
            return;
        }
        int k=0;//用于前插奇数
        int j=0;
        for(int i=0;i<array.size();i++)//
        {
            if(array[i]&1)//奇数的二进制最后一位是1
            {
                j=i;//i不能被改变,所以用j
                int tmp=array[i];//保留奇数,前插置k下标位置处
                while(j>k)//必须保证j>k,否则就会破坏已经排好的奇数序列
                {
                    array[j]=array[j-1];//偶数整体后移
                    j--;
                }
                array[k++]=tmp;
            }
        }
    }
};

java版本:

public class Solution {
    public void reOrderArray(int [] array) {
        int k = 0;
        for (int i = 0; i < array.length; i++) {
            if ((array[i] & 1) ==1) { //从左向右,每次遇到的,都是最前面的奇数,一定将来要被放在k下标处
                int temp = array[i];//现将当前奇数保存起来
                int j = i;
                while (j > k) { //将该奇数之前的内容(偶数序列),整体后移一个位置
                    array[j] = array[j - 1];
                    j--;
                }
                array[k++] =temp;//将奇数保存在它将来该在的位置,因为我们是从左往右放的,没有跨越奇数,所以一定是相对位置不变的
                 }
            }
        }
}

你可能感兴趣的:(C/C++刷题训练营,C++基础,C/C++,数据结构与算法,算法,leetcode,职场和发展)