• 剑指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

  • 相关阅读:
    DevExpress Winform SearchGridLookUpEdit多选简单设置
    C# yyyyMMdd 类型字符串转换为datetime 类型
    Winform中TextBox文字垂直居中显示
    操作数据库 增删改查
    C# 连接MySql数据库
    XtraEditors三、LookUpEdit、GridLookUpEdit、SearchLookUpEdit
    前端下拉框多选
    php round函数失效
    防御式CSS开发
    微服务解决方案
  • 原文地址:https://www.cnblogs.com/codeapes666/p/12257464.html
Copyright © 2020-2023  润新知