回味javascript数据结构与算法(一)连载

287 阅读4分钟

一、栈数据结构

1、什么是栈数据结构 栈是一种遵循从后进先出原则的有序集合,新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。 2、实现一个基于数组的栈


/**
 * 创建一个javascript数据结构和算法库
 * 栈数据结构
 * 向栈中添加元素
 * 从栈中移除元素
 * 如何使用stack类
 * 
 */

// 栈是是一种遵从先进后出原则的有序集合

class Stack {
    constructor() {
        this.items = [] // {1}
    }
    get() {
     return this.items   
    }
    // 实现向栈顶添加一个或多个元素
    push(ele) {
        this.items.push(ele)
    }
    // 移除栈顶的元素,同时返回被移除的元素
    pop() {
        return this.items.pop()
    }
    // 返回栈顶的元素,不对栈做任何修改
    peek() {
        // 也就是返回栈最后添加的元素
        return this.items[this.items.length - 1]
     }
    // 如果栈里没有任何元素就返回true,否则就返回false
    isEmpty() { 
        return this.items.length === 0
     }
    // 移除栈里的所有元素
    clear() {
        this.items = []
    }
    // 返回栈里元素个数。该方法和数组的length属性很类似
    size() {
       return this.items.length
    }
}

// 上面栈的功能已经实现,现在开始测试
// 1、实例化 栈
const stact = new Stack()
stact.push(1)
stact.push(2)
stact.push(3)
stact.push(4)
stact.push(5)

// console.log( 'pop',stact.pop()) //1
console.log( 'peek',stact.peek()) //1
console.log( 'get',stact.get()) //1
console.log( 'size',stact.size())
console.log( 'isEmpty',stact.isEmpty())

3、实现一个基于javascript对象的Stack类

/**
 * 创建一个javascript数据结构和算法库
 * 栈数据结构
 * 向栈中添加元素
 * 从栈中移除元素
 * 如何使用stack类
 * 
 */

// 栈是是一种遵从先进后出原则的有序集合

class Stack {
    constructor() {
        this.count = 0 // 记录栈的大小
        this.items = {} // {1}
    }
    get() {
     return this.items   
    }
     // 返回栈里元素个数。该方法和数组的length属性很类似
    size() {
        return this.count
     }
    // 向栈中插入元素
    push(ele) {
        this.items[this.count] = ele
        this.count++
    }
    // 移除栈中的元素 ,从栈中弹出元素,也就是移除栈顶的元素
    pop() {
        if (this.isEmpty()) {
            // 栈为空的情况
            return undefined
        }
        this.count--
        const result = this.items[this.count]; //先保存栈顶的值 以便在删除之前保存下来并在删除之后返回删除的元素
        delete this.items[this.count];
        return result
    }
    // 返回栈顶的元素,不对栈做任何修改
    peek() {
        if (this.isEmpty()) {
            // 栈为空的情况
            return undefined
        }
        // 因为count 是从
        return this.items[this.count - 1]
        // 也就是返回栈最后添加的元素

     }
    // 如果栈里没有任何元素就返回true,否则就返回false
    isEmpty() { 
        return this.count === 0
     }
    // 移除栈里的所有元素
    clear() {
        this.items = {}
        this.count = 0
    }
    // 实现tostring()功能
    toString() {
        if (this.isEmpty()) {
            // 栈为空的情况
            return ''
        }
        let objstring = `${this.items[0]}`
        for (let i = 1; i < this.count; i++) {
            objstring = `${objstring},${this.items[i]}`
        }
        return objstring

    }
   
}

// 上面栈的功能已经实现,现在开始测试
// 1、实例化 栈
const stact = new Stack()
stact.push(1)
stact.push(2)
stact.push(3)
stact.push(4)
stact.push(5)
console.log( 'get',stact.get()) //1
// console.log('pop', stact.pop()) //1
// console.log( 'get',stact.get()) //1
console.log('peek', stact.peek()) //1
console.log('toString', stact.toString()) //1


// console.log( 'size',stact.size())
// console.log( 'isEmpty',stact.isEmpty())

二、队列数据结构

1、队列的定义

队列是遵循先进后厨的原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。

例如:电影厅、自助餐厅、杂货店收银台。

2、实现一个队列

/**
 * 本文件实现目标
 * 队列数据结构
 * 双端队列数据结构
 * 向队列和双端队列增加元素
 * 从队列和双端队列增加元素
 * 用击鼓传花游戏模拟循环队列
 * 用双端队列检查一个词组是否构成回文
 * 
 */
