JavaScript数据结构与算法 - 队列

1. 队列数据结构

  • 队列是遵循先进先出(FIFO,先来先服务)原则的一组有序的项
  • 队列在尾部添加新元素,从顶部移除元素

1. 创建队列

        class Queue {
     
            constructor() {
     
                this.count = 0; // 控制队列大小
                this.lowestCount = 0; // 追踪第一个元素
                this.items = {
     }; // 使用对象来存储元素
            }
            // 方法
        }

声明方法:

  • enqueue(element(s)):向队列尾部添加一个(或多个)新的项
  • dequeue():移除队列的第一项并返回被移除的元素
  • peek():返回队列中的第一个元素,队列将不做任何变动,只返回元素信息
  • isEmpty():如果队列中不包含任何元素,返回true,否则返回false
  • size():返回队列包含的元素个数

1.1 向队列添加元素

新的项只能添加到队列末尾。

enqueue(element){
     
	this.items[this.count] = element;
	this.count++;
}

1.2 从队列移除元素

最先移除的项是最先添加的。

dequeue() {
     
	if(this.isEmpty()) {
     
		return undefined;
	}
	// 暂存队列头部的值
	const result = this.items[this.lowestCount];
	delete this.items[this.lowestCount];
	this.lowestCount++;
	return result;
}

1.3 查看队列头元素

返回队列最前面的项。

peek() {
     
	if(this.isEmpty) {
     
		return undefined;
	}
	return this.items[this.lowestCount];
}

1.4 检查队列是否为空并获取长度

如果队列为空,返回true,否则返回false。

isEmpty() {
     
	return this.count - this.lowestCount === 0;
}

计算队列中有多少元素,只需要返回count和lowestCount的差值。

size() {
     
	return this.count - this.lowestCount;
}

如果差值为0,则队列为空,isEmpty可以用size方法实现:

isEmpty() {
     
	return this.size() === 0;
}

1.5 清空队列

可以简单地将队列中地属性值重设为和构造函数中的一样。

clear() {
     
	this.items = {
     };
	this.count = 0;
	this.lowestCount = 0;
}

1.6 创建toString方法

toString() {
     
	if(this.isEmpty()) {
     
		return ''
	}
	let objString = `${
       this.items[this.lowestCount]}`;
	// Queue类中第一个索引值不一定是0,需要从lowestCount的位置开始迭代队列
	for(let i = this.lowestCount + 1; i < this.count; i++) {
     
		objString = `${
       objString}, ${
       this.items[i]}`;
	}
	return objString;
}

2. 使用Queue类

        const queue = new Queue();
        console.log(queue.isEmpty()); // true
        // 添加元素
        queue.enqueue('aaa');
        queue.enqueue('bbb');
        console.log(queue.toString()); // aaa,bbb
        queue.enqueue('ccc');
        console.log(queue.toString()); // aaa,bbb,ccc
        console.log(queue.size()); // 3
        console.log(queue.isEmpty()); // false
        queue.dequeue(); // 移除aaa
        console.log(queue.toString()); // bbb,ccc

2. 双端队列数据结构

双端队列(deque)允许同时从前端和后端添加和移除元素。同时遵循了先进先出和后进先出的原则,是把栈和队列组合的一种数据结构。

常见应用:撤销操作。用户在软件中进行一个操作,该操作会被存在一个双端队列中,点击撤销时,将从后面被移除。

2.1 创建Deque类

class Deque {
     
	constructor() {
     
		this.count = 0;
		this.lowestCount = 0;
		this.items = {
     };
	}
}

双端队列是一种特殊的队列,其构造函数中的部分代码和队列相同,包括相同的内部属性和以下方法:isEmpty、clear、size、toString。

双端队列允许在两端添加和移除元素,所有还有以下方法:

  • addFront(element):在双端队列前端添加新的元素
  • addBack(element):在双端队列后端添加新元素(与Queue中的enqueue方法相同)
  • removeFront():从双端队列前端移除第一个元素(与Queue中的dequeue方法相同)
  • removeBack():从双端队列后端移除第一个元素(与Stack中的pop方法相同)
  • peekFront():返回双端队列前端的第一个元素(与Queue中的peek方法相同)
  • peekBack():返回双端队列后端的第一个元素(与Stack中的peek方法相同)

向双端队列的前端添加元素

addFront(element) {
     
	// 双端队列为空的情况下可以直接在队列后端添加
	if(this.isEmpty()) {
     
		this.addBack(element);
	} else if(this.lowestCount) {
     
	// 一个元素已经被从双端队列的前端移除,即lowestCount属性会大于等于1
		this.lowestCount--;
		this.items[this.lowestCount] = element;
	} else {
     
	// lowestCount为0时,可以设置负键
		for(let i = this.count; i > 0; i--) {
     
			this.items[i] = this.items[i - 1];
		}
		this.count++;
		this.lowestCount = 0;
		this.items[0] = element;
	}
}

