在前端工程师中,常常有一种声音,我们为什么要学数据结构与算法,没有数据结构与算法,我们一样很好的完成工作。
实际上,算法是一个宽泛的概念,我们写的任何程序都可以称为算法,甚至往冰箱里放大象,也要通过开门,放入,关门这样的规划,我们也可以视作为一种算法。可以说:简单的算法是人类的本能。而算法的知识的学习则是吸取前人的经验。对于复杂的问题进行归类,抽象,帮助我们脱离刀耕火种的时代,系统掌握一个算法的过程。

随着自身知识的增长,不论是做前端,服务端还是客户端,任何一个程序员都会开始面对更加复杂的问题,算法和数据结构就变得更不可或缺了。

前端工程师应该是最需要重视算法和数据结构基础的人。数据结构和算法的又没有照顾到入门的需要,所以前端工程师如果自身不重视算法和数据结构这样的基础知识,很可能陷入一直重复劳动很少有成长的这样职业发展困境。未来的网页UI,绝不是靠几个选择器操作加链接就能应付的。越来越复杂的产品和基础库,需要坚实的数据结构与算法才能驾驭。

在过去的几年中,得益于Node.js和SpiderMonkey等平台,javascript越来越多的用于服务器端编程,介于javascript已经走出了浏览器,程序员发现他们需要更多的传统语言(比如C++和JAVA),提供的工具。这些工具包括传统的数据结构(如链表,栈,队列,图等)也包括传统的排序和查找算法。本系列博文在使用javascript进行服务器端编程时,如何使用这些数据结构和算法。

javascript程序员会发现本系列内容很有用,因为本书讨论了在javascript语言的限制下,如何实现数据结构和算法。这些限制包括:数组即对象,无处不在的全局变量,基于原型的对象模型等。javascript作为一种编程语言,名声不大好,本系列博文将展示javascript好的一面,去实现如何高效的数据结构和算法。

在那些学校没有学习过计算机的程序员来说,唯一熟悉的数据结构就是数组。在处理一些问题时,数组无疑是很好的选择,但对于很多复杂的问题,数组就显得太过简陋。大多数程序员都原因承认这样一个事实:对于很多编程问题,当他们提出一个合适的数据结构,设计和实现解决这些问题的算法就变得手到擒来。

二叉查找数(BST)就是这样一个例子。设计二叉差找树的目的就是为了方便查找一组数据中的最小值和最大值,由于这个数据结构自然引申出一个查找算法,该算法比目前最好的查询算法效率还高。不熟悉二叉查找树的程序员可能会使用一个更简单的数据结构,但效率上就打了个折扣。

学习算法非常重要,因为解决同样的问题,往往可以使用多种算法。对于高效程序员来说,知道那种算法效率高非常重要。比如,现在至少有六七种排序算法,如果知道快速排序比选择排序效率更高,那么就会让排序过程变得高效。又比如,实现一个线性查找的算法很简单,但是如果知道有时二分查找可能比线性查找快两倍以上,那么你势必会写出一个更好的程序。

深入学习数据结构和算法,不仅可以知道那种数据结构和算法更高效,还会知道如何找出最适合解决手头问题的数据结构和算法。写程序,尤其适用javascript写程序时 ,经常要权衡。

javascript的编程环境和模型

本章描述了javascript编程环境和基本的编程模块,后续章节将使用这些知识定义数据结构和实现各种算法。

1.javascript历来都是在浏览器里运行的程序语言,然而在过去的几年中,这些情况发生了变化,javascript可以作为桌面程序执行。或者在服务器上执行。介绍一种编程环境:javascript shell,这是MOzilla提供的综合javascript编程环境SpiderMonkey中的一部分。

2.声明和初始化变量

javascript变量默认是全局变量,严格的说,甚至不需要在使用前进行声明。如果对一个事先未声明的javascript变量进行初始化,该变量就变成了一个全局变量。所有,我们在编程中,遵循c++或java等编译型语言的习惯,在使用变量前进行声明,这样做的好处就是:声明的变量都是局部变量。本章稍后将部分详细讨论变量的作用域。

在javascript中声明变量,需要使用关键字var,后面跟变量名,或后面跟随一个赋值表达式。

    var number;
var name;
var rate = 1.2;
var greeting = "hello world!";
var flag = false;

3.javascript中的算术运算符和数学库函数

javascript使用标准的算术运算符

+ 加 , - 减 , * 乘 , / 除 , % 去余

