文章首发在我的个人博客:http://www.brandhuang.com/article/1575117792236
这是之前学习记录的一篇文章,最近准备面试复习一下,内容做了些修修补补,如有错误欢迎指出
本文来自于学习 《JavaScript数据结构与算法(第3版)》 以及网路资料,如有不对请指正。
作为软件开发工作者,可能你听过这么一句话:程序 = 数据结构 + 算法。可见数据结构和算法在我们的编码工作中是非常的重要的。如果我们使用了不恰当的数据结构或者算法,可能会影响我们程序的性能。总之,对于算法和数据结构,我们只需要撸起袖子加油学。
数组——最简单的内存数据结构
数组存储一系列同一种数据类型的值。( Javascript
中不存在这种限制)
对数据的随机访问,数组是更好的选择,否则几乎可以完全用 「链表」 来代替
在很多编程语言中,数组的长度是固定的,当数组被填满时,再要加入新元素就很困难。
Javascript
中数组不存在这个问题。
但是
Javascript
中的数组被实现成了对象,与其他语言相比,效率低下。
数组的一些核心方法
方法 | 描述 |
---|---|
push | 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度 。(改变原数组 ) |
pop | 方法从数组中删除最后一个元素,并返回该元素的值 。(改变原数组 ) |
shift | 方法从数组中删除第一个元素,并返回该元素 的值,如果数组为空则返回 undefined 。(改变原数组 ) |
unshift | 将一个或多个元素添加到数组的开头,并返回该数组的新长度 (改变原数组 ) |
concat | 连接两个或多个数组,并返回结果(返回一个新数组,不影响原有的数组 。) |
every | 对数组中的每个元素运行给定函数,如果该函数对每个元素 都返回 true ,则返回 true 。若为一个空数组,,始终返回 true。 (不会改变原数组 ,[].every(callback) 始终返回 true ) |
some | 对数组中的每个元素运行给定函数,如果任一元素 返回 true,则返回 true。若为一个空数组,,始终返回 false。(不会改变原数组 ,) |
forEach | 对数组中的每个元素运行给定函数。这个方法没有返回值 ,没有办法中止或者跳出 forEach() 循环,除了抛出一个异常(foreach不直接改变原数组 ,但原数组可能会被 callback 函数该改变。) |
map | 对数组中的每个元素运行给定函数,返回每次函数调用的结果组成的数组(map不直接改变原数组 ,但原数组可能会被 callback 函数该改变。) |
sort | 按照Unicode位点 对数组排序,支持传入指定排序方法的函数作为参数(改变原数组 ) |
reverse | 方法将数组中元素的位置颠倒,并返回该数组(改变原数组 ) |
join | 将所有的数组元素连接成一个字符串 |
indexOf | 返回第一个与给定参数相等的数组元素的索引,没有找到则返回 -1 |
lastIndexOf | 返回在数组中搜索到的与给定参数相等的元素的索引里最大的值,没有找到则返回 -1 |
slice | 传入索引值,将数组里对应索引范围内的元素(浅复制原数组中的元素 )作为新数组返回(原始数组不会被改变 ) |
splice | 删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容(改变原数组 ) |
toString | 将数组作为字符串返回 |
valueOf | 和 toString 类似,将数组作为字符串返回 |
栈是一种遵循后进先出(LIFO)原则的有序集合,新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
通俗来讲,就是你向一个桶里放书本或者盘子,你要想取出最下面的书或者盘子,你必须要先把上面的都先取出来。
栈也被用在编程语言的编译器和内存中保存变量、方法调用等,也被用于浏览器历史记录 (浏览器的返回按钮)。
代码实现
// 封装栈
function Stack() {
// 栈的属性
this.items = []
// 栈的操作
// 1.将元素压入栈
Stack.prototype.push = function (element) {
this.items.push(element)
}
// 2.从栈中取出元素
Stack.prototype.pop = function () {
return this.items.pop()
}
// 3.查看栈顶元素
Stack.prototype.peek = function () {
return this.items[this.items.length - 1]
}
// 4.判断是否为空
Stack.prototype.isEmpty = function () {
return this.items.length === 0
}
// 5.获取栈中元素的个数
Stack.prototype.size = function () {
return this.items.length
}
// 6.toString()方法
Stack.prototype.toString = function () {
let str = ''
for (let i = 0; i< this.items.length; i++) {
str += this.items[i] + ' '
}
return str
}
}
// 栈的使用
let s = new Stack()
队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新
元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
生活中常见的就是排队
代码实现
function Queue() {
this.items = []
// 1.将元素加入队列
Queue.prototype.enqueue = function (element) {
this.items.push(element)
}
// 2.从队列前端删除元素
Queue.prototype.dequeue = function () {
return this.items.shift()
}
// 3.查看队列前端元素
Queue.prototype.front = function () {
return this.items[0]
}
// 4.判断是否为空
Queue.prototype.isEmpty = function () {
return this.items.length === 0
}
// 5.获取队列中元素的个数
Queue.prototype.size = function () {
return this.items.length
}
// 6.toString()方法
Queue.prototype.toString = function () {
let str = ''
for (let i = 0; i< this.items.length; i++) {
str += this.items[i] + ' '
}
return str
}
}
// 队列使用
let Q = new Queue()
代码实现
function PriorityQueue() {
function QueueElement(element, priority) {
this.element = element
this.priority = priority
}
this.items = []
PriorityQueue.prototype.enqueue = function (element, priority) {
let queueElement = new QueueElement(element,priority)
// 判断队列是否为空
if (this.isEmpty()) {
this.items.push(queueElement)
} else {
let added = false // 如果在队列已有的元素中找到满足条件的,则设为true,否则为false,直接插入队列尾部
for (let i = 0; i< this.items.length; i++) {
// 假设priority值越小,优先级越高,排序越靠前
if (queueElement.priority < this.items[i].priority) {
this.items.splice(i, 0, queueElement)
added = true
break
}
}
if (!added) {
this.items.push(queueElement)
}
}
}
}
链表——存储有序的元素集合
,但在内存中不是连续放置
的。
链表(单向链表)中的元素
由存放元素本身「data」
的节点和一个指向下一个「next」
元素的指针组成。牢记这个特点
相比数组,链表添加或者移除元素不需要移动其他元素,但是需要使用指针
。访问元素每次都需要从表头
开始查找。
代码实现:
单向链表
function LinkedList() {
function Node(data) {
this.data = data
this.next = null
}
this.head = null // 表头
this.length = 0
// 插入链表
LinkedList.prototype.append = function (data) {
// 判断是否是添加的第一个节点
let newNode = new Node(data)
if (this.length == 0) {
this.head = newNode
} else {
let current = this.head
while (current.next) {
// 如果next存在,
// 则当前节点不是链表最后一个
// 所以继续向后查找
current = current.next
}
// 如果next不存在
// 则当前节点是链表最后一个
// 所以让next指向新节点即可
current.next = newNode
}
this.length++
}
// toString方法
LinkedList.prototype.toString = function () {
let current = this.head
let listString = ''
while (current) {
listString += current.data + ' '
current = current.next
}
return listString
}
// insert 方法
LinkedList.prototype.insert = function (position, data) {
if (position < 0 || position > this.length) return false
let newNode = new Node(data)
if (position == 0) {
newNode.next = this.head
this.head = newNode
} else {
let index = 0
let current = this.head
let prev = null
while (index++ < position) {
prev = current
current = current.next
}
newNode.next = current
prev.next = newNode
}
this.length++
return true
}
// get方法
LinkedList.prototype.get = function (position) {
if (position < 0 || position >= this.length) return null
let index = 0
let current = this.head
while (index++ < position){
current = current.next
}
return current.data
}
LinkedList.prototype.indexOf = function (data) {
let index = 0
let current = this.head
while (current) {
if (current.data == data) {
return index
} else {
current = current.next
index++
}
}
return -1
}
LinkedList.prototype.update = function (position, data) {
if (position < 0 || position >= this.length) return false
let index = 0
let current = this.head
while (index++ < position) {
current = current.next
}
current.data = data
return true
}
LinkedList.prototype.removeAt = function (position) {
if (position < 0 || position >= this.length) return null
if (position == 0) {
this.head = this.head.next
} else {
let index = 0
let current = this.head
let prev = null
while (index++ < position) {
prev = current
current = current.next
}
prev.next = current.next
}
this.length--
return true
}
LinkedList.prototype.remove = function (data) {
let postions = this.indexOf(data)
return this.removeAt(postions)
}
}
let list = new LinkedList()
双向链表:包含表头
、表尾
和 存储数据的 节点
,其中节点
包含三部分:一个链向下一个元素的next
, 另一个链向前一个元素的prev
和存储数据的 data
。牢记这个特点
function doublyLinkedList() {
this.head = null // 表头:始终指向第一个节点,默认为 null
this.tail = null // 表尾:始终指向最后一个节点,默认为 null
this.length = 0 // 链表长度
function Node(data) {
this.data = data
this.prev = null
this.next = null
}
doublyLinkedList.prototype.append = function (data) {
let newNode = new Node(data)
if (this.length === 0) {
// 当插入的节点为链表的第一个节点时
// 表头和表尾都指向这个节点
this.head = newNode
this.tail = newNode
} else {
// 当链表中已经有节点存在时
// 注意tail指向的始终是最后一个节点
// 注意head指向的始终是第一个节点
// 因为是双向链表,可以从头部插入新节点,也可以从尾部插入
// 这里以从尾部插入为例,将新节点插入到链表最后
// 首先将新节点的 prev 指向上一个节点,即之前tail指向的位置
newNode.prev = this.tail
// 然后前一个节点的next(及之前tail指向的节点)指向新的节点
// 此时新的节点变成了链表的最后一个节点
this.tail.next = newNode
// 因为 tail 始终指向的是最后一个节点,所以最后修改tail的指向
this.tail = newNode
}
this.length++
}
doublyLinkedList.prototype.toString = function () {
return this.backwardString()
}
doublyLinkedList.prototype.forwardString = function () {
let current = this.tail
let str = ''
while (current) {
str += current.data + ''
current = current.prev
}
return str
}
doublyLinkedList.prototype.backwardString = function () {
let current = this.head
let str = ''
while (current) {
str += current.data + ''
current = current.next
}
return str
}
doublyLinkedList.prototype.insert = function (position, data) {
if (position < 0 || position > this.length) return false
let newNode = new Node(data)
if (this.length === 0) {
this.head = newNode
this.tail = newNode
} else {
if (position == 0) {
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
} else if (position == this.length) {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
} else {
let current = this.head
let index = 0
while( index++ < position){
current = current.next
}
newNode.next = current
newNode.prev = current.prev
current.prev.next = newNode
current.prev = newNode
}
}
this.length++
return true
}
doublyLinkedList.prototype.get = function (position) {
if (position < 0 || position >= this.length) return null
let current = this.head
let index = 0
while (index++) {
current = current.next
}
return current.data
}
doublyLinkedList.prototype.indexOf = function (data) {
let current = this.head
let index = 0
while (current) {
if (current.data === data) {
return index
}
current = current.next
index++
}
return -1
}
doublyLinkedList.prototype.update = function (position, newData) {
if (position < 0 || position >= this.length) return false
let current = this.head
let index = 0
while(index++ < position){
current = current.next
}
current.data = newData
return true
}
doublyLinkedList.prototype.removeAt = function (position) {
if (position < 0 || position >= this.length) return null
let current = this.head
if (this.length === 1) {
this.head = null
this.tail = null
} else {
if (position === 0) { // 删除第一个节点
this.head.next.prev = null
this.head = this.head.next
} else if (position === this.length - 1) { // 删除最后一个节点
this.tail.prev.next = null
this.tail = this.tail.prev
} else {
let index = 0
while (index++ < position) {
current = current.next
}
current.prev.next = current.next
current.next.prev = current.prev
}
}
this.length--
return current.data
}
doublyLinkedList.prototype.remove = function (data) {
let index = this.indexOf(data)
return this.removeAt(index)
}
}
感谢你的阅读~