// 队列遵循先进先出原则的一组有序的项

// 创建队列
class Queue {
    constructor() {
        this.count = 0;   // 用来储存队列的大小 
        this.lowestCount = 0; // 追踪第一个元素
        this.items = {}  //存储队列
    }
    get() {
        return this.items
    }
    // 声明一些队列可用的方法
    //向队列尾部添加一个或多个项
    enqueue(ele) {
        this.items[this.count] = ele
        this.count++
    }
    // 移除队列的第一项(即排在队列最前面的项)并返回被移除的元素
    dequeue() {
        if (this.isEmpty()) {
            return undefined
        }
        const result = this.items[this.lowestCount]; //保存第一个元素
        delete this.items[this.lowestCount]; // 删除第一个元素
        this.lowestCount++;
        // this.count--;
        return result
    }
    // 查看队列头部的元素
    peek() {
        if (this.isEmpty()) {
            return undefined
        }
        return this.items[this.lowestCount]
    }
    // 如果队列中不包含任何元素,返回true,否则返回false
    isEmpty() {
        return this.size() === 0
    }
    // 返回队列包含的元素个数
    size() {
        return  this.count - this.lowestCount
    }
    // 清空队列
    clear() {
        this.items = {};
        this.count = 0;
        this.lowestCount = 0
    }
    // 实现tostring方法
    toString() {
        if (this.isEmpty()) {
            return ''
        }
        let objString = `${this.items[this.lowestCount]}`
        for (let i = this.lowestCount + 1; i < this.count; i++) {
            objString = `${objString},${this.items[i]}`
        }
        return objString
    }
}

const queue = new Queue()
queue.enqueue('第一')
queue.enqueue('第2')
queue.enqueue('第3')
queue.enqueue('第4')
console.log('查看队列头部的信息',queue.peek())
console.log('查看队列的大小',queue.size())
console.log('判断是否为空',queue.isEmpty())
console.log('移除堆列信息', queue.dequeue())
console.log('查看队列头部的信息',queue.peek())
console.log('格式化toString',queue.toString())
console.log('查看队列的大小', queue.size())
console.log('移除堆列信息', queue.dequeue())
console.log('移除堆列信息', queue.dequeue())
console.log('移除堆列信息', queue.dequeue())
console.log('查看队列的大小', queue.size())
module.exports = Queue

3、双端队列数据结构

双端队列是一种允许我们同时从前端和后端添加和一簇元素的特殊队列。

双端队列在显示生活中的例子有电影院、餐厅种排队的队伍等。举个例子,一个刚买了票的人如果知识还需要再问一些简单的信息,就可以直接回到队伍的头部。另外,再队伍末尾的人如果赶时间,他可以直接离开队伍。

实现一个双端队列

/**
 * 本文件实现目标
 * 队列数据结构
 * 双端队列数据结构
 * 向队列和双端队列增加元素
 * 从队列和双端队列增加元素
 * 用击鼓传花游戏模拟循环队列
 * 用双端队列检查一个词组是否构成回文
 * 
 */
// 队列遵循先进先出原则的一组有序的项

// 创建队列
class Deque {
    constructor() {
        this.count = 0;   // 用来储存队列的大小 
        this.lowestCount = 0; // 追踪第一个元素
        this.items = {}  //存储队列
    }
    get() {
        return this.items
    }
    // 在双端队列前端添加元素
    addFrot(ele) {
        if (this.isEmpty()) {
            // 如果队列为空,则添加第一个元素,调用添加元素到末尾的方法
            this.addBack(ele)
        } else if (this.lowestCount > 0) {
            // 第一个元素被移除过lowestCount !== 0
            this.lowestCount--;
            this.items[this.lowestCount] = ele
        } else {
            // 从后面开始移动所有元素一位
            for (let i = this.count; i > 0; i-- ) {
                this.items[i] = this.items[i - 1];
            }
            this.count++;
            this.lowestCount = 0;
            this.items[0] = ele
        }
    }
    //向队列尾部添加一个或多个项
    addBack(ele) {
        this.items[this.count] = ele
        this.count++
    }
    // 移除队列的第一项(即排在队列最前面的项)并返回被移除的元素
    removeFront() {
        if (this.isEmpty()) {
            return undefined
        }
        const result = this.items[this.lowestCount]; //保存第一个元素
        delete this.items[this.lowestCount]; // 删除第一个元素
        this.lowestCount++;
        // this.count--
        return result
    }
    // 移除队列后面一项
    removeBack() {
        if (this.isEmpty()) {
            // 栈为空的情况
            return undefined
        }
        this.count--
        const result = this.items[this.count]; //先保存栈顶的值 以便在删除之前保存下来并在删除之后返回删除的元素
        delete this.items[this.count];
        return result
    }
    // 查看队列头部的元素
    peek() {
        if (this.isEmpty()) {
            return undefined
        }
        return this.items[this.lowestCount]
    }
    // 如果队列中不包含任何元素,返回true,否则返回false
    isEmpty() {
        return this.count === 0
    }
    // 返回队列包含的元素个数
    size() {
        return  this.count
    }
    // 清空队列
    clear() {
        this.items = {};
        this.count = 0;
        this.lowestCount = 0
    }
    // 实现tostring方法
    toString() {
        if (this.isEmpty()) {
            return ''
        }
        let objString = `${this.items[this.lowestCount]}`
        for (let i = this.lowestCount + 1; i < this.count; i++) {
            objString = `${objString},${this.items[i]}`
        }
        return objString
    }
}

