JavaScript數據結構與算法之 "隊列和雙端隊列"

隊列數據結構

  • 隊列數據結構遵循先進先出原則的一組有序項
  • 隊列在頭部移除數據,在尾部添加數據。最新添加的數據必須排在隊列的最末端
  • 在現實生活中最常見的隊列就是排隊

隊列數據結構的實現

  • 通過對象我們可以實現隊列
  • 隊列包含的方法
    • enqueue(): 向隊列尾部添加一個新的元素
    • dequeue(): 移除隊列的第一個元素,並返回移除的元素
    • peek(): 返回隊列中的第一個元素—最先被添加,也是最先被返回的元素,不對隊列做任何改動
    • isEmpty(): 判斷隊列中是否有元素,沒有返回true,有方法false
    • clear(): 清空隊列
    • size(): 返回隊列中元素的個數
    • toString()
  • 代碼
    /*創建隊列的類*/
    class Queue {
        constructor() {
            this.count = 0; //隊列中元素的計數
            this.lowestCount = 0; // 隊列中的元素的最小計數
            this.items = {};  // 存儲隊列中元素的對象
        }
    
        // enqueue(): 向隊列尾部添加一個新的元素
        enqueue(element) {
            this.items[this.count] = element;
            this.count += 1;
        }
    
        // dequeue(): 移除隊列的第一個元素,並返回移除的元素
        dequeue() {
            if (this.isEmpty()) {
                return undefined;
            }
    
            const result = this.items[this.lowestCount];
            delete this.items[this.lowestCount];
            this.lowestCount += 1;
    
            return result;
        }
    
        // peek(): 返回隊列中的第一個元素---最先被添加,也是最先被返回的元素,不對隊列做任何改動
        peek() {
            if (this.isEmpty()) {
                return undefined;
            }
    
            return this.items[this.lowestCount];
        }
    
        // isEmpty(): 判斷隊列中是否有元素,沒有返回true,有方法false
        isEmpty() {
            return this.count - this.lowestCount === 0;
        }
    
        // clear(): 清空隊列
        clear() {
            this.count = 0;
            this.lowestCount = 0;
            this.items = {};
        }
    
        // size(): 返回隊列中元素的個數
        size() {
            return this.count - this.lowestCount;
        }
    
        // toString()
        toString() {
            if (this.isEmpty()) {
                return '';
            }
    
            let str = `${this.items[this.lowestCount]}`;
            for (let i = this.lowestCount + 1; i < this.count; i += 1) {
                str = `${str},${this.items[i]}`;
            }
    
            return str;
        }
    }
    

雙端隊列數據結構

  • 雙端隊列是一種允許我們同時從前端和後端添加和移除數據的特殊隊列
  • 在計算機科學中,雙端隊列一個常見應用是存儲一系列的撤銷操作
  • 由於雙端隊列同時遵守了先進先出和後進後出的原則,可以所它是把隊列和棧相結合的一種數據結構

