Js-数据结构与算法(非完整)

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值