javascript同时拥有一个数学库,用来完成一些高级运算,比如平方根,绝对值和三角函数。算术运算符遵循标准的运算顺序,可以用括号来改变运算顺序。

    var x = 3;
var y = 1.1;
console.log(x + y)
console.log(x * y)
console.log((x + y) * (x - y));
var z = 9;
console.log(Math.sqrt(z));
console.log(Math.abs(y/x))

如果计算精度不必像上面那样精确,可以将数字转换为固定精度

    var x = 3;
var y = 1.1;
var z = x * y;
console.log(z.toFixed(2));

2.判断结构

根据布尔表达式的值,判断结构让程序可以执行到那句可以执行的程序的语句。常用的有if和switch语句

if有如下三种形式

  • 简单的if语句
  • if-else语句
  • if-else-if语句

下面演示简单的if语句

    var mid = 25;
var height = 50;
var low = 1;
var current = 13;
var found = -1;
if (current < mid) {
mid = (current - low) / 2
}

下面演示if - else语句

    var mid = 25;
var height = 50;
var low = 1;
var current = 13;
var found = -1;
if (current < mid) {
mid = (current - low) / 2;
} else {
mid = (current + height) / 2;
}

下面演示if - else -if

    var mid = 25;
var height = 50;
var low = 1;
var current = 13;
var found = -1;
if (current < mid) {
mid = (current - low) / 2;
} else if (current > mid) {
mid = (current + height) /2
} else {
found = current
}

另外一个判断结构是switch语句,在有多个简单选择时,使用该语句的代码更加清晰。

 3.循环结构

常用的两种循环结构:while循环和for循环。

如果希望条件为真时,只执行一组语句,就选择while循环,下面展示了while循环的工作原理

while循环

    var number = 1;
var sum = 0;
while (number < 11) {
sum += number;
++number
}
console.log(sum) //

如果希望按执行次数执行一组语句,就选择for循环。下面是for循环求1到10的整数累加和

    var number = 1;
var sum = 0;
for (var number =1 ; number < 11; number++){
sum += number;
}
console.log(sum)

访问数组时,经常使用到for循环,如下:

    var number = [3,7,12,22,100];
var sum = 0;
for (var i = 0; i < number.length; ++i) {
sum += number[i];
}
console.log(sum)//

4.函数
javascript提供了两种自定义函数的方式,一种有返回值,一种没有返回值(这种函数有时叫做子程或者void函数)。

下面展示了如何自定义一个有返回值的函数如何在javascript调用该函数。

    function factorial (number) {
var product = 11;
for (var i = number; i >= 1; --i) {
product *= 1
}
return product;
}
console.log(factorial(4))

下面的例子展示如何定义一个没有返回值的函数,使用该函数并不是为了得到它的返回值,而是为了执行函数中定义的操作。

javascript中的子程或者void函数

    function curve(arr, amount) {
for (var i = 0; i < arr.length; ++i) {
arr[i] += amount;
}
} var grades = [77,73,74,81.90];
curve(grades,5);
console.log(grades)

javascript中,函数的参数传递方式都是按值传递,没有按引用传递的参数。但是javascript有保存引用的对象,比如数组,如上例,它们是按引用传递的。

5.变量作用域

变量的作用域是指一个变量在程序中那些对象可以访问。javascript中的变量作用域被定义为函数作用域。
这是指变量的值在定义该变量的函数内是可见的。并且定义在该函数内的嵌套函数也可以访问该变量。

在主程序中,如果在函数的外部定义一个变量,那么该变量拥有全局作用域,这是指可以在包括函数体内的程序的任何部分访问该变量。下面用一段简短的程序展示作用域的工作原理。

    function showScope(){
return scope;
}
var scope = "global";
console.log(scope); //global
console.log(showScope()); //global

showScope()函数内定义的变量scope拥有局部作用域,而在主程序中定义变量scope是一个全局变量。尽管两个变量名字相同,但它们的作用域不同,在定义他们的地方访问时得到的值也不一样。

这些行为都是正常且符合预期的,但是如果在定义变量时省略了关键字var,那么一切都变了。javascript允许在定义变量时不使用关键字var,但是这样做的后果是定义的变量自动拥有了全局作用域,即使你在一个函数内定义该变量,它也是全局变量。

下面的例子是滥用全局变量的恶果。

    function showScope(){
scope = "local";
return scope;
} scope = "global";
console.log(scope); //global
console.log(showScope()); //local
console.log(scope) //local

