数据结构--双端队列的实现和实际运用

170 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

双端队列数据结构

双端队列是一种允许同时从顶部和尾部添加和移除元素的特殊队列。

在计算机科学中,双端队列的一个常见应用是存储一系列的撤销操作。当用户在软件中进行了一个操作,该操作会被保存在一个双端队列中。当用户撤销时,该操作会从双端队列中弹出,表示从队尾移除了。当保存了一定数量的操作后,还有操作被保存进来,就会把队顶的操作移除。

所以双端队列同时遵守了先进先出和后进先出的原则,可以说它是队列和栈相结合的一种数据结构。

创建 Deque

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

因为双端队列是一种特殊的队列,所以它们有相同的内部属性和以下方法:isEmptyclearsizetoString

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

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

将一个元素添加到双端队列的队顶,存在三种场景:

  1. 双端队列为空时,执行 addBack方法,将元素添加到队尾。
  2. 如果双端队列的队顶在之前有元素被删除,lowestCount属性大于等于1时,只需要将 lowestCount属性减1并以这个值为键,元素为值插入对象即可。
  3. 如果 lowestCount属性值为0,需要把双端队列里的值往后顺延一位,将队顶位置空出来,然后用新添加的元素覆盖它即可了。
//addBack(el):该方法在双端队列后端添加新的元素
addBack(el) {
    this.items[this.count] = el;
    this.count++;
}
//removeFront():该方法会从双端队列前端移除第一个元素
removeFront() {
    if (this.isEmpty()) {
        return undefined;
    }
    const result = this.items[this.lowestCount];
    delete this.items[this.lowestCount];
    this.lowestCount++;
    return result;
}
//removeBack():该方法会从双端队列后端移除第一个元素
removeBack() {
    if (this.isEmpty()) {
        return undefined;
    }
    this.count--;
    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];
}

使用 Deque

const deque = new Deque
console.log(deque.isEmpty()); //true
deque.addBack("jack");
deque.addFront("jiaji");
console.log(deque.toString());
deque.removeBack();
deque.addFront("john");
console.log(deque.size());
console.log(deque.toString());

回文检查器

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

检查一个词组或者字符串有很多方法,最简单的方式就是将字符串置反后与原字符串比较,如果相同,就是回文。也可以用数据结构栈来完成,但是用数据结构最简单的方法是用双端队列。

//回文检查器
function palindromeChecker(aString) {
    //检查传入的字符串是否合法
    if (
        aString === undefined ||
        aString === null ||
        aString.trim().length === 0
    ) {
        return false;
    }
    //实例化双端队列
    const deque = new Deque();
    //将字符串转为小写并移除所有空格
    const lowerString = aString.toLocaleLowerCase().split(" ").join("");
    //默认是回文
    let isEqual = true;
    //第一个字符,最后一个字符
    let firstChar, lastChar;
    for (let i = 0; i < lowerString.length; i++) {
        //将每个字符添加到双端队列中
        deque.addBack(lowerString.charAt(i));
    }
    //循环双端队列中的每一个元素,如果isEqual为false,表示不是回文
    while (deque.size() > 1 && isEqual) {
        //移除第一个字符
        firstChar = deque.removeFront();
        //移除最后一个字符
        lastChar = deque.removeBack();
        //如果不相等,表示不是回文
        if (firstChar !== lastChar) {
            isEqual = false;
        }
    }
    return isEqual;
}
​
console.log('a', palindromeChecker('a')); 
console.log('aa', palindromeChecker('aa')); 
console.log('kayak', palindromeChecker('jiaij'));
console.log('Was it a car or a cat I saw', palindromeChecker('Was it a car or a cat I saw')); 
console.log('Step on no pets', palindromeChecker('Step on no pets'));

\