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>

在这里插入图片描述


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>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值