雙端隊列數據結構的實現

  • 雙端隊列的方法:
    • addFront(element):該方法在雙端隊列的前端添加一個新的數據
    • addBack(element):該方法在雙端隊列的後端添加一個新的數據
    • removeFront():該方法從雙端隊列的前端移除一個數據
    • removeBack():該方法從雙端隊列的後端移除一個數據
    • peekFront():該方法返回雙端隊列的第一個元素,不對隊列做任何修改
    • peekBack():該方法返回雙端隊列的最後一個元素,不對隊列做任何修改
    • isEmpty(): 判斷隊列中是否有元素,沒有返回true,有方法false
    • clear(): 清空隊列
    • size(): 返回隊列中元素的個數
    • toString()
  • 代碼
    /*創建雙端隊列的類*/
    class Deque {
        constructor() {
            this.count = 0; //雙端隊列的技術器
            this.lowestCount = 0; //雙端隊列的最小計數器
            this.items = {};  // 存儲雙端隊列數據的對象
        }
    
        //  addFront(element):該方法在雙端隊列的前端添加一個新的數據
        addFront(element) {
            if (this.isEmpty()) {
                this.addBack(element);
            } else if (this.lowestCount > 0) {
                this.lowestCount -= 1;
                this.items[this.lowestCount] = element;
            } else {
                for (let i = this.count; i > 0; i -= 1) {
                    this.items[i] = this.items[i - 1];
                }
    
                this.lowestCount = 0;
                this.count += 1;
                this.items[0] = element;
            }
        }
    
        //  addBack(element):該方法在雙端隊列的後端添加一個新的數據
        addBack(element) {
            this.items[this.count] = element;
            this.count += 1;
        }
    
        //  removeFront():該方法從雙端隊列的前端移除一個數據
        removeFront() {
            if (this.isEmpty()) {
                return undefined;
            }
    
            const result = this.items[this.lowestCount];
            delete this.items[this.lowestCount];
            this.lowestCount += 1;
            return result;
        }
    
        //  removeBack():該方法從雙端隊列的後端移除一個數據
        removeBack() {
            if (this.isEmpty()) {
                return undefined;
            }
    
            this.count -= 1;
            const result = this.items[this.count];
            delete this.items[this.count];
    
            return result;
        }
    
        //  peekFront():該方法返回雙端隊列的第一個元素,不對隊列做任何修改
        peekFront() {
            if (this.isEmpty()) {
                return undefined;
            }
            return this.items[this.lowestCount];
        }
    
        //  peekBack():該方法返回雙端隊列的最後一個元素,不對隊列做任何修改
        peekBack() {
            if (this.isEmpty()) {
                return undefined;
            }
            return this.items[this.count - 1];
        }
    
        //  isEmpty(): 判斷隊列中是否有元素,沒有返回true,有方法false
        isEmpty() {
            return this.count - this.lowestCount === 0;
        }
    
        //  clear(): 清空隊列
        clear() {
            this.count = 0;
            this.lowestCount = 0;
            this.items = {};
        }
    
        //  size(): 返回隊列中元素的個數
        size() {
            return this.count - this.lowestCount;
        }
    
        //  toString()
        toString() {
            if (this.isEmpty()) {
                return '';
            }
    
            let str = `${this.items[this.lowestCount]}`;
            for (let i = this.lowestCount + 1; i < this.count; i += 1) {
                str = `${str},${this.items[i]}`;
            }
    
            return str;
        }
    
    }
    

隊列和雙端隊列的使用

擊鼓傳花遊戲

  • 我們使用隊列來解決一個名爲擊鼓傳花的遊戲。在這個遊戲中孩子們圍成一圈
  • 代碼
    /*擊鼓傳花遊戲*/
    const hotPotato = (elementList, num) => {
        const queue = new Queue();
        let eliminatedList = [];  // 淘汰人員的名單
    
        // 將elementList 存入隊列中
        elementList.forEach((element) => queue.enqueue(element));
    
        // 如果隊列的size大於1,就一致進行遊戲
        while (queue.size() > 1) {
            for (let i = 0; i < num; i += 1) {
                queue.enqueue(queue.dequeue());
            }
    
            eliminatedList.push(queue.dequeue());
        }
    
        return {
            eliminated: eliminatedList,
            winner: queue.dequeue(),  //勝利者
        };
    };
    
    /*使用下面的代碼來嘗試hotPotato算法*/
    const names = ['John', 'Tom', 'Jack', 'Curry', 'Bil', 'Camila', 'Jam'];
    const result = hotPotato(names, 5);
    
    result.eliminated.forEach((name) => {
        console.log(`${name}在遊戲中被淘汰`);
    })
    
    console.log(`勝利者:${result.winner}`);
    
    /*結果*/
    // Camila在遊戲中被淘汰
    // Bil在遊戲中被淘汰
    // Jam在遊戲中被淘汰
    // Tom在遊戲中被淘汰
    // John在遊戲中被淘汰
    // Curry在遊戲中被淘汰
    // 勝利者:Jack
    
    

迴文檢查器

  • 迴文是正反都能讀通的單詞、詞組、數或一系列的序文,例如:madam或 “你好你”
  • 可以使用雙端隊列來編寫一個檢測迴文的算法
  • 代碼:
    /*迴文檢測*/
    const palindromeCheck= (str) => {
        if (str === null || str === undefined || str.length === 0) {
            return false;
        }
    
        const deque = new Deque();  // 存儲迴文字符的雙端隊列
        const lowerStr = str.toLowerCase().split(' ').join('');  //去除空格,並轉換爲小寫
        let isEqual = true;
        let firstChar, lastChar;
    
        while (deque.size() > 1 && isEqual) {
            firstChar = deque.removeFront();
            lastChar = deque.removeBack();
    
            if (firstChar !== lastChar) {
                isEqual = false;
            }
        }
    
        return isEqual;
    }
    
    /*測試*/
    console.log('a', palindromeCheck('a'));
    console.log('madam', palindromeCheck('madam'));
    console.log('你好你', palindromeCheck('你好你'));
    console.log('Was it a car or a cat I saw', palindromeCheck('Was it a car or a cat I saw'));
    
    /*結果*/
    // a true
    // madam true
    // 你好你 true
    // Was it a car or a cat I saw true
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章