背景
以前不写技术博客,但写文章,写段子,深刻体会过“写”的妙处。 写博客,不止是分享的过程,更不是单纯的记录,它还有个特殊功效,就是产生更多细致具体的思考。思维在脑海中是完全自由的,但落笔时,它受到约束,你必须让自己有理有据。所以很多思维上的小漏洞,会在写出来时暴露,而新的想法,也可能会在落笔的时候促生。这是妙处。
因此,开个小系列,写技术博客。这是缘由。
简单介绍
这个小系列,主要练习一些基础算法,从排序、查找开始,中后期会有一些数图,最短路径都常见算法。本人是前端程序员,所以目的旨在掌握常见算法,更多的高能,暂时不打算深入,因为时间有限,还要腾出时间去了解学习前端庞大繁杂的生态。
至于身为前端为什么搞算法?
首先基础算法掌握一下,是CS毕业生对自己的基本要求。
再者算法玩起来,会对自己有个提醒,做程序员,一定要玩出脑力工作,而不是舒适地保持在体力工作。算法用作锻炼逻辑思维。
再功利一点,面试的时候,会派上用场,多少前端高手折在了手写二分上面?遗憾。
本系列的代码,除了会摘取关键部分贴到博文中之外,所有代码均会放在一个仓库内,托管到github。算法部分以用typescript进行编写,正好同时学习ts,用karam + mocha + chai 编写测试,(现在还没建好,地址可能是git@github.com:qixman/An-algorithm-a-day
)。有兴趣的,可以clone下来,也欢迎把意见、建议,以issue形式提出。也欢迎拍砖,雷霆雨露,俱是君恩。
快速排序
快速排序,它的主要逻辑是,把待排序的序列,以某个元素为基准K(具体是哪个?无所谓,但一般会选择第一个元素),将序列分成两个部分,A部分全部大于这个基准,B部分全部小于这个基准。如果你把A,B部分都作为一个整体,那么现在你得到的就是一个有序序列。A < K < B。
不过暂时还没结束,因为A,B内部,还没有得到排序。怎么办?对A,B再进行上述操作。
没错,这里就是要用一个递归。
递归是一个非常有趣的概念。我记得在我刚刚学习编程时,深深地为这种思维模式的能力吸引,因为相比于一步一步推算公式得出结果,这种方式充满了智能和自动化。
这里不讲递归定义, 它大概的样子,其实就是一个调用它自身的方式,来处理问题。由此可以看出,它非常适合那种在处理数据时,有循环逻辑的情况。比如上述的快排,它的循环操作就是,不断将一个序列,分化成 A < K < B 的模式(如果是降序,则 A > K > B)。而这个循环操作就是递归的主体部分。
不断调用自身,会陷入死循环。因此只有主体是不够的,递归的另一个部分,就是终止条件,这个很关键。还是以快排为例。主体操作是将一个序列处理成 A < K < B, 但如果接受到的是一个只有1个元素的序列,或空序列,那么继续下去就毫无意义。此时就是递归在当前分支
终止的最佳时机。请注意,这里点亮了当前分支
,因为递归很可能沿着多条路径递归,比如本次快排,子节点会像二叉树一样向外发散(这也是递归会产生性能问题的原因,时间复杂度会上升到指数级别)。因此当终止条件结束递归的时候,它可能只是终止了当前分支的递归,而整体部分,仍在继续运转。
这里对递归进行了科普,大家也能发现,在科普递归的时候,实际上快速排序的思路,已经很全面的进行了分析。那么代码就比较容易了。
最终代码如下:
export default function quickSort(arr: number[], isAscending: boolean): number[] {
if (1 === arr.length) return arr;
if (0 === arr.length) return [];
let small: number[] = [];
let big: number[] = [];
let equal: number[] = [];
let key = arr[0];
for (let i: number = 0; i < arr.length; i++) {
if (arr[i] < key) {
small.push(arr[i]);
} else if (arr[i] > key) {
big.push(arr[i]);
} else {
equal.push(arr[i]);
}
}
if (isAscending) {
return [].concat(quickSort(small, isAscending), equal, quickSort(big, isAscending));
}
return [].concat(quickSort(big, isAscending), equal, quickSort(small, isAscending));
}
测试用例如下
var quickSort = require('../dist/quicksort.js').default;
var assert = require('chai').assert;
describe('quickSort', () => {
it("should return sorted array.", () => {
assert.deepEqual(quickSort([3, 4, 2, 1, 5], true), [1, 2, 3, 4, 5]);
});
it("has only one number", () => {
assert.deepEqual(quickSort([1], true), [1]);
});
it("has no number", () => {
assert.deepEqual(quickSort([], true), []);
});
it("should repeating number", () => {
assert.deepEqual(quickSort([3,1,2,2,2], true), [1,2,2,2,3]);
});
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。