1

背景


以前不写技术博客,但写文章,写段子,深刻体会过“写”的妙处。 写博客,不止是分享的过程,更不是单纯的记录,它还有个特殊功效,就是产生更多细致具体的思考。思维在脑海中是完全自由的,但落笔时,它受到约束,你必须让自己有理有据。所以很多思维上的小漏洞,会在写出来时暴露,而新的想法,也可能会在落笔的时候促生。这是妙处。

因此,开个小系列,写技术博客。这是缘由。

简单介绍


这个小系列,主要练习一些基础算法,从排序、查找开始,中后期会有一些数图,最短路径都常见算法。本人是前端程序员,所以目的旨在掌握常见算法,更多的高能,暂时不打算深入,因为时间有限,还要腾出时间去了解学习前端庞大繁杂的生态。


至于身为前端为什么搞算法?

首先基础算法掌握一下,是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]);
    });
});

第一汁
75 声望3 粉丝