上面的例子中,由于showScope()函数内定义变量scope时省略了关键字var。所以在将字符串"local"赋值给该变量时,实际上是改变了主程序中scope变量的值。因此,在定义变量时,应该总是var开始,避免发生类似错误。

前面我们提到,javascript拥有的是函数作用域,其含义是javascript没有块级作用域,这一点有别于其它很多现代的编程语言。使用块级作用域,可以在一段代码中定义变量,该变量只在块内可见,离开这段代码块就不见了。在C++或者java的for循环中,我们经常看到这样的例子

    for (int i = 1; i <= 10; ++i) {
count << "hello world!" << end;
}

虽然javascript没有块级作用域,但在我们写for循环时,我们假设它有:

    for (var i = 1; i <=10; ++i) {
console.log("hello,world!")
}

这样做的原因是,不希望自己养成坏编程习惯的帮手!

6.递归

javascript中允许函数递归调用,前面定义过的factorial()函数也可以使用递归方式定义

    function factorial(number) {
if (number == 1) {
return number
} else {
return number * factorial(number - 1)
}
} console.log(factorial(5)) //

当一个函数递归调用时,当递归没有完成时,函数的计算结果暂时会被挂起。为了说明这个过程,这里用一副图展示以5作为参数,调用factorial()函数时函数执行的过程。

5 * factorial(4)
5 * 4 * factorial(3)
5 * 4 * 3 * factorial(2)
5 * 4 * 3 * 2 * factorial(1)
5 * 4 * 3 * 2 * 1
5 * 4 * 3 * 2
5 * 4 * 6
5 * 24
120

对于大多数情况,javascript有能力处理层次较深的递归调用;但保不齐有些算法需要的递归深度超出了javascript的处理能力,这时候,我们就需要寻求改算法的一种迭代方式的解决方案了。任何可以被递归定义的函数,都可以被改写为迭代的程序,这点要牢记于心。

对象和面向对象编程

数据结构都被要实现为对象。javascript提供了很多种方式来创建和使用对象。下面做展示,创建对象,并用于创建和使用对象中的方法和属性。

对象通过如下方式创建:定义包含属性和方法声明的构造函数,并在构造函数后紧跟方法的定义。下面是一个检查银行账户对象的构造函数:

    function Checking(amount){
this.balance = amount; //属性
this.deposit = deposit; //方法
this.withdraw = withdraw;//方法
this.toString = toString;//方法
}

this关键字用来将方法和属性绑定到一个对象的实例上,下面看看我们对上面声明过的方法是如何定义的

    function deposit(amount) {
this.balance += amount;
} function withdraw(amount) {
if (amount <= this.balance) {
this.balance -= amount;
}
if (amount > this.balance) {
console.log("Insufficient funds");
}
} function toString(){
return "Balance:" + this.balance;
}

这里,我们又一次的使用this关键字和balance属性,以便让javascript解释器指定我们引用的是那个对象的balance属性

下面给出checking对象的完整定义和测试

    function Checking(amount){
this.balance = amount; //属性
this.deposit = deposit; //方法
this.withdraw = withdraw;//方法
this.toString = toString;//方法
} function deposit(amount) {
this.balance += amount;
} function withdraw(amount) {
if (amount <= this.balance) {
this.balance -= amount;
}
if (amount > this.balance) {
console.log("余额不足");
}
} function toString(){
return "余额:" + this.balance;
} var account = new Checking(500);
account.deposit(1000);
console.log(account.toString());//余额:1500 account.withdraw(750);
console.log(account.toString());//余额:750 account.withdraw(800); //余额不足
console.log(account.toString());//余额:750

ps:编写出让人容易阅读的代码和编写出让计算机能正确执行的代码同等重要,作为负责任的程序员,必须将这一点牢记于心。

