剑指Offer-调整数组顺序使奇数位于偶数前面

题目

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

解法探析

解法 1

若在不考虑时间复杂度的情况下,可以从头扫描这个数组,每碰到一个偶数,取出该数字,并把该数字后面的所有数字往前移一位。移完之后在数组的末尾有一个空位,再把该偶数放入到这个空位。由于每碰到一个偶数就要移动 \(O(n)\)个数字,故总的时间复杂度为 \(O(n^2)\)

解法 2

该题要求把奇数放到数组的前半部分,偶数放在数组的后半部分,因此所有的奇数应该在偶数的前面。那么,在扫描这个数组时,若发现有偶数在奇数的前面,就交换它们的顺序。采用双指针的解法如下所示:

void ReorderOddEven(int* pData, unsigned int length)
{
    if (pData == NULL || length == 0) {
        return;
    }

    int* pBegin = pData;                // pBegin指针指向数组的第一个数字
    int* pEnd = pData + length -1;      // pEnd指针指向数组的最后一个数字

    while (pBegin < pEnd) {
        // 向后移动pBegin,直到它指向偶数
        // (*pBegin & 0x1) != 0 表示pBegin指向的是奇数
        while (pBegin < pEnd && (*pBegin & 0x1) != 0) {
            pBegin++;
        }

        // 向前移动pEnd,直到它指向奇数
        // (*pEnd & 0x1) == 0 表示pEnd指向的是偶数
        while (pBegin < pEnd && (*pEnd & 0x1) == 0) {
            pEnd--;
        }

        // 若pBegin指针指向的是偶数,pEnd指针指向的是奇数。则交换这两个数字
        if (pBegin < pEnd) {
            int temp = *pBegin;
            *pBegin = *pEnd;
            *pEnd = temp;
        }
    }   
}

解法 3

编写代码时我们思考的不应该只是解决一个问题的方法,而是解决一系列同类型问题的通用方法,因为优秀的代码应具有良好的可扩展性。可以将 解法 2 中的整个函数解耦成两部分,一部分是判断数字应该在数组前半部分还是后半部分;另一部分是拆分数组的操作。解耦后的代码如下所示:

void Reorder(int* pData, unsigned int length, bool (*func)(int))
{
    if (pData == NULL || length == 0) {
        return;
    }

    int* pBegin = pData;                // pBegin指针指向数组的第一个数字
    int* pEnd = pData + length -1;      // pEnd指针指向数组的最后一个数字

    while (pBegin < pEnd) {
        // 向后移动pBegin,直到它指向偶数
        while (pBegin < pEnd && !func(*pBegin)) {
            pBegin++;
        }

        // 向前移动pEnd,直到它指向奇数
        while (pBegin < pEnd && func(*pEnd)) {
            pEnd--;
        }

        // 若pBegin指针指向偶数,pEnd指针指向奇数,则交换这两个数字
        if (pBegin < pEnd) {
            int temp = *pBegin;
            *pBegin = *pEnd;
            *pEnd = temp;
        }
    }   
}

// 判断一个数是否为偶数
bool isEven(int n)
{
    // 若是偶数,则返回true,否则返回false
    return (n & 0x1) == 0;
}

void ReorderOddEven(int* pData, unsigned int length)
{
    Reorder(pData, length, isEven);
    // Reorder(pData, length, &isEven);
}

若把问题改成将数组中的负数移到非负数的前面,或者把能被 3 整除的数移到不能被 3 整除的数的前面,都只需定义新的函数来确定分组的标准,而函数 Reorder 不需要进行任何改动。将数组中的负数移到非负数的前面的代码如下所示:

// 判断一个数是否为非负数
bool isNonNegative(int n)
{
    // 若是非负数,则返回true,否则返回false
    return ((n >= 0) ? true : false);
}

void ReorderNonNegAndNeg(int* pData, unsigned int length)
{
    Reorder(pData, length, isNonNegative);
    // Reorder(pData, length, &isNonNegative);
}

个人主页:

www.codeapes.cn

posted @ 2020-02-03 21:33  Codeapes  阅读(177)  评论(0编辑  收藏  举报