博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
21 调整数组的顺序使奇数位于偶数的前面 (第3章 高质量的代码-代码的完整性)...
阅读量:4358 次
发布时间:2019-06-07

本文共 7120 字,大约阅读时间需要 23 分钟。

题目描述:(题目一)

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

 

测试用例:  

功能测试:(输入的数组中的奇数、偶数交替出现;所有偶数都在奇数的前面;输入的数组中所有奇数都出现在偶数的前面) 

特殊输入测试:(输入为nullptr;输入的数组只包含一个数字)

 

解题思路:

1) 初级程序员:维护两个指针,一个指向数组的第一个数字,一个指向数组的最后一个数字;两者向中间移动,第一个指针寻找偶数,第二个指针寻找奇数,将两者内容交换。(快速排序算法)

class Solution {public:    void reOrderArray(vector
&array) { if(array.empty()) return; int front = 0; int back = array.size()-1; while(front

 注意:!=优先级高于& 因此判断是要对(array[front]&0x1)加括号,否则在寻找奇数时,会先计算0x1  !=1 永远不成立,因此永远不会进入到循环内

2)高级程序员:写成一些列同类型的通用写法

-- 题目改成把数组中的数按照大小分为两部分,所有负数都在非负数的前面。

-- 能被3整除的数都在不能被3整除的数的前面。

上述问题都只要修改第二个while跟第三个while的的判断条件即可

通用的解决方法:把判断的标准变成一个函数指针,也就是用一个单独的函数来判断数字是否符合标准。

class Solution {public:    void reOrderArray(vector
&array, bool (*func)(int)) { if(array.empty()) return; int front = 0; int back = array.size()-1; while(front

 


 

题目描述:(题目二)

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。并保证奇数和奇数,偶数和偶数之间的相对位置不变。

 

测试用例: 

功能测试:(输入的数组中的奇数、偶数交替出现;所有偶数都在奇数的前面;输入的数组中所有奇数都出现在偶数的前面) 

特殊输入测试:(输入为nullptr;输入的数组只包含一个数字)

 

解题思路:

1)使用vector内置的函数erase与push_back,从左到右遍历,当遇到偶数时,将其在原位置删除并在vector的后面追加。

class Solution {public:    void reOrderArray(vector
&array) { //if(array.empty()) //return; vector
::iterator even = array.begin(); int size = array.size(); while (size) { if ((*even &01) == 0) { int tmp = *even; even = array.erase(even); //使用erase函数 array.push_back(tmp); }else{ even++; } size--; } }};  

 注意:该方法的效率主要取决于erase的效率。

erase()函数删除当期位置,然后将后面的数据前移   效率较低。

2)类似冒泡算法,前偶后奇数就交换

class Solution {public:    void reOrderArray(vector
&array) { for (int i = 0; i < array.size();i++) //每一次i循环,可以确定i位置的奇数,离i最近的奇数会传递到i位置 { for (int j = array.size() - 1; j>i;j--)//j每次都从尾部遍历到i后面 { if ((array[j] & 0x1) == 1 && (array[j - 1] & 0x1) == 0) //前偶后奇交换 { //交换两个值swap(array[j], array[j-1]); array[j] = array[j] + array[j-1]; array[j-1] = array[j] - array[j-1]; array[j] = array[j] - array[j-1]; } } } }};

3)使用stable_partition函数:

//实现1 判断函数定义在类外部 bool isOk(int n){      return ((n & 1) == 1); //奇数返回真 }class Solution {public:    void reOrderArray(vector
&array) { stable_partition(array.begin(),array.end(),isOk); }};
//实现2 判断函数定义在类内部 class Solution {public:    void reOrderArray(vector
&array) { stable_partition(array.begin(),array.end(),isOk); } static bool isOk(int n){ //定义在类内,要加static,否则类内会报错。与*this指针有关 return ((n & 1) == 1); //奇数返回真 }};
//实现3 lambda表达式 class Solution {public:    void reOrderArray(vector
&array) { stable_partition(array.begin(), array.end(), [](int i){return (i & 1) == 1;}); //奇数返回真 }};  

注:

[1] stable_partiton(first,last,compare);//first为容器的首迭代器,last为容器的末迭代器,compare为比较函数(可略写)。1 2 3 4 5 序列,用partition函数 那么输出不一定是:1 3 5 2 4 也有可能是:3 5 1 4 2。如果使用stable_partition函数(稳定整理算法),那么输出一定就是:1 3 5 2 4 

[2]  stable_partition函数源码其实是开辟了新的空间,然后把符合条件的放在新空间的前面,其他的放在后面。  代码中看起来时在原array上进行的操作

[3] 可以向一个算法传递任何类别的可调用对象。可调用对象有,函数指针,其实还有重载了函数调用运算符的类,和lambda表达式。 

 -- lambda表达式 : 用于定义并创建匿名的函数对象,以简化编程工作。

 -- 语法形式:

[capture list] (params list) mutable exception-> return type { function body }各项具体含义如下1. capture list:捕获外部变量列表2. params list:形参列表3. mutable指示符:用来说用是否可以修改捕获的变量4. exception:异常设定5. return type:返回类型6. function body:函数体可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种//省略 mutable指示符、exception:异常设定1. [capture list] (params list) -> return type {function body}格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值//省略返回类型及mutable指示符、exception:异常设定2. [capture list] (params list) {function body}格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型: (1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定; (2):如果function body中没有return语句,则返回值为void类型。3. [capture list] {function body}格式3中省略了参数列表,类似普通函数中的无参函数。示例:[] (int x, int y) { return x + y; } // 隐式返回类型[] (int& x) { ++x;  } // 没有 return 语句 -> Lambda 函数的返回类型是 'void'[] () { ++global_x;  } // 没有参数,仅访问某个全局变量[] { ++global_x; } // 与上一个相同,省略了 (操作符重载函数参数)//显示指定返回类型[] (int x, int y) -> int { int z = x + y; return z; }

4)新建数组,遍历两遍,先存奇数,再存偶数

class Solution {public:    void reOrderArray(vector
&array) { vector
res; //存奇数 for(int i = 0; i < array.size(); i++) { if((array[i] & 1) == 1) res.push_back(array[i]); } //存偶数 for(int i = 0; i < array.size(); i++) { if(array[i] % 2 == 0) res.push_back(array[i]); } //array.swap(res); //swap的效率? array = res; //或者用 swap }  

注:以后判断奇偶性,不要用  array[i] % 2 == 1 因为不仅效率低,而且当array[i] 为负数,比如-1%2=-1 并不等于1。

用空间换时间,存偶数与存奇数分别是O(n),最后array=res;的操作,时间复杂度是O(1)么?  

5)插入排序算法

相对位置不变--->保持稳定性;奇数位于前面,偶数位于后面 --->存在判断,挪动元素位置;

这些都和内部排序算法相似,考虑到具有稳定性的排序算法不多,例如插入排序,归并排序等;这里采用插入排序的思想实现。
//java public class Solution {    public void reOrderArray(int [] array) {        //相对位置不变,稳定性        //插入排序的思想        int m = array.length;        int k = 0;//记录已经摆好位置的奇数的个数        for (int i = 0; i < m; i++) {            //将找到的奇数移动到已确定的k的后面,k++,可以确定k+1个            if (array[i] % 2 == 1) {                 int j = i;                while (j > k) {//j >= k+1                    int tmp = array[j];                    array[j] = array[j-1];                    array[j-1] = tmp;                    j--;                }                k++;            }        }    }}  

6)归并排序 (快速排序是不稳定的,这里不能使用)归并时间复杂度是nlogn,需要额外的O(N)空间

//java  该算法过于麻烦   待考虑public class Solution {    public void reOrderArray(int [] array) {           //用用归并排序的思想,因为归并排序是稳定的        int length = array.length;        if(length == 0){            return;        }        int[] des = new int[length];        MergeMethod(array, des, 0,length - 1);    }    public void MergeMethod(int[] array, int [] des, int start, int end){        if(start < end){            int mid = (start + end) / 2;            MergeMethod(array, des, start, mid);  //前半部分            MergeMethod(array, des, mid + 1, end);  //后半部分            Merge(array, des, start, mid, end);        }    }        public void Merge(int[] array, int[] des, int start, int mid, int end){        int i = start;        int j = mid + 1;        int k = start;        while(i <= mid && array[i] % 2 == 1){            des[k++] = array[i++];        }        while(j <= end && array[j] % 2 == 1){            des[k++] = array[j++];        }        while(i <= mid){            des[k++] = array[i++];        }        while(j <= end){            des[k++] = array[j++];        }                for(int m = start; m <= end; m++){            array[m] = des[m];        }    }}

7)新开数组空间换时间的解法:

    a.遍历数组,如果是奇数从头部放入到原数组中,并记录指针
    b.如果是偶数,放入到新数组中,并记录指针
    c.将新数组的元素安顺序,从最后一个奇数后边插入到原数组中

  

转载于:https://www.cnblogs.com/GuoXinxin/p/10441992.html

你可能感兴趣的文章
emmet使用技巧
查看>>
RPC-Thrift(二)
查看>>
MSSQL for Linux 安装指南
查看>>
【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
查看>>
前端必读:浏览器内部工作原理
查看>>
Uri、URL和URN三者的区别
查看>>
数据字典的转换
查看>>
二维数组按照指定的字段排序的函数
查看>>
linux安装Mac的默认Monaco字体
查看>>
java语言的特点
查看>>
关于动态添加iview admin路由以及刷新侧边栏
查看>>
ApplicationInsights的探测器尝鲜
查看>>
java 解析Json格式数据
查看>>
unix中的线程池技术详解
查看>>
CSS简介
查看>>
常用三大软件评价1
查看>>
MVC各层介绍使用---初步理解
查看>>
单例对象的创建与销毁
查看>>
知识点关键词(记录一下)
查看>>
国际结算业务
查看>>