数据结构(一)
说明:本文基于哔哩哔哩视频【JavaScript数据结构与算法】整理
一、基本概念:
数据的组织方式
二、生活中的实例:
图书的摆放,快递分发
三、常见的数据结构
注意事项
- (操作性能不同:查询速度、插入、范围查找、重复性)
- ※ ※※ ※ ※ 是重点,每个编程语言都提供的数据类型
- JS 是API的使用者,对数据结构接触不多;
数组(Array) ※ ※ ※ ※ ※
-
线性结构 查找性能较高(利用下标,有序)或者较低(基于内容)插入效率低
-
操作:
-
长度
-
增删改查(splice)
-
索引(indexOf/lastIndexOf)
-
数组合并(cancat)
-
迭代(every,some,forEach,filter,map,reduce【较难,用于函数编程】)
-
倒序(reverse)
-
截取(slice)
-
排序(sort)
-
转为字符串(toString/valueOf);
-
缺点:需要一整块连续的内存空间,插入和删除需要大量的位移,成本较高。
-
操作的代码实现
var arr = [1, 2, 3, 4, 5]
var arr1 = new Array() // 效率低
arr1 = ['q', 'w', 'e']
// 数组的长度
console.log(arr.length);
// 数组的遍历
arr.forEach((item, i) => {
console.log(item == arr[i]);
});
// 数组元素插入
arr.splice(2, 0, 'a', 'b') // [1, 2, "a", "b", 3, 4, 5] "插入"
console.log(arr, '插入');
// 删除
arr.splice(1, 3)
console.log(arr, '删除'); // [1, 3, 4, 5] "删除"
// 修改
arr.splice(1, 1, '1123456', 'gh')
console.log(arr, '修改'); // [1, "1123456", "gh", "a", "b", 3, 4, 5] "删除"
// 获取
console.log(arr[0]); // 1
// 拼接数组
var arrC = arr.concat(arr1)
console.log(arrC, '拼接数组'); // [1, "1123456", "gh", 4, 5, "q", "w", "e"] "拼接数组"
// 拼接 返回字符串
// var arrJ = arr.join(',')
// console.log(arrJ, '==join'); // 1,1123456,gh,4,5 ==join
// 转化为字符串
console.log(arr.toString(), '转化为字符串 toString'); // 1,1123456,gh,4,5
console.log(arr.valueOf(3), '转化 value of'); // [1, "1123456", "gh", 4, 5] value.valueOf() 返回value 的原始值
// 数组的迭代
// every,some(判断数组中是不是包含某一项或者所有项)
let flag = arr.every(item => {
// console.log(item, 'item');
return item.toString().indexOf('1') != -1
})
console.log(flag, '迭代 every'); // every=>false some=> true
//filter map
let arrF = arr.filter(item => {
return item.toString().indexOf('1') != -1
})
console.log(arrF, '迭代 filter'); // [1, "1123456"]
let arrMap = arr.map(item => {
return item + " &"
})
console.log(arrMap, '迭代 map'); // ["1 &", "1123456 &", "gh &", "4 &", "5 &"]
// arr.reduce(callback[, initialValue])
let arrReduce = arr.reduce((pre, cur) => {
return pre + ',' + cur
})
console.log(arrReduce, '迭代 reduce'); // 1,1123456,gh,4,5
// 优势在于reduce方法有返回值, 而forEach没有 【 函数式编程 】
队列(queue)
-
基于数组
-
受限的线性结构 从前端删除,从后端插入(先进先出【 FIFO 】:FIRST IN FIRST OUT)
-
生活实例:排队;
-
程序中的应用:打印机的打印队列;线程队列;
-
栈结构的实现方式:
-
基于数组 栈中存放数组。代码实现。
-
基于链表
具体操作
-
enqueue(element):向队列尾部添加一个(或多个)新的项。
-
dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。shift 删除第一个
-
front():返回队列中第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)。
-
isEmpty():如果队列中不包含任何元素,返回true,否则返回false。
-
size():返回队列包含的元素个数,与数组的length属性类似
-
操作的代码实现
// 受限的线性结构
function Queue(params) {
// 属性
this.items = [];
// 方法
// enqueue(element):向队列尾部添加一个(或多个)新的项。
Queue.prototype.enqueue = function(ele) {
this.items.push(ele);
};
// dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。
Queue.prototype.dequeue = function() {
return this.items.shift();
};
// front():返回队列中第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)。
Queue.prototype.front = function() {
return this.items[0];
};
// isEmpty():如果队列中不包含任何元素,返回true,否则返回false。
Queue.prototype.isEmpty = function() {
return this.items.length === 0;
};
// size():返回队列包含的元素个数,与数组的length属性类似
Queue.prototype.size = function() {
return this.items.length;
};
// return this.items
}
- 击鼓传花案例
// 实现击鼓传花的函数
function passGame(nameList, num) {
// 1.创建一个队列, 并且将所有的人放在队列中
// 1.1.创建队列
var queue = new Queue();
// 1.2.通过for循环, 将nameList中的人放在队列中
for (var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i]);
}
// 2.寻找最后剩下的人
while (queue.size() > 1) {
// 将前num-1中的人, 都从队列的前端取出放在队列的后端
for (var i = 0; i < num; i++) {
queue.enqueue(queue.dequeue());
}
// 将第num个人, 从队列中移除
queue.dequeue();
}
// 3.获取剩下的一个人
alert(queue.size());
var endName = queue.dequeue();
alert("最终留下来的人:" + endName);
// 4.获取该人在队列中的位置
return nameList.indexOf(endName);
}
*** 优先级队列(有特权,可以插队的队列)。
// 封装优先级队列
function PriorityQueue() {
var items = [];
// 封装一个新的构造函数, 用于保存元素和元素的优先级
function QueueElement(element, priority) {
this.element = element;
this.priority = priority;
}
// 添加元素的方法
this.enqueue = function(element, priority) {
// 1.根据传入的元素, 创建新的QueueElement
var queueElement = new QueueElement(element, priority);
// 2.获取传入元素应该在正确的位置
if (this.isEmpty()) {
items.push(queueElement);
} else {
var added = false;
for (var i = 0; i < items.length; i++) {
// 注意: 我们这里是数字越小, 优先级越高
if (queueElement.priority < items[i].priority) {
items.splice(i, 0, queueElement);
added = true;
break;
}
}
// 遍历完所有的元素, 优先级都大于新插入的元素时, 就插入到最后
if (!added) {
items.push(queueElement);
}
}
};
// 删除元素的方法
this.dequeue = function() {
return items.shift();
};
// 获取前端的元素
this.front = function() {
return items[0];
};
// 查看元素是否为空
this.isEmpty = function() {
return items.length == 0;
};
// 获取元素的个数
this.size = function() {
return items.length;
};
}
栈(stack)
-
基于数组
-
受限的线性结构
-
插入或者删除位置只能在栈顶
-
进栈出栈(后进先出【 LIFO 】:因为只能在栈顶进或者出)
-
生活实例:盘子叠放拿取, 程序中的应用:函数调用栈(A=调用==>B调用>C),C 最早被调用;递归; 十进制转二进制
-
面试题:654321的顺序进栈,选出下列不能实现的出栈顺序 A、543612 B、453216 C、346521 D、234156
-
栈结构的实现方式:
-
基于数组 栈中存放数组。代码实现(见下方)。
-
基于链表
-
常用的操作:
-
push(element): 添加一个新元素到栈顶位置.
-
pop():移除栈顶的元素,同时返回被移除的元素。删除最后一个
-
peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
-
isEmpty():如果栈里没有任何元素就返回true,否则返回false。 clear():移除栈里的所有元素。
-
size():返回栈里的元素个数。这个方法和数组的length属性很类似。
-
代码实现
// 栈结构的封装
function Stack() {
// 栈的属性
var items = []; //基于数组实现
// 栈的操作
// push(element): 添加一个新元素到栈顶位置. // 压栈操作
Stack.prototype.push = function(ele) {
// console.log(this, 'this');
items.push(ele);
};
// pop():移除栈顶的元素,同时返回被移除的元素。 // 出栈
Stack.prototype.pop = function() {
items.pop();
};
// peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
Stack.prototype.peek = function() {
console.log(items, "item");
return items[items.length - 1];
};
// isEmpty():如果栈里没有任何元素就返回true,否则返回false。
Stack.prototype.isEmpty = function() {
return items.length === 0;
};
// size():返回栈里的元素个数。这个方法和数组的length属性很类似。
Stack.prototype.size = function() {
return items.length;
};
// clear():移除栈里的所有元素。
Stack.prototype.clear = function() {
return items = [];
};
// toString():栈里的所有元素转化为字符。
Stack.prototype.toString = function() {
let res = "";
items.forEach((item) => {
res += item + " ";
});
return res;
};
}
其他数据结构可访问以下地址:
数据结构与算法(二)之单向链表和双向链表(偏向JS)
数据结构与算法(三)之集合,字典(偏向JS).