《数据结构与算法JavaScript描述》读书笔记

《数据结构与算法JavaScript描述》读书笔记。

总的来说,书的质量不太高,如果作为前端工程师算法入门的书的话,不如去读《算法图解》,相比之下更容易理解,讲解也更加准确。这本JavaScript描述只是用了JavaScript去实现一些基础算法,而且实现的也不是很令人满意。

豆瓣评分:6.5 个人评分:☆☆☆

第1章 JavaScript的编程环境与模型

第2章 数组

第3章 列表

我的疑惑是,在实现列表的remove方法时,使用了数组的splice方法,那么为什么列表的其他方法不可以直接使用数组的方法来实现呢?

实际上列表完全可以用元素时对象的数组来代替。

第4章 栈

栈的实现

对于栈,我同样有上一章的疑问,入栈和出栈能够使用数组的pushpop方法来实现,为什么我们不可以直接使用呢?或者说,什么情况下可以使用数组的方法、什么时候不可以使用数组的方法来实现对应的数据结构呢?

栈的元素只能通过列表的一端访问,这一端成为栈顶。一摞盘子就是一个典型的栈,只能从最上面取盘子,盘子洗净后也只能放置在一摞盘子的最上面。

栈是一种先入后出(LIFO,last-in-first-out)的数据结构

栈包含的属性和方法有:

  • length,栈内存储了多少元素
  • push,入栈
  • pop,出栈
  • peak,返回栈顶元素
  • clear,清空站内元素

栈的底层数据结构可以使用数组实现,其中声明了一个内部变量top,记录栈顶位置,当栈内没有元素是,top为零

栈的完整实现:

function Stack() {
  this.data = [];
  this.top = 0;
}

Stack.prototype.push = function (val) {
  this.data[this.top++] = val;
};

Stack.prototype.pop = function () {
  return this.data[this.top <= 0 ? 0 : --this.top];
};

Stack.prototype.peak = function () {
  return this.data[this.top - 1];
};

Stack.prototype.length = function () {
  return this.top;
};

Stack.prototype.clear = function () {
  this.top = 0;
  this.data = [];
};

相比于书中的实现,我进行了三点改动

  1. 方法由实例方法改为了原型方法
  2. pop方法时判断了top的正负,因为top不能为负数
  3. clear时,同时将数据结构清空了

利用栈实现数制间的相互转换

利用栈将一个数字从一种数制转换为另一种,算法如下:

  1. 最高位为n%b,将此位压入栈中
  2. 使用n/b代替n
  3. 重复步骤1和2,直到n等于0,且没有余数
  4. 将栈内元素逐个弹出,直到栈为空

只适用于基数为2-9的情况

function mulBase(num, base) {
  let temp = num;
  const s = new Stack();
  let result = '';
  while (temp > 0) {
    s.push(temp % base);
    temp = Math.floor(temp / base);
  }
  while (s.length()) {
    result += s.pop();
  }
  return result;
}

console.log(mulBase(32, 2)); // 100000

判断回文字符串

回文指的是从前到后书写与从后到前书写都相同的单词、短语和数字,可以利用栈来判断回文字符串,方法就是将一个字符串按顺序压入栈中,然后再依次弹出,这样得到的字符串时一个与原来的字符串顺序相反的字符串,判断新字符串与旧字符串是否相同即可

function isPalindrome(word) {
  let s = new Stack();
  for (let i = 0; i < word.length; i++) {
    s.push(word[i]);
  }

  let newWord = '';
  while (s.length() > 0) {
    newWord += s.pop();
  }

  return newWord === word;
}

console.log(isPalindrome('hello')); // false
console.log(isPalindrome('racecar')); // true

第5章 队列

队列是一种先进先出(First-In-First-Out,FIFO)的数据结构,用在很多地方例如提交操作系统执行的一系列进程等。

可以使用数组模拟队列。

第6章 链表

数组的缺点

其他编程语言中的数组长度是固定的,当数组被数据填满后,要再加入新的元素会很困难。另外加入、删除元素,都需要移动其他元素,很麻烦。

JavaScript中的数组不存在上述问题,因为JavaScript中的数组实际上被实现成了对象,但是与其他语言的数组相比效率很低。

如果发现在使用时数组很慢,可以考虑使用链表来代替,除了对数据的随机访问外,链表几乎可以用在任何可以使用一维数组的情况。

定义链表

链表是一组节点组成的集合,每个节点都使用一个对象的引用指向它的后继。指向另一个节点的引用叫做链

数组元素靠位置进行引用,链表元素靠相互间的关系进行引用,下图是一个有头节点的链表

设计一个基于对象的链表

包含两个类:

  1. Node类表示节点
  2. LinkedList类提供插入节点、删除节点、显示列表元素的方法,以及一些辅助方法
function Node(element) {
  this.element = element;
  this.next = null;
}

function LList() {
  this.head = new Node('head');
}

LList.prototype.insert = function (newElement, item) {
  const currNode = this.find(item);
  const newNode = new Node(newElement);
  newNode.next = currNode.next;
  currNode.next = newNode;
};

LList.prototype.find = function (item) {
  let currNode = this.head;
  while (currNode.element !== item) {
    if (currNode.next === null) {
      return currNode;
    }
    currNode = currNode.next;
  }
  return currNode;
};

LList.prototype.remove = function () {};

LList.prototype.display = function () {
  let currNode = this.head;
  while (currNode.next) {
    console.log(currNode.next.element);
    currNode = currNode.next;
  }
};

const cities = new LList();
cities.insert('Beijing', 'head');
cities.insert('Tianjin', 'Beijing');
cities.insert('Shanghai', 'Tianjin');
cities.insert('Guangzhou', 'Beijing');

cities.display();

// Beijing
// Guangzhou
// Tianjin
// Shanghai

实现find时候稍有点不一样,判断了找不到的当前节点的情况,如果找不到的话就插入到最后

实现remove方法时,需要先实现findPrevious方法:

LList.prototype.findPrevious = function (item) {
  let currNode = this.head;
  while (currNode.next !== null && currNode.next.element !== item) {
    currNode = currNode.next;
  }
  return currNode;
};

LList.prototype.remove = function (item) {
  const prevNode = this.findPrevious(item);
  if (prevNode && prevNode.next) {
    prevNode.next = prevNode.next.next;
  }
};

const cities = new LList();
cities.insert('Beijing', 'head');
cities.insert('Tianjin', 'Beijing');
cities.insert('Shanghai', 'Tianjin');
cities.insert('Guangzhou', 'Beijing');

cities.remove('Guangzhou');

cities.display();
// Beijing
// Tianjin
// Shanghai

双向链表

普通链表从后向前遍历很麻烦,可以为Node类添加一个属性,该属性存储指向前驱节点的链接,这样反向遍历就容易多了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值