const queue = new Deque()
// queue.addFrot('第一')
// queue.addBack('第2')
// queue.addFrot('第3')
// queue.addBack('第4')
// console.log('查看队列头部的信息',queue.peek())
// console.log('查看队列的大小',queue.size())
// console.log('判断是否为空',queue.isEmpty())
// console.log('移除堆列信息', queue.dequeue())
// console.log('查看队列头部的信息',queue.peek())
// console.log('移除后面一个元素', queue.peek())
// console.log('查看队列的大小',queue.size())
// console.log('格式化toString',queue.removeBack())

module.exports = Deque


三、使用队列解决现实问题

1、实现一个击鼓传花游戏

在这个游戏中,孩子们围城一个圆圈,把花尽快地传递给旁边的人。某一时刻传话停止,这个时候花在谁手里,谁就推出圆圈、结束游戏。重复这个过程,直到只剩一个孩子(胜者)。

    /**
     * 使用queue 模拟击鼓传花游戏
     */

    var Queue = require("./队列");  //这个文件见标题二 实现一个队列代码

    function hotPhtato ( elementList ,num ) {
        const queue = new Queue();
        const elimitatedList = [];//被移除的人
        for (let i = 0; i < elementList.length; i++) {
            queue.enqueue(elementList[i])
        }
        while (queue.size() > 1) {
            for (let i = 0; i < num; i++) {
                // console.log('queue.dequeue()',queue.dequeue())
                queue.enqueue(queue.dequeue()) //把移除的项,从新追加进去
                // console.log('queue.enqueue()',queue.get())
            }
            elimitatedList.push(queue.dequeue());
        }

        return {
            elimitated: elimitatedList,
            winner:queue.dequeue()
        }
    }



    const names = ['关羽', '张飞', '刘备', '黄忠', '诸葛亮']

    const result = hotPhtato(names, 7)
    result.elimitated.forEach(name => {
        console.log(`${name}在击鼓传花游戏中被淘汰`)
    })
    console.log(`胜利者${result.winner}`)

2、回文检查器

回文的解释:回文是正反都能读通的单词、词组、数或 一系列字符的序列例如 madam 或者 racecar

有不同的算法可以检查一个词组或字符串是否为回文。最简单的方式是将字符串反向排列并检查它和源字符串是否相同。如果两者相同,那么它就是一个回文。我们也可以用栈来完成,但是利用数据结构来解决这个问题的最简单方法是使用双端队列。

/**
 * 回文检查器
 * 
 */
 var Deque = require("./双端队列");  // 代码见上文
function palindromeChecker(aString) {
    const deque = new Deque();
    const lowerString = aString.toLocaleLowerCase().split(' ').join('') // 去除空格
    console.log("lowerString", lowerString)
    let isEqual = true;
    let firstChar, lastChar;
    for (let i = 0; i < lowerString.length; i++){
        deque.addBack(lowerString.charAt(i))
    }
    while (deque.size() >1 && isEqual) {
        firstChar = deque.removeFront();
        lastChar = deque.removeBack();
        console.log("firstChar",firstChar)
        console.log("lastChar",lastChar)
        if (firstChar !== lastChar) {
            isEqual = false;
        }
    }
    return isEqual
}
console.log(palindromeChecker('abcdyydcba'))