2.2 使用Deque类

    <script>
        class Queue {
     
            constructor() {
     
                    this.count = 0; // 控制队列大小
                    this.lowestCount = 0; // 追踪第一个元素
                    this.items = {
     }; // 使用对象来存储元素
                }
                // 方法
            enqueue(element) {
     
                this.items[this.count] = element;
                this.count++;
            };

            dequeue() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                const result = this.items[this.lowestCount];
                delete this.items[this.lowestCount];
                this.lowestCount++;
                return result;
            };

            peek() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                return this.items[this.lowestCount];
            };

            isEmpty() {
     
                return this.count - this.lowestCount === 0;
            };

            size() {
     
                return this.count - this.lowestCount;
            };

            clear() {
     
                this.items = {
     };
                this.count = 0;
                this.lowestCount = 0;
            };

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

            addFront(element) {
     
                if (this.isEmpty()) {
     
                    this.enqueue(element);
                } else if (this.lowestCount) {
     
                    this.lowestCount--;
                    this.items[this.lowestCount] = element;
                } else {
     
                    for (let i = this.count; i > 0; i--) {
     
                        this.items[i] = this.items[i - 1];
                    }
                    this.count++;
                    this.lowestCount = 0;
                    this.items[0] = element;
                }
            }
        }

        const queue = new Queue();
        console.log(queue.isEmpty()); // true
        // 添加元素
        queue.enqueue('aaa');
        queue.enqueue('bbb');
        console.log(queue.toString()); // aaa,bbb
        queue.enqueue('ccc');
        console.log(queue.toString()); // aaa,bbb,ccc
        console.log(queue.size()); // 3
        console.log(queue.isEmpty()); // false
        queue.dequeue(); // 移除aaa
        console.log(queue.toString()); // bbb,ccc
        
        queue.addFront('aaa'); // 头插法
        console.log(queue.toString()); // aaa,bbb,ccc
    </script>

3. 循环队列-击鼓传花

    <script>
        class Queue {
     
            constructor() {
     
                    this.count = 0; // 控制队列大小
                    this.lowestCount = 0; // 追踪第一个元素
                    this.items = {
     }; // 使用对象来存储元素
                }
                // 方法
            enqueue(element) {
     
                this.items[this.count] = element;
                this.count++;
            };

            dequeue() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                const result = this.items[this.lowestCount];
                delete this.items[this.lowestCount];
                this.lowestCount++;
                return result;
            };

            peek() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                return this.items[this.lowestCount];
            };

            isEmpty() {
     
                return this.count - this.lowestCount === 0;
            };

            size() {
     
                return this.count - this.lowestCount;
            };

            clear() {
     
                this.items = {
     };
                this.count = 0;
                this.lowestCount = 0;
            };

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

        function hotPotato(elementsList, num) {
     
            const queue = new Queue();
            // 把名单都加入队列
            const eliminatedList = [];

            for (let i = 0; i < elementsList.length; i++) {
     
                queue.enqueue(elementsList[i]);
            }

            while (queue.size() > 1) {
     
                for (let i = 0; i < num; i++) {
     
                    // 从队列开头移除一项,将其添加到队尾
                    queue.enqueue(queue.dequeue());
                }
                // 达到给定的传递次数,拿花者淘汰
                eliminatedList.push(queue.dequeue());
            }

            return {
     
                eliminated: eliminatedList,
                // 剩下的胜者
                winner: queue.dequeue()
            }
        }
        const names = [1, 2, 3, 4, 5];
        const result = hotPotato(names, 7);
        result.eliminated.forEach(name => {
     
            console.log(`${
       name}在击鼓传花中被淘汰。`);
        });
        console.log(`胜利者:${
       result.winner}`);
    </script>

JavaScript数据结构与算法 - 队列_第1张图片


4. 回文检查器

使用双端队列。

    <script>
        class Deque {
     
            constructor() {
     
                this.items = {
     };
                this.count = 0;
                this.lowestCount = 0;
            };
            addFront(element) {
     
                if (this.isEmpty()) {
     
                    this.addBack(element);
                } else if (this.lowestCount > 0) {
     
                    this.lowestCount--;
                    this.items[this.lowestCount] = element;
                } else {
     
                    for (let i = this.count; i > 0; i--) {
     
                        this.items[i] = this.this.items[i - 1];
                    }
                    this.count++;
                    this.lowestCount = 0;
                    this.items[0] = element;
                }
            };
            addBack(element) {
     
                this.items[this.count] = element;
                this.count++;
            };
            removeFront() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                const result = this.items[this.lowestCount];
                delete this.items[this.lowestCount];
                this.lowestCount++;
                return result;
            };
            removeBack() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                this.count--;
                const result = this.items[this.count];
                delete this.items[this.count];
                return result;
            };
            peekFront() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                return this.items[this.lowestCount];
            };
            peekBack() {
     
                if (this.isEmpty()) {
     
                    return undefined;
                }
                return this.items[this.count - 1];
            };
            isEmpty() {
     
                return this.count - this.lowestCount === 0;
            };
            clear() {
     
                this.items = {
     };
                this.count = 0;
                this.lowestCount = 0;
            };
            size() {
     
                return this.count - this.lowestCount;
            };
            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;
            };
        }

        function palindromeChecker(aString) {
     
            // 检查传入的字符串是否合法
            if (aString === undefined || aString === null || (aString !== null && aString.length === 0)) {
     
                return false;
            }
            const deque = new Deque();
            // 把所有字母转为小写,并移除所有空格
            const lowestString = aString.toLocaleLowerCase().split(' ').join('');
            let isEqual = true;
            let firstChar, lastChar;

            // 对字符串中所有字符进行操作
            for (let i = 0; i < lowestString.length; i++) {
     
                deque.addBack(lowestString.charAt(i));
            }

            // 如果只有一个字符,肯定为回文
            while (deque.size() > 1 && isEqual) {
     
                // 从前端移除一个元素
                firstChar = deque.removeFront();
                // 从后端移除一个元素
                lastChar = deque.removeBack();
                // 移除的元素不相同返回错误
                if (firstChar !== lastChar) {
     
                    isEqual = false;
                }
            }

            return isEqual;
        }

        console.log('a', palindromeChecker('a')); // true
        console.log('abc', palindromeChecker('abc')); // false
        console.log('madam', palindromeChecker('madam')); // true
    </script>

你可能感兴趣的:(#,数据结构与算法)