排序作为前端面试的经典考题,常常让我等厂工无奈。但是跑又跑不掉,逃又逃不了,索性勇敢面对,一口气将所谓的排序算法一口气吃个透。本文如果不强调,一律按照降序排序进行。
冒泡排序
我敢说这东西应该所有的码农都会 - - 。毕竟,大家应该都逃不过所谓的谭浩强的红宝书。
冒泡排序的思路是:
- 比较相邻的元素,如果前者大于后者(前者>后者),则两数进行交换。
- 一轮比较下来。我们可以保证得到最小的数已经跑到了数组的最前。
- 执行 n - 1 轮,我们就能得到排序的结果。
代码如下:
const arr = [29,10,19,23,33];
Array.prototype.bubbleSort = function () {
if (this.length === 1) {
return this;
}
for (let i = 0; i < this.length; i += 1) {
for (let j = i + 1; j < this.length; j += 1) {
if (this[i] > this[j]) {
let temp = this[j];
this[j] = this[i];
this[i] = temp;
}
}
}
return this;
};
选择排序
选择排序是一种常见的排序算法,因为简单,所以,在面试中为了缓解尴尬场面,也可能作为面试题进行考虑。 因为选择排序的性能并不高,且不稳定,所以,在实际生产中,我们使用选择排序的机会并不多。
代码思路:
- 第一次从待排序的数据元素中选择最小的一个元素,放在数组的起始位置。
- 从剩余待排序的数据元素中选择最小的一个元素,放在已排序数据的后边。
- 重复2直到所有元素排序完成。
代码实现:
const arr = [29,10,19,23,33];
Array.prototype.selectionSort = function () {
if (this.length === 1) {
return this;
}
for (let i = 0; i < this.length; i++) {
let minIdx = i;
for (let j = i + 1; j < this.length; j++) {
if (this[minIdx] > this[j]) {
minIdx = j;
}
}
const t = this[i];
this[i] = this[minIdx];
this[minIdx] = t;
}
return this;
};
插入排序
插入排序是指,从数组的第二项开始,与前面的项进行比较,如果前者大于后者(前者>后者), 则交换两个数的位置。 这样,第一轮时,第一项将变成最小的数。 第二轮时, 第二个数将变成第二小的数。
代码思路:
- 从第n项(init n = 1,n为数组下标)开始遍历数组。
- 取出第n项与前面的项进行大小比较, 如果前者大于后者(前者>后者),则交换。
- 重复12步直至数组遍历完成。
代码如下:
const arr = [29,10,19,23,33];
Array.prototype.insertSort = function () {
for (let i = 0; i < this.length; i++) {
const temp = this[i];
let j = i;
while(j > 0) {
if (this[j-1] > this[j]) {
this[j] = this[j-1];
this[j-1] = temp;
}
j--;
}
}
}
归并排序
归并排序是一种效率极高的排序算法,FireFox浏览器就曾用归并排序去实现sort的功能。我们这种小水前端当然不能错过的来一回。
归并排序是指, 将数组递归分成若干个有序数组(数组内只有一个元素,这样的数组一定是有序数组),然后再将有序数组进行合并,合并时,先比较数组的头部元素,如果前者大于后者则出队,将后者推入res数组中,前者后续推入。并将剩余的数组元素依旧按照此步骤合并,直到整个数组合并完成。 就形成了一个排序好的有序数组。
描述上比较抽象: 我们可以分步进行描述。 总的来说,分为两个过程。
一. 分: 将数组递归分成有序数组。 二. 合: 新建一个res数组, 将有序数组进行合并,对有序数组的头部元素进行比较, 如果 前者大于后者,则后者出队,push到res数组后, 前者出队,push到res数组中。 将数组的剩余元素按照这个规则进行合并。直到所有的有序数组合并完成,就形成了一个有序数组。
代码实现:
const arr = [29,10,19,23,33];
Array.prototype.mergeSort = function () {
const rec = (arr) => {
if (arr.length === 1) {
return arr;
}
const midPoint = Math.floor(arr.length / 2);
const left = arr.slice(0, midPoint);
const right = arr.slice(midPoint);
const orderLeft = rec(left);
const orderRight = rec(right);
const res = [];
while (orderLeft.length || orderRight.length) {
if (orderLeft.length && orderRight.length) {
res.push(
orderLeft[0] > orderRight[0] ? orderRight.shift() : orderLeft.shift()
);
} else if (orderLeft.length) {
res.push(orderLeft.shift());
} else if (orderRight.length) {
res.push(orderRight.shift());
}
}
return res;
};
return rec(this);
};
快速排序
快速排序也是前端当中一种效率极高的排序算法, chrome浏览器就用过快速排序去作为sort方法。 它是指,随机选择一个基准点。然后遍历数组。将所有大于基准点的数都放到基准点的后边,将所有小于基准点的数都放到基准点的前边。然后对于前边和后边的数组重新选定基准点,重新进行操作。能够选定基准点的数组长度为一。
代码思路:
- 分区: 随机选定一个基准点,可以直接指定数组的第一个元素作为基准点。 将数组内的数与基准点进行比较,将所有大于基准点的数放到基准点后,将所有小于基准点的数放到基准点前。
- 递归: 递归的对子数组进行分区操作,直到子数组长度为1。
代码如下:
Array.prototype.quickSort = function () {
const rec = (arr) => {
if (arr.length === 1) {
return arr;
}
const basePoint = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] <= basePoint) {
left.push(arr[i]);
}
if (arr[i] > basePoint) {
right.push(arr[i]);
}
}
const orderLeft = (left.length && rec(left)) || [];
const orderRight = (right.length && rec(right)) || [];
return [...orderLeft, basePoint, ...orderRight];
};
return rec(this);
};