1、栈的结构实现
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1]
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
2、栈的结构应用—十进制转二进制
function dec2bin(num) {
const sk = new Stack();
while (num > 0) {
// 获得余数
let remainder = num % 2;
// 获得商
num = Math.floor(num / 2);
sk.push(remainder);
}
// 拼接字符串
let str = '';
while (!sk.isEmpty()) {
str += sk.pop();
}
return str;
}
console.log(dec2bin(100))
3、队列数据结构的实现
class Queue{
constructor() {
this.items = [];
}
enqueue(item){
this.items.push(item);
}
dequeue(){
return this.items.shift();
}
front(){
if(this.isEmpty()) return null;
return this.items[0];
}
isEmpty(){
return this.items.length === 0;
}
size(){
return this.items.length;
}
}
4、队列实现击鼓传花
function passGame(nameList, number) {
// 创建队列
const queue = new Queue();
// 2、循环让这些人进入到队列中
for (name of nameList) {
queue.enqueue(name);
}
while (queue.size() > 1){
// 淘汰最后一个人
queue.dequeue();
// 将当前第一个人添加到最后一个位置
for (let i = 0; i < number - 1; i++) {
queue.enqueue(queue.dequeue());
}
}
return queue.front();
}
console.log(passGame(['tim', 'tom', 'li', 'lucy'], 5))
5、优先级队列的封装
class QueElement {
constructor(ele, pri) {
this.element = ele;
this.priority = pri;
}
}
// 数字越小,优先级越高
class PriorityQueue {
constructor() {
this.items = [];
}
enqueue(item, priority) {
const it = new QueElement(item, priority);
if (this.isEmpty()) {
this.items.push(it);
} else {
// 如果一直没有找到元素,也就是被插入的元素是第一个的情况下,前面的元素为空
let is_added = false;
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].priority > it.priority) {
// 只要找到了合适的那个位置
this.items.splice(i, 0, it);
is_added = true;
break;
}
}
if (!is_added) {
this.items.splice(0, 0, it);
}
}
}
dequeue() {
return this.items.shift();
}
front() {
if (this.isEmpty()) return null;
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
6、 单向链表的封装
class Node {
constructor(element) {
this.value = element;
this.next = null;
}
}
class Linklist {
constructor() {
this.head = null;
this.length = 0;
}
// 向末尾添加一个元素
append(value) {
const node = new Node(value);
if (!this.head) {
this.head = node;
} else {
// 找到最后一个节点,然后添加指针
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
this.length++;
}
// 插入一个元素
insert(position, value) {
// 判断插入的元素是否越界
if (position < 0 || position > this.length) return false;
// 创建一个新的节点
const node = new Node(value);
// 插入元素
if (position === 0) {
node.next = this.head;
this.head = node;
} else {
let current = this.head;
let previous = null;
for (let i = 0; i < position; i++) {
previous = current;
current = current.next;
}
previous.next = node;
node.next = current;
}
this.length++;
return true;
}
// 获取莫一个位置的元素
get(position) {
if (position < 0 || position > this.length - 1) return null;
// 查找该位置的元素
let current = this.head;
let index = 0;
while (index++ < position) {
current = current.next;
}
return current.value;
}
// 返回元素的索引,如果没有的话返回-1
indexOf(value) {
let current = this.head;
for (let i = 0; i < this.length; i++) {
if (current.value === value) {
return i;
}
current = current.next;
}
return -1;
}
// 删除某以位置的元素
removeAt(position) {
if (position < 0 || position > this.length - 1) return null;
let current = this.head;
if (position === 0) {
// 内存回收机制,当并没有使用这个元素的时候,内存会自动回收
this.head = this.head.next;
} else {
let previous = null;
for (let i = 0; i < position; i++) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length--;
}
// update 修改某一位置的元素
update(position, value) {
this.removeAt(position);
this.insert(position, value);
}
// 删除摸一个元素,根据值来删除
remove(value) {
const index = this.indexOf(value);
if (index === -1) return null;
this.removeAt(index);
this.length--;
}
isEmpty() {
return this.length === 0;
}
size() {
return this.length;
}
}
7、双向链表的封装
class DoubleNode {
constructor(value) {
this.value = value;
this.next = null;
this.previous = null;
}
}
class DoubleLinkList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
// 向链表的末尾添加一个元素
append(value) {
const node = new DoubleNode(value);
// 1、原来链表里面没有一个节点
// 2、原来链表有节点
if (this.head === null) {
this.head = node;
this.tail = node;
} else {
let current = this.head;
// 找到最后一个节点, 但是没有这个必要,tail指向的是最后一个节点
// while (current.next){
// current = current.next;
// }
// 更新指针
this.tail.next = node;
node.previous = this.tail;
// 更新末尾节点
this.tail = node;
}
this.length++;
}
// 插入一个元素, 在中间的莫一个位置
insert(position, value) {
if (position < 0 || position > this.length - 1) return null;
const node = new DoubleNode(value);
if (position === 0) {
// 原来本来就没有元素
if (this.head === null) {
this.head = node;
this.tail = node;
} else {
// 原来本来就有元素
node.next = this.head;
this.head.previous = node;
this.head = node;
}
} else if (position === this.length) {
// 如果插入的位置是最后面的元素
this.tail.next = node;
node.previous = this.tail;
// 更新末尾节点
this.tail = node;
} else {
let current = this.head;
// 插入的节点不是头部后者尾部
for (let i = 0; i < position; i++) {
current = current.next;
}
node.previous = current;
node.next = current.next;
current.next.previous = node;
current.next = node;
}
this.length++;
return true;
}
// 获取某一个位置的元素
get(position) {
if (position < 0 || position > this.length - 1) return null;
// 查找该位置的元素
let current = this.head;
let index = 0;
while (index++ < position) {
current = current.next;
}
return current.value;
}
// 返回元素的索引,如果没有的话返回-1
indexOf(value) {
let current = this.head;
for (let i = 0; i < this.length; i++) {
if (current.value === value) {
return i;
}
current = current.next;
}
return -1;
}
// 删除某以位置的元素
removeAt(position) {
if (position < 0 || position > this.length - 1) return null;
let current = this.head;
if (position === 0) {
if (this.length === 1){
this.head = null;
this.tail = null;
}else{
// 内存回收机制,当并没有使用这个元素的时候,内存会自动回收
this.head = this.head.next;
this.head.previous = null;
}
} else if (position === this.length - 1) {
let new_tail = this.tail.previous;
new_tail.next = null;
this.tail.previous = null;
this.tail = new_tail;
} else {
let previous = null;
for (let i = 0; i < position; i++) {
previous = current;
current = current.next;
}
previous.next = current.next;
current.next.previous = current.previous;
}
this.length--;
}
// update 修改某一位置的元素
update(position, value) {
this.removeAt(position);
this.insert(position, value);
}
// 删除摸一个元素,根据值来删除
remove(value) {
const index = this.indexOf(value);
if (index === -1) return null;
this.removeAt(index);
this.length--;
}
isEmpty() {
return this.length === 0;
}
size() {
return this.length;
}
}
8、哈希表的实现
class HashTable {
static MAX_LOAD_FACTOR = 0.75;
static MIN_LOAD_FACTORY = 0.25;
constructor() {
this.storage = [];
this.count = 0;
// 总共可以放元素的个数
this.limit = 7;
}
// 计算hash位置的哈希函数
hashFunc(string, max) {
let hash_code = 0;
for (let i = 0; i < string.length; i++) {
// 对字符进行hash编码
// string.charCodeAt(i) 获取指定字符的编码
hash_code = 31 * hash_code + string.charCodeAt(i);
}
// 将计算出来的结果映射到下标值
return hash_code % max;
/*
2*n*n*n + 5*n*n + n; 这是一个哈希函数,但是可以简化为以下的类型
((2*n+5)*n+1)+6; 这是一个简化了的哈希函数
上文中的31是一个质数,因为质数的计算效果会更好一些
这里的2,5,6相当于上文中的字符编码
所有的可变数值尽可能都写作质数,效率会更高一些
*/
}
// 放入一个元素
// key 表示一个键,value表示的是一个值
put(key, value) {
// 将对应的key计算成相应的坐标index,如果获取的index为值
// 然后使用链地址法来添加,每一个链可以是数组或者是链表
// 在这里直接使用js里面的数组
const index = this.hashFunc(key, this.limit);
let bucket = this.storage[index];
if (bucket === undefined) {
bucket = [];
this.storage[index] = bucket;
}
// 如果hash表里面的键是重复的,那么值将会覆盖原来的值
// 所以键是不能重复的
// 这里每一个bucket里面都是一个数组,或者元组 [key, value]
// 在这里判断是插入还是修改操作
let is_over_ride = false;
for (let i = 0; i < bucket.length; i++) {
let tuple = bucket[i];
if (tuple[0] === key) {
tuple[1] = value;
is_over_ride = true;
}
}
// 如果没有覆盖,那么就是新增加
if (!is_over_ride) {
bucket.push([key, value]);
// 表示在这里添加了一个数据
this.count++;
// 判断比例因子,总数量 / 限制的数量 < 0.75 最为合适
// 当超出这一个比例的话会实现hash_table的自动扩容
if (this.count > this.limit * HashTable.MAX_LOAD_FACTOR) {
// 确定扩容质数的大致范围
let new_limit = this.limit * 2;
// 通过大致的范围来获取一个质数
new_limit = this.getPrime(new_limit);
// 通过获取的质数来确定扩容的大小
this.resize(new_limit);
}
}
}
// 获取一个值
get(key) {
const index = this.hashFunc(key, this.limit);
// 判断是否存在这一个桶子(因为采用的是连地址法)
if (this.storage[index] === undefined) return null;
const bucket = this.storage[index];
// 遍历桶子来实现查找
for (let i = 0; i < bucket.length; i++) {
if (bucket[i][0] === key) {
return bucket[i][1];
}
}
return null;
}
// 删除一个元素的方法
remove(key) {
const index = this.hashFunc(key, this.limit);
// 判断是否存在这一个桶子(因为采用的是连地址法)
if (this.storage[index] === undefined) return null;
const bucket = this.storage[index];
for (let i = 0; i < bucket.length; i++) {
let tuple = bucket[i];
if (tuple[0] === key) {
// 删除某一位置的元素, 可以通过splice方法
bucket.splice(i, 1);
this.count--;
// 判断比例因子,总数量 / 限制的数量 < 0.75 最为合适
// 当缩小这一个比例的话会实现hash_table的自动容器减小
if (this.count < this.limit * HashTable.MIN_LOAD_FACTORY && this.limit > 8) {
let new_limit = Math.floor(this.limit / 2);
this.resize(this.getPrime(new_limit));
}
return tuple[1];
}
}
return null;
}
isEmpty() {
return this.count === 0;
}
size() {
return this.count;
}
// 扩容函数的实现
// 先保存旧的数组中的内容
resize(new_limit) {
let old_storage = this.storage;
this.limit = new_limit;
// 这个是重新赋值,并不是修改原来的元素
// 这一个浅拷贝拷贝的是数组内部元素的地址,但是直接进行 [] 操作是重新开辟控件
// 指向新的地址,换一句话说就是 = 拷贝拷贝的是元素内部的地址,而不是被拷贝元素本身的地址
// 在这里可以看作 = 创建了一个新的变量,但是只是内部元素指向的地址和原来的变量是一样的
this.storage = [];
// 取出元素,重新计算
// foreach 里面的 continue 和 break 是无效的
old_storage.forEach(bucket => {
if (bucket === null) return;
for (let i = 0; i < bucket.length; i++) {
let tuple = bucket[i];
this.put(tuple[0], tuple[1]);
}
})
}
// 判断这个数是否为一个质数
isPrime(number) {
// 在这里开一个根号
let temp = Math.ceil(Math.sqrt(number));
for (let i = 2; i < temp; i++) {
if (number % 2 === 0) {
return false;
}
}
return true;
}
// 获取一个质数, 将传入的值换成一个质数
getPrime(number) {
while (!this.isPrime(number)) {
number++;
}
return number;
}
}
9、判断一个数是否为质数
function isPrime1(number) {
for (let i = 2; i < number; i++) {
if (number % 2 === 0) {
return false;
}
}
return true;
}
function isPrime2(number) {
// 在这里开一个根号
let temp = Math.ceil(Math.sqrt(number));
for (let i = 2; i < temp; i++) {
if (number % 2 === 0) {
return false;
}
}
return true;
}
10、二叉搜索树的封装
class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(data) {
let node = new Node(data);
if (this.root === null) {
this.root = node;
} else {
this.__insert_node(this.root, node);
}
}
__insert_node(node, new_node) {
if (new_node.data > node.data) {
if (node.right === null) {
node.right = node;
} else {
this.__insert_node(node.right, new_node);
}
} else if (new_node.data < node.data) {
if (node.left === null) {
node.left = node;
} else {
this.__insert_node(node.left, new_node);
}
} else {
new_node.data = node.data;
}
}
// 先序遍历
preOrderTraverse() {
this.__preOrderTraverseNode(this.root);
}
__preOrderTraverseNode(node) {
if (node === null) return;
console.log(node.data);
// 先遍历左节点,当左节点访问完毕之后访问有右节点
this.__preOrderTraverseNode(node.left);
this.__preOrderTraverseNode(node.right);
}
// 中序遍历
midOrderTraverse() {
this.__midOrderTraverse(this.root);
}
__midOrderTraverse(node) {
if (node === null) return;
this.__midOrderTraverse(node.left);
console.log(node.data);
this.__midOrderTraverse(node.right);
}
// 中序遍历
backOrderTraverse() {
this.__backOrderTraverse(this.root);
}
__backOrderTraverse(node) {
if (node === null) return;
this.__backOrderTraverse(node.left);
this.__backOrderTraverse(node.right);
console.log(node.data);
}
// 获取树中的最大值或者最小值
// 左边的节点一定比右边的节点小,所以最左边的值最小,最右边的值最大
min() {
if (!this.root) return null;
let node = this.root;
while (!node.left) {
node = node.left;
}
return node.data;
}
max() {
if (!this.root) return null;
let node = this.root;
while (!node.right) {
node = node.right;
}
return node.data;
}
// 二叉树搜索特定的值, 判断某一个值是否存在二叉树中
search(data) {
return this.__searchNode(this.root, data);
}
__searchNode(node, data) {
// 如果没有搜索到值的话的返回false
if (node === null) return false;
if (data < node.data) {
return this.__searchNode(node.left, data);
} else if (node > node.data) {
return this.__searchNode(node.right, data);
} else {
return true;
}
}
// 通过while循环来实现值的搜索,判断是否存在该值
search_with_while(data) {
let node = this.root;
while (data !== node.data) {
if (data < node.data) {
node = node.left;
} else {
node = node.right;
}
if (node === null) return false;
}
return true;
}
// 二叉树的删除操作
remove(data) {
let current_node = this.root;
let parent_node = null;
let is_left_child = true;
// 开始查早要删除的节点
while (current_node.data !== data) {
parent_node = current_node;
if (current_node.data > data) {
is_left_child = true;
current_node = current_node.left;
} else {
is_left_child = false;
current_node = current_node.right;
}
if (current_node === null) return false;
}
// 如果代码走到了这里表示找到了要删除的节点
// 情况1:删除的节点是一个叶子节点
if (current_node.left === null && current_node.right === null) {
// 如果当前节点就是一个根节点
if (current_node === this.root) {
this.root = null;
} else {
// 如果不是一个跟节点的话,将父亲节点相应的位置置空
if (is_left_child) {
parent_node.left = null;
} else {
parent_node.right = null;
}
}
}
// 情况2:如果父亲节点只有一个子节点的话,直接删除父节点
else if ((current_node.left === null && current_node.right !== null) ||
(current_node.right === null && current_node.left !== null)) {
// 如果删除的节点就是跟节点
if (current_node === this.root && is_left_child) {
this.root = current_node.left;
} else {
this.root = current_node.right;
}
// 如果当前的节点就是左边的节点
if (is_left_child) {
parent_node.left = current_node.left
} else {
parent_node.right = current_node.right;
}
}
// 情况3:如果既有左节点又有右边的节点, 这个是最复杂的一种情况
// 删除的节点可以用左子树最右边,或者右子树的最左边的节点代替
// 通过这一种方法找到的节点就是最接近被删除的节点的值
// 或者说找到的是左子树的最大值或者右子树的最小值
// 这里有两个专业术语:前驱和后继
// 所以前驱比当前的值小,在左子树,后继在右子树,比当前的节点的值还要大
// 在这里通过通过后继来删除节点
else {
// 获取后继节点,此时已经更新了右边节点的指向,不需要再次跟新
let successor = this.__getSuccessor(current_node);
// 跟新被替换节点的左节点
successor.left = current_node.left;
// 判断是否为跟节点
if (this.root === current_node) {
this.root = successor;
}
// 将被替换的节点连接到当前节点的父亲节点
if (is_left_child) {
parent_node.left = successor;
} else {
parent_node.right = successor;
}
}
return true;
}
// 找到一个节点的后继, 后继一般出现在右节点;找右边最小的值
__getSuccessor(delNode) {
// 定义一个变量来存储临时的一个节点, 这个节点表示找到的节点的父亲节点
let successor_parent = null;
// successor 表示找到的最左边的节点
let successor = delNode;
// current 表示找到的节点的下一个节点
let current = delNode.right;
while (current !== null) {
successor_parent = successor;
successor = current;
current = current.left;
}
// 如果后继节点不是删除节点的右节点
if (successor !== delNode.right) {
// 如果此时的后继节点找到以后,如果这时的successor的节点还有右边的节点的话
if (successor.right !== null) {
// 将当前节点的右节点赋值给当期节点的父亲节点的左节点
successor_parent.left = successor.right;
// 更新右节点,通过找到的节点, 更新原来节点指向的右节点
successor.right = delNode.right;
}
}
return successor;
}
}
11、红黑树以及平衡原理
12、 图结构封装
class Queue{
constructor() {
this.items = [];
}
enqueue(item){
this.items.push(item);
}
dequeue(){
return this.items.shift();
}
front(){
if(this.isEmpty()) return null;
return this.items[0];
}
isEmpty(){
return this.items.length === 0;
}
size(){
return this.items.length;
}
}
class Graph{
// 属性 : 顶点(数组)/ 边(字典)
// 方法
constructor() {
this.vertixes = [];
this.edges = new Map();
}
// 添加顶点的方法
addVertex(v){
this.vertixes.push(v);
this.edges.set(v, []);
}
// 添加边的方法
addEdge(v1, v2){
// 表示的是 v1 到 v2 之间的一个边, 或者说是一个有向线段, 或者说就是一个向量
this.edges.get(v1).push(v2);
// 这里添加一个双向的向量,也就是表示的是一个无向图
// 如果是一个有向图,那么只需要添加一条线段即可
this.edges.get(v2).push(v1);
}
// 重写toString方法,来改变console.log对的默认的行为
toString(){
let string = '';
// 遍历所有的顶点以及顶点对应的所有的边
for (let v of this.vertixes){
string += v;
string += ' --> ';
// 遍历顶点对应的边
for (let edge of this.edges.get(v)){
string += ` ${edge}`;
}
string += '\n';
}
return string;
}
// 初始化状态颜色, 将所有的颜射初始为白色,标记,表示未探索,
initialize(){
let colors = new Map();
for (let i = 0; i < this.vertixes.length; i++){
colors.set(this.vertixes[i], 'white');
}
return colors;
}
// 广度优先搜索
bfs(init_v, handler){
// 初始化颜色
let colors = this.initialize();
// 创建一个队列
let queue = new Queue();
queue.enqueue(init_v);
while (!queue.isEmpty()){
let v = queue.dequeue();
let other_v = this.edges.get(v);
colors.set(v, 'grey');
for (let v_ of other_v){
if (colors.get(v_) === 'white'){
colors.set(v_, 'grey');
queue.enqueue(v_);
}
}
}
// 访问顶点
handler(init_v);
// 将顶点设置未黑色
colors.set(init_v, 'black');
}
// 深度优先搜索,本质上是递归函数
dfs(init_v, handler){
let colors = this.initialize();
this.__dfs(init_v, colors, handler);
}
__dfs(v, colors, handler){
colors[v] = 'grey';
// 处理 v 顶点
handler(v);
// 访问v相连的其他顶点
let v_list = this.edges.get(v);
for (let i = 0; i < v_list.length; i++){
let e = v_list[i];
if (colors[e] === 'white'){
this.__dfs(e, colors, handler);
}
}
}
}
13、冒泡排序
// 冒泡排序
function bubbleSort(array){
for (let j = array.length - 1; j > 1; j--){
let is_changed = false;
for (let i = 0; i < j; i++){
if (array[i] > array[i + 1]){
let temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
is_changed = true;
}
}
if (!is_changed) return array;
}
return array;
}
14、选择排序
// 每次移动指针向后,找出最小元素的坐标
function select_sort(array) {
for (let i = 0; i < array.length - 1; i++) {
let min_index = i;
for (let j = i + 1; j < array.length; j++) {
if (array[j] < array[min_index]) {
min_index = j;
}
}
if (min_index !== i) {
let temp = array[i];
array[i] = array[min_index];
array[min_index] = temp;
}
}
return array;
}
arr = [1, 5, 4, 6, 12, 7];
console.log(select_sort(arr));
15、插入排序
// 分为插入区和被插入区
// 这时候遍历的是插入区
// 移动的是插入区的元素
//
// 在遍历区中,如果当前的值比该区中的最后一个值大,
// 那么就将最后的值一直向右移动(在这里用赋值来表示),
// 直到找到后,将当前的值赋值给比前面的值大同时又比后面的值小的位置
function insert_sort(array){
for (let i = 1; i < array.length; i++){
// 暂时保存当前位置的值
let temp = array[i];
// 获得前一个值的指针
let pre_pointer = i - 1;
// 一直循环,直到找到合适的插入位置
while (pre_pointer >= 0 && array[pre_pointer] > temp){
// 将数组中的前指针的值向后复制一位
array[pre_pointer + 1] = array[pre_pointer];
// 将前指针向前移动一位
pre_pointer--;
}
// 当退出循环时,说明当前的值是比temp储存的值小的,所以后一位一定是有重复的
// 将temp赋值给后一位
array[pre_pointer + 1] = temp;
}
return array;
}
16、快速排序
// 找出一个元素p,
// 使左边的都比p小,
// 右边的都比p大
//
// 1、将列表切分
// 2、将切分后的列表进行排序,使上述条件成立
function find_middle_position(array, left_pointer, right_pointer){
// 初始化当前的值
let temp = array[left_pointer];
// 遍历左右指针
while (left_pointer < right_pointer){
// 当左指针的值小于时,右指针一直向左移动
while (temp <= array[right_pointer] && left_pointer < right_pointer){
right_pointer--;
}
// 将左指针的值大于右边时,将右边的值赋值给左边
array[left_pointer] = array[right_pointer]
// 当temp的值大于左指针的值时,左指针一直向左移动
while (array[left_pointer] <= temp && left_pointer < right_pointer){
left_pointer++;
}
array[right_pointer] = array[left_pointer];
}
// 找到中间的值的指针
array[left_pointer] = temp;
return array;
}
function quick_sort(array, left_pointer, right_pointer){
if (left_pointer < right_pointer){
let middle = find_middle_position(array, left_pointer, right_pointer);
quick_sort(array, left_pointer, middle-1);
quick_sort(array, middle+1, right_pointer);
}
}
// 猜测python中和js中函数传入的参数是以引用的方式传入的
// 而不是简单的拷贝
ls = [4, 2, 6, 3, 5, 0, 8, 7, 7]
quick_sort(ls, 0, ls.length - 1)
console.log(ls)
17、栈解迷宫(深度优先dfs)
let maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
let directions = [
(x, y) => [x + 1, y],
(x, y) => [x - 1, y],
(x, y) => [x, y - 1],
(x, y) => [x, y + 1]
]
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1]
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
function maze_path(start_x, start_y, final_x, final_y){
let stack = new Stack();
stack.push([start_x, start_y]);
let find_path = false;
while (!stack.isEmpty()){
let going = false;
let current_node = stack.peek();
// 当走到终点时退出循环
if (current_node[0] === final_x && current_node[1] === final_y){
find_path = true;
for (let path of stack.items){
console.log(path);
}
}
// 上下左右四个坐标, 上下左右, x-1, x+1, y-1, y+1
for (let direction of directions){
let next_node = direction(current_node[0], current_node[1])
// 如果下一个节点可以走
if (maze[next_node[0]][next_node[1]] === 0){
stack.push(next_node);
// 将当前的位置标记为已经走过
maze[next_node[0]][next_node[1]] = -1;
going = true;
break;
}
}
// 当四个方向都遍了没法走时
if (!going){
maze[current_node[0]][current_node[1]] = -1;
stack.pop();
}
}
if (!find_path){
console.log('找完了,没有路');
}
}
maze_path(1, 1, 8, 8);
18、队列解迷宫(广度优先bfs)
class Queue{
constructor() {
this.items = [];
}
enqueue(item){
this.items.push(item);
}
dequeue(){
return this.items.shift();
}
front(){
if(this.isEmpty()) return null;
return this.items[0];
}
isEmpty(){
return this.items.length === 0;
}
size(){
return this.items.length;
}
}
maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
let directions = [
(x, y) => [x + 1, y],
(x, y) => [x - 1, y],
(x, y) => [x, y - 1],
(x, y) => [x, y + 1]
]
function print(path){
let real_path = [];
let current_node = path[path.length - 1];
while (current_node[2] !== -1){
real_path.push(current_node.slice(0, 2));
current_node = path[current_node[current_node.length - 1]];
}
real_path.push(current_node.slice(0, 2));
real_path.reverse();
for (let path of real_path){
console.log(real_path);
}
}
function maze_path(start_x, start_y, final_x, final_y){
let queue = new Queue();
queue.enqueue([start_x, start_y, -1]);
let path = [];
while (!queue.isEmpty()){
let current_node = queue.dequeue();
path.push(current_node);
// 判断是否到达终点
if (current_node[0] === final_x && current_node[1] === final_y){
print(path);
return true;
}
for (let direction of directions){
let next_node = direction(current_node[0], current_node[1]);
if (maze[next_node[0]][next_node[1]] === 0){
// 这里的 (len(path) - 1) 相当于一个指针,指向的是上一个路径path
queue.enqueue([...next_node, path.length - 1]);
// 标记已经走过
maze[next_node[0]][next_node[1]] = -1;
}
}
}
console.log('没有找到路径');
return false;
}