第一章:javascript: 数据结构与算法的更多相关文章

  1. Python3-Cookbook总结 - 第一章:数据结构和算法

    第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...

  2. <python3-cookbook>第一章:数据结构和算法

    第一章:数据结构和算法 介绍:python3-cookbook这本书是高级用法,不是小白使用书目的:写作目的是记录下自己学习这本书的过程以及收获书籍地址:https://python3-cookboo ...

  3. 【读书笔记】《Python_Cookbook3》第一章:数据结构和算法

      Python提供了多样化有用的内建数据结构,例如列表.集合.字典.大多数时候,这些结构的使用比较简单,然后,一些关于搜索.排序.过滤的常见问题经常出现.本章节的目标是讨论常见的数据结构,以及涉及到 ...

  4. 为什么我要放弃javaScript数据结构与算法(第一章)—— JavaScript简介

    数据结构与算法一直是我算比较薄弱的地方,希望通过阅读<javaScript数据结构与算法>可以有所改变,我相信接下来的记录不单单对于我自己有帮助,也可以帮助到一些这方面的小白,接下来让我们 ...

  5. 为什么我要放弃javaScript数据结构与算法(第十一章)—— 算法模式

    本章将会学习递归.动态规划和贪心算法. 第十一章 算法模式 递归 递归是一种解决问题的方法,它解决问题的各个小部分,直到解决最初的大问题.递归通常涉及函数调用自身. 递归函数是像下面能够直接调用自身的 ...

  6. 为什么我要放弃javaScript数据结构与算法(第九章)—— 图

    本章中,将学习另外一种非线性数据结构--图.这是学习的最后一种数据结构,后面将学习排序和搜索算法. 第九章 图 图的相关术语 图是网络结构的抽象模型.图是一组由边连接的节点(或顶点).学习图是重要的, ...

  7. 为什么我要放弃javaScript数据结构与算法(第七章)—— 字典和散列表

    本章学习使用字典和散列表来存储唯一值(不重复的值)的数据结构. 集合.字典和散列表可以存储不重复的值.在集合中,我们感兴趣的是每个值本身,并把它作为主要元素.而字典和散列表中都是用 [键,值]的形式来 ...

  8. 为什么我要放弃javaScript数据结构与算法(第六章)—— 集合

    前面已经学习了数组(列表).栈.队列和链表等顺序数据结构.这一章,我们要学习集合,这是一种不允许值重复的顺序数据结构. 本章可以学习到,如何添加和移除值,如何搜索值是否存在,也可以学习如何进行并集.交 ...

  9. 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表

    这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...

  10. 为什么我要放弃javaScript数据结构与算法(第四章)—— 队列

    有两种结构类似于数组,但在添加和删除元素时更加可控,它们就是栈和队列. 第四章 队列 队列数据结构 队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序 ...

随机推荐

  1. Best Time to Buy and Sell Stock II

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  2. WordCount Analysis

    1.Create a new java project, then copy examples folder from /home/hadoop/hadoop-1.0.4/src; Create a ...

  3. UESTC 915 方老师的分身II --最短路变形

    即求从起点到终点至少走K条路的最短路径. 用两个变量来维护一个点的dis,u和e,u为当前点的编号,e为已经走过多少条边,w[u][e]表示到当前点,走过e条边的最短路径长度,因为是至少K条边,所以大 ...

  4. Java的文件读写操作 <转>

    目录: file内存----输入流----程序----输出流----file内存 java中多种方式读文件 判断文件是否存在不存在创建文件 判断文件夹是否存在不存在创建文件夹 java 写文件的三种方 ...

  5. uGUI VS NGUI

    前言 这篇日志的比较是根据自己掌握知识所写的,请各路大神多多指教. 引擎版本: Unity 4.6 beta 两者区别 1.uGUI的Canvas 有世界坐标和屏幕坐标 2.uGUI的Button属性 ...

  6. BIO、NIO与NIO.2的区别与联系

    BIO.NIO.NIO.2之间的区别主要是通过同步/异步.阻塞/非阻塞来进行区分的 同步: 程序与操作系统进行交互的时候采取的是问答的形式 异步: 程序与操作系统取得连接后,操作系统会主动通知程序消息 ...

  7. [2]Telerik Extensions for ASP.NET MVC 中文教程(2)

    上一篇文章对Telerik MVC Extensions作了一个大概的介绍,这篇文章将介绍如何将Telerik MVC Extensions添加到项目中.有以下两种方式可以将Telerik MVC E ...

  8. C# 改变无边框窗体的尺寸大小

    以下代码为修改窗体尺寸的代码: const int HTLEFT = 10; ; ; ; ; ; const int HTBOTTOMLEFT = 0x10; ; protected override ...

  9. 【原创】有关Silverlight中“DataGrid中级联动态绑定父/子ComboBox ”的示例。

    尝试了很多种方案,由于Datagrid动态生成的每行父子comboBox的Name的不确定性,给父ComboBox绑定事件中获取 子ComboBox很难根据Name获取到. 花了不少时间和公司同事商讨 ...

  10. U3D Debug.log的问题

    今天在测试有yield有关的问题时,发现Debug.log()是异步输出机制.不一定会在调用后立即执行. 在C++有类似问题:std::cout 也不一定会立即输出,加上"\n"或 ...