第2章 数组
2.1 创建数组
方式一:通过[ ]操作符声明一个数组变量(推荐使用):
var numbers = [];//数组长度为0
var numbers = [1,2,3,4,5];
console.log(numbers.length);//数组长度为5
方式二:可以调用Array的构造函数创建数组:
var numbers = new Array();
var numbers = new Array(1,2,3,4,5);//数组长度为5
//在调用Array的构造函数时,可以只传入一个参数,用来指定数组的长度。
var numbers = new Array(10);//数组长度为10
在脚本语言里很常见一个特性是,数组中的元素不必是同一个数据类型。
var objects = [1,"joe",true,null];
可以通过Array.isArray()来判断一个对象是否是数组。
var numbers = 3;
var arr = [1,2,3,4,5];
console.log(Array.isArray(numbers));//false
console.log(Array.isArray(arr));//true
2.2 读写数组
使用[ ]操作符:
var nums = [];
for(var i=0;i<100;i++){
nums[i] = i+1;
}
var nums = [1,2,3,4,5];
var sum = 0;
for(var i=0;i<nums.length;i++){
sum+= nums[i];
}
console.log(sum);
由字符串生成数组:
var sentence = "the quick brown fox jumped over the lazy dog";
var words = sentence.split(" ");
for(var i=0;i<words.length;++i){
console.log("word" + i + ":" + words[i]);
}
复制数组:
首先,可以将一个数组赋给另外一个数组。但是,简单的复制,只是为被复制的数组增加了一个新的引用。
当你通过原引用修改了数组的值,另外一个引用也会感知到这个变化。这种行为被称为浅复制。新数组依然指向原来的数组。
var nums = [];
for(var i=0;i<100;++i){
nums[i] = i+1;
}
var samenums = nums;
nums[0] = 400;
console.log(samenums[0]);//400
深复制:将原数组中的每一个元素都复制一份到新数组中。
function copy(arr1,arr2){
for(var i=0;i<arr1.length;i++){
arr2[i] = arr1[i];
}
}
var nums = [];
for(var i=0;i<100;i++){
nums[i] = i+1;
}
var samenums = [];
copy(nums,samenums);
nums[0] = 400;
console.log(samenums[0]); //1
2.3 存取函数
js提供一组用来访问数组元素的函数,叫做存取函数。
查找元素:indexOf()
用途:用来查找传进来的参数在目标数组中是否存在。如果数组中包含多个相同的元素,indexOf()函数总是返回第一个与参数相同的元素的索引。
返回值:如果目标数组包含该参数,就返回该元素在数组中中的索引;如果不包含,就返回-1。
var names = ["David","Cynthia","Raymond","Clayton","Jennifer"];
var name = "joey";
var position = names.indexOf(name);
if(position !== -1){//或if(position >=0 )
console.log("Found" + position);
}else{
console.log("Not Found");
}
lastIndexOf():该函数返回相同元素中最后一个元素的索引。如果没有找到相同的元素,则返回-1.
var names = ["David","Cynthia","Mike","Raymond","Clayton","Jennifer","Mike"];
var name = "Mike";
console.log(names.indexOf(name));//2
console.log(names.lastIndexOf(name));//6
数组的字符串表示:join() 和 toString()
这两个方法都返回一个包含数组所有元素的字符串,各元素之间用逗号分隔。
var names = ["David","Cynthia","Mike","Raymond","Clayton","Jennifer","Mike"];
var namesstr = names.join();
console.log(namesstr);//David,Cynthia,Mike,Raymond,Clayton,Jennifer,Mike
var namesstr = names.toString();
console.log(namesstr);//David,Cynthia,Mike,Raymond,Clayton,Jennifer,Mike
由已有数组创建新数组:concat()和splice()
concat():可以合并多个数组创建一个新数组。
splice():截取一个数组的子集创建一个新数组。
var cisDept = ["David","Cynthia","Mike","Raymond","Clayton","Jennifer","Mike"];
var dmpDept = ["Danny","Bryan","Terrill"];
var itDiv = cisDept.concat(dmpDept);
console.log(itDiv);//["David", "Cynthia", "Mike", "Raymond", "Clayton", "Jennifer", "Mike", "Danny", "Bryan", "Terrill"]
splice():该方法的第一个参数是截取的起始索引,第二个参数是截取的长度。
var cisDept = ["David","Cynthia","Mike","Raymond","Clayton","Jennifer","Mike"];
var dmpDept = cisDept.splice(3,3);
console.log(dmpDept);// ["Raymond", "Clayton", "Jennifer"]
2.4 可变函数
为数组添加元素:push()和unshift()
第3章 列表
3.1 列表的抽象数据类型定义
listSize(属性) | 列表的元素个数 |
pos(属性) | 列表的当前位置 |
length(属性) | 返回列表中元素的个数 |
clear()(方法) | 清空列表中的所有元素 |
toString()(方法) | 返回列表的字符串行式 |
getElement()(方法) | 返回当前位置的元素 |
insert()(方法) | 在现有元素后插入新元素 |
append()(方法) | 在列表的末尾添加新元素 |
remove()(方法) | 在列表中删除元素 |
front()(方法) | 将列表的当前位置移动到第一个元素 |
end()(方法) | 将列表的当前位置移动到最后一个元素 |
pre()(方法) | 将当前位置后移一位 |
next()(方法) | 将当前位置前移一位 |
currPos()(方法) | 返回列表的当前位置 |
moveTo()(方法) | 将当前位置移动到指定位置 |
3.2 实现列表类
function List(){
this.listSize = 0;
this.pos = 0;
this.dataStore = [];//初始化一个空数组来保存列表元素
this.clear = clear;
this.find = find;
this.toString = toString;
this.append = append;
this.remove = remove;
this.front = front;
this.end = end;
this.prev =prev;
this.next = next;
this.length = length;
this.currPos = currPos;
this.moveTo = moveTo;
this.getElement = getElement;
this.contains = contains;
}
append:给列表添加源:
function append(element){
this.dataStore[this.listSize++] = element;
}
remove:从列表中删除元素:首先需要在列表中找到该元素,然后删除它。
find()方法用查找要删除的元素:
function find(element){
for(var i=0;i<this.dataStore.length;++i){
if(this.dataStore[i] == element){
return i;
}
}
return -1;
}
find() 方法通过对数组对象dataStore 进行迭代,查找给定的元素。如果找到,就返回该元素在列表中的位置,否则返回-1,这是在数组中找不到指定元素时返回的标准值。我们可以在remove() 方法中利用此值做错误校验。
remove() 方法使用find() 方法返回的位置对数组dataStore 进行截取。数组改变后,将变量listSize 的值减1,以反映列表的最新长度。如果元素删除成功,该方法返回true,否则返回false
function remove(element){
var foundAt = this.find(element);
if(foundAt > -1){
this.dataStore.splice(foundAt,1);
--this.listSize;
return true;
}
return false;
}
length:列表中有多少个元素
function length(){
return this.listSize;
}
toString:显示列表中的元素
function toString(){
return this.dataStore;
}
简单的测试:
var names = new List();
names.append("Cynthia1");
names.append("Cynthia2");
names.append("Cynthia3");
names.append("Cynthia4");
console.log(names.toString());//(4) ["Cynthia1", "Cynthia2", "Cynthia3", "Cynthia4"]
names.remove("Cynthia4");
console.log(names.toString());//(3) ["Cynthia1", "Cynthia2", "Cynthia3"]
insert:向列表中插入一个元素
insert() 方法用到了find() 方法,find() 方法会寻找传入的after 参数在列表中的位置,找到该位置后,使用splice() 方法将新元素插入该位置之后,然后将变量listSize 加1 并返回true,表明插入成功。
function insert(element,after){
var insertPos = this.find(after);
if(insertPos > -1){
this.dataStore.splice(insertPos+1,0,element);
++this.listSize;
return true;
}
return false;
}
clear:清空列表中所有的元素
clear() 方法使用delete 操作符删除数组dataStore,接着在下一行创建一个空数组。最后一行将listSize 和pos 的值设为1,表明这是一个新的空列表。
function clear(){
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
contains:判断给定值是否在列表中
function contains(element){
for(var i=0;i<this.dataStore.length;i++){
if(this.dataStore[i] == element){
return true;
}
}
return false;
}
遍历列表:
function front(){
this.pos = 0;
}
function end(){
this.pos = this.listSize - 1;
}
function prev(){
if(this.pos > 0){
--this.pos;
}
}
function next(){
if(this.pos < this.listSize - 1){
++this.pos;
}
}
function currPos(){
return this.pos;
}
function moveTo(position){
this.pos = position;
}
function getElement(){
return this.dataStore[this.pos];
}
var names = new List();
names.append("Clayton1");
names.append("Clayton2");
names.append("Clayton3");
names.append("Clayton4");
names.append("Clayton5");
names.append("Clayton6");
names.append("Clayton7");
names.front();
console.log(names.getElement());//Clayton1
names.next();
console.log(names.getElement());//Clayton1
3.3 使用迭代器访问列表
使用迭代器,可以不必关心数据的内部存储方式,以实现对列表的遍历。前面提到的方法front()、end()、prev()、next() 和currPos 就实现了cList 类的一个迭代器。以下是和使用数组索引的方式相比,使用迭代器的一些优点。
•访问列表元素时不必关心底层的数据存储结构。
• 当为列表添加一个元素时,索引的值就不对了,此时只用更新列表,而不用更新迭代器。
• 可以用不同类型的数据存储方式实现cList 类,迭代器为访问列表里的元素提供了一种
统一的方式。
for(names.front();names.currPos() < names.length();names.next()){
console.log(names.getElement);
}
第4章 栈
4.1 栈的操作
栈:后人先出
push():入栈
pop():出栈
peek():只返回栈顶元素,而不删除它。
clear():清除栈内所有元素。
empty属性,length属性
4.2 栈的实现
//定义Stack类的构造函数
function Stack(){
this.dataStore = [];
this.top = 0;
this.push = push;
this.pop = pop;
this.peek = peek;
this.clear = clear;
this.length = length;
}
//实现push()方法:向栈中压入一个新元素,需要将其保存在数组中变量top所对应的位置,top值加1
function push(element){
this.dataStore[this.top++] = element;
}
//pop()方法:返回栈顶元素,同时将变量top的值减1
function pop(){
return this.dataStore[--this.top];
}
//peek()方法:返回数组的第top-1个位置的元素,即栈顶元素
function peek(){
return this.dataStore[this.top-1];
}
//length()方法:通过返回top值的方式返回栈内的元素个数
function length(){
return this.top;
}
//clear()方法:将变量top的值设为0,清空一个栈
function clear(){
this.top = 0;
}
//测试
var s = new Stack();
s.push("David1");
s.push("David2");
s.push("David3");
s.push("David4");
console.log(s.length());//4
var poped = s.pop();
console.log(poped);//Davidi4
console.log(s.peek());//Davidi3
s.push("Cynthia");
console.log(s.peek());//Cynthia
s.clear();
console.log(s.length());//0
console.log(s.peek());//undefined
s.push("Cynthia2");
console.log(s.peek());//Cynthia2
4.3 使用Stack类
4.3.1 数值间的相互转换
可以利用栈将一个数字从一种数制转换成另一种数制。假设将数字n转换为以b为基数的数字,转换思想如下:
- 最高位为n % b,将此压入栈。
- 使用n / b代替n.
- 重复上两步,直到n等于0,且没有余数。
- 持续将栈内元素弹出,直到栈为空,依次将这些元素排列。
将八进制转化为二进制
function mulBase(num,base){
var s = new Stack();
do{
s.push(num % base);
num = Math.floor(num /= base);
}while(num>0);
var converted = "";
while(s.length() > 0){
converted += s.pop();
}
return converted;
}
var num = 32;
var base = 2;
var newNum = mulBase(num,base);
console.log(newNum);//100000
4.3.2 判断一个单词是否是回文
回文:一个单词、短语或是数字,从前往后和从后往前都是一样的。比如:"dad","racecar",1001等
思路:字符串完整压入栈内后,通过持续弹出栈中的每个字母就可以得到一个新字符串,该字符串刚好与原来的字符串顺序相反。比较两个字符串,如果两个字符串相等就是回文。
function isPlindrome(word){
var s = new Stack();
for(var i=0;i<word.length;i++){
s.push(word[i]);
}
var rword = "";
while(s.length() > 0){
rword += s.pop();
}
if(word == rword){
return true;
}else{
return false;
}
}
var word = "abccba";
console.log(isPlindrome(word));//true
4.3.3 栈实现递归
//递归函数
function factorial(n){
if(n === 0){
return 1;
}else{
return n*factorial(n-1);
}
}
//使用栈模拟递归过程
function fact(n){
var s = new Stack();
while(n>1){
s.push(n--);
}
var product = 1;
while(s.length() > 0){
product *= s.pop();
}
return product;
}
console.log(factorial(5));//120
console.log(fact(5));//120
第五章 队列
5.1 队列的操作
队列:先进先出
入队:向队列中插入新元素
出队:删除操作
5.2 一个用数组实现的队列
function Queue() {
this.dataStore = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.back = back;
this.toString = toString;
this.empty = empty;
}
//enqueue()方法:向对尾添加一个元素
function enqueue(element) {
this.dataStore.push(element);
}
//dequeue()方法:删除队首的元素
function dequeue() {
return this.dataStore.shift();
}
//front()方法:读取队首的元素
function front() {
return this.dataStore[0];
}
//back()方法:读取队尾的元素
function back() {
return this.dataStore[this.dataStore.length-1];
}
//toString()方法:显示队列内的所有内容
function toString() {
var retStr = "";
for (var i = 0; i < this.dataStore.length; ++i) {
retStr += this.dataStore[i] + "\n";
}
return retStr;
}
//empty()方法:判断队列是否为空
function empty() {
if (this.dataStore.length == 0) {
return true;
}
else {
return false;
}
}
//测试
var q = new Queue();
q.enqueue("Meredith");
q.enqueue("Cynthia");
q.enqueue("Jennifer");
console.log(q.toString());//Meredith Cynthia Jennifer
q.dequeue();
console.log(q.toString());//Cynthia Jennifer
console.log("Front of queue: " + q.front());//Front of queue: Cynthia
console.log("Back of queue: " + q.back());//Back of queue: Jennifer
5.3 使用队列: 方块舞伴分配问题
第6章 链表
6.1 设计一个基于对象的链表
//使用构造函数来创建节点
function Node(element){
this.element = element;
this.next = null;
}
//LinkedList类
function LList(){
this.head = new Node("head");
this.find = find;
this.insert = insert;
this.remove = remove;
this.display = display;
}
插入新节点
首先要明确在哪个节点前面或者后面插入一个新的节点。在一个已知节点后面插入元素时,先要找到“后面”的节点。为此,创建一个辅助方法find(),该方法遍历链表,查找给定数据。如果找到数据,该方法就返回保存该数据的节
点。find() 方法的实现代码如下所示:
function find(item){
var currNode = this.head;
while(currNode.element != item){
currNode = currNode.next;
}
return currNode;
}
//insert()
function insert(newElement,item){
var newNode = new Node(newElement);
var current = this.find(item);
newNode.next = current.next;
current.next = newNode;
}
//display():显示链表中的元素
function display(){
var currNode = this.head;
while(!(currNode.next == null)){
console.log(currNode.next.element);
currNode = currNode.next;
}
}
第10章 二叉树和二叉查找树
10.1 实现二叉查找树
Node类的定义:
function Node(data,left,right){
this.data = data;
this.left = left;
this.right = right;
this.show = show;
}
function show(){
return this.data;
}
BST 先要有一个insert() 方法,用来向树中加入新节点。这个方法有点复杂,需要着重讲解。首先要创建一个Node 对象,将数据传入该对象保存。其次检查BST 是否有根节点,如果没有,那么这是棵新树,该节点就是根节点,这个方法到此也就完成了;否则,进入下一步。
如果待插入节点不是根节点,那么就需要准备遍历BST,找到插入的适当位置。该过程类似于遍历链表。用一个变量存储当前节点,一层层地遍历BST。进入BST 以后,下一步就要决定将节点放在哪个地方。找到正确的插入点时,会跳出循环。查找正确插入点的算法如下。
(1) 设根节点为当前节点。
(2) 如果待插入节点保存的数据小于当前节点,则设新的当前节点为原节点的左节点;反
之,执行第4 步。
(3) 如果当前节点的左节点为null,就将新的节点插入这个位置,退出循环;反之,继续
执行下一次循环。
(4) 设新的当前节点为原节点的右节点。
(5) 如果当前节点的右节点为null,就将新的节点插入这个位置,退出循环;反之,继续
执行下一次循环。
function BST(){
this.root = null;
this.insert = insert;
this.inOrder = inOrder;
}
function insert(data){
var n = new Node(data,null,null);
if(this.root == null){
this.root = n;
}else{
var current = this.root;
var parent;
while(true){
parent = current;
if(data <current.data){
current = current.left;
if(current == null){
parent.left = n;
break;
}
}else{
current = current.right;
if(current == null){
parent.right = n;
break;
}
}
}
}
}