社区编辑申请
注册/登录
调整数组元素顺序,你了解几分?
大数据 数据分析
如果数组中的元素不按照奇前偶后排列,我们需要将其按照大小进行划分,所有负数都排在非负数的前面,应该怎么做?

前言

有一个整数数组,我们想按照特定规则对数组中的元素进行排序,比如:数组中的所有奇数位于数组的前半部分。

实现思路

我们通过一个实例来分析下:假设有这样一个数组:[2, 4, 5, 6, 7, 8, 9, 11],将奇数移动到最前面后,就是:[11, 9, 5, 7, 6, 8, 4, 2]。

通过观察后,我们发现在扫描这个数组的时候,如果发现有偶数出现在奇数的前面, 就交换他们的顺序,交换之后就符合要求了。

因此,我们可以维护两个指针:

  • 第一个指针初始化时指向数组的第一个数字,它只向后移动;
  • 第二个指针初始化时指向数组的最后一个数字,它只向前移动;

在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数,并且第二个指针指向的数字是奇数,则交换这两个数字。

接下来,我们来通过图来描述下上述例子交换指针的过程,如下所示:

  • 第一个指针永远指向偶数,如果不为偶数就向后移动;
  • 第二个指针永远指向奇数,如果不为奇数就向前移动;
  • 当两个指针各自指向的数都符合条件时,就交换两个元素的位置;
  • 交换完成后,重复上述步骤,直至两个指针相遇或者第一个指针位于第二个指针之后则代表问题已得到解决。

实现代码

有了思路之后,我们来看下实现代码,如下所示:

export class AdjustArrayOrder {
// 指向数组元素的两个指针:一个指向数组头部、一个指向数组尾部
private begin = 0;
private end = 0;

// 调整数组中奇数与偶数元素的位置:奇数位于偶数前面
reorderOddEven(arr: Array<number>): void {
this.end = arr.length - 1;
while (this.begin < this.end) {
// 向后移动begin(转成二进制跟1做与运算,运算结果为0就表示为偶数),直至其指向偶数
while (this.begin < this.end && (arr[this.begin] & 0x1) !== 0) {
this.begin++;
}

// 向前移动end(转成二进制跟1做与运算,运算结果为1就表示为奇数),直至其指向奇数
while (this.begin < this.end && (arr[this.end] & 0x1) === 0) {
this.end--;
}

// begin指向了偶数,end指向了奇数
if (this.begin < this.end) {
// 交换两个元素的顺序
[arr[this.begin], arr[this.end]] = [arr[this.end], arr[this.begin]];
}
}
// 重置指针位置
this.begin = 0;
this.end = 0;
}
}

代码的可扩展

性如果数组中的元素不按照奇前偶后排列,我们需要将其按照大小进行划分,所有负数都排在非负数的前面,应该怎么做?

聪明的开发者可能已经想到了方案:双指针的思路还是不变,我们只需修改内层while循环的的判断条件即可。

这样回答没有问题,确实解决了这个问题,那么如果再改改题目,我们需要把数组中的元素分为两部分,能被3整除的数都在不能被3整除的数前面,应该怎么做?

经过思考后,我们发现这个问题无论再怎么改变都有一个共同的部分:双指针的逻辑永远不会变。变化的只是判断条件,那么我们就可以把变化的部分提取成函数,当作参数让调用者传进来,这样就完美的解决了这个问题,也正是我们所提及的代码的可扩展性。

最后,我们来看下实现代码,如下所示:

  // 元素排序
reorder(arr: Array<number>, checkFun: (checkVal: number) => boolean): void {
this.end = arr.length - 1;
while (this.begin < this.end) {
// 向后移动begin
while (this.begin < this.end && !checkFun(arr[this.begin])) {
this.begin++;
}

// 向前移动end
while (this.begin < this.end && checkFun(arr[this.end])) {
this.end--;
}

// begin与end都指向了正确的位置
if (this.begin < this.end) {
// 交换两个元素的顺序
[arr[this.begin], arr[this.end]] = [arr[this.end], arr[this.begin]];
}
}

测试用例

我们先来测试下奇数在偶数之前的函数处理代码能否正常执行,如下所示:

const adjustArrayOrder = new AdjustArrayOrder();
// 奇数在前
const arr = [2, 4, 5, 6, 7, 8, 9, 11];
adjustArrayOrder.reorderOddEven(arr);
console.log(arr);

执行结果如下所示:

最后,我们来测试下reorder函数能否正常执行:

  • 负数在数组的最前面
// 负数在前
const checkMinusNumber = function (val: number) {
return val > 0;
};
const arr = [2, 4, 5, 6, 7, -8, -10 - 12, -2];
adjustArrayOrder.reorder(arr, checkMinusNumber);
console.log(arr);

  • 能被3整除的数在数组的最前面
const checkDivisible = function (val: number) {
return val % 3 !== 0;
};
const arr = [2, 4, 5, 6, 3, 6, 9, 12];
adjustArrayOrder.reorder(arr, checkDivisible);
console.log(arr);

示例代码

文中所举代码的完整版请移步:

  • AdjustArrayOrder.ts[1]
  • adjustArrayOrder-test.ts[2]

参考资料

[1]AdjustArrayOrder.ts: https://github.com/likaia/algorithm-practice/blob/e7f6a38021426397af60a73d4c6b8bf88548ba91/src/AdjustArrayOrder.ts#L2

[2]adjustArrayOrder-test.ts: https://github.com/likaia/algorithm-practice/blob/e7f6a38021426397af60a73d4c6b8bf88548ba91/src/test-case/adjustArrayOrder-test.ts#L3

[3]个人网站: https://www.kaisir.cn/

责任编辑:武晓燕 来源: 神奇的程序员
相关推荐

2022-06-12 06:48:34

2022-06-20 22:37:25

Linux操作系统命令

2022-06-05 21:09:47

Python办公自动化

2022-05-27 10:00:06

C++游戏引擎

2022-06-27 19:01:04

Python应用程序数据

2022-06-15 11:51:14

Vue3开发避坑

2022-07-01 17:19:33

网络安全零信任

2022-06-07 10:09:42

新技术人工智能5G

2022-05-24 15:22:09

网络安全企业风险

2022-07-01 14:25:27

机器学习人工智能工业4.0

2022-07-01 05:47:19

PyCharm插件开发

2022-06-24 10:16:59

Python精选库

2022-06-30 14:23:56

机器学习工具算法

2022-06-27 08:07:13

Go语言互斥锁

2022-06-29 09:43:14

SQL优化数据库

2022-06-29 10:16:25

数据库SQL

2022-06-28 14:01:42

MITOpenAI预训练模型

2022-06-29 16:33:52

安全信息泄露信息安全

2022-06-28 10:22:00

2022-06-27 11:09:06

边缘计算

同话题下的热门内容

七个好用常见的大数据分析模型如何用好数据科学?七张图,学会做有价值的经营分析Apache Doris刚刚 "毕业":这个SQL数据仓库有什么不一样?一文看懂:数据指标体系的四大类型

编辑推荐

什么是数据分析的漏斗模型?数据分析师还吃香吗?用数据告诉你对比解读五种主流大数据架构的数据分析能力《狄仁杰之四大天王》影评分析(爬虫+词云+热力图)22个免费的数据可视化和分析工具推荐
我收藏的内容
点赞
收藏

51CTO技术栈公众号