单调栈结构

文章目录

  • 作用
  • 实现流程

作用

  • 保持栈的单调性,在被破坏时弹出栈中元素,从而记录元素信息,比如记录离元素最近的比它大|小的信息,因为每个元素只会出栈入栈一次,所以时间复杂度为O(n)

实现流程

  • 如果要记录每个元素离它最近且比它小的元素,则需要建立一个单调递增栈(否则单调递减)
  • 每次放入一个元素的时候,判断是否满足单调性,如果和前一个元素相同,则前一个位置结构变成链表或者数组存放相同的元素
  • 如果满足单调递增则直接放入,如果不满足,则弹出元素直到要放入的元素满足单调递增,每弹出一个元素时,要放入的元素就是离弹出元素右边最近且比它小的,每个弹出元素的左边第一个元素就是左边离它最近且比它小的元素
  • 当元素都加入完后,如果单调栈中还有元素,那么弹出所有元素,每个元素右边没有比它小的元素,左边的第一个元素是离它最近且比它小的元素
class descStack {
  // 获取 num 最近且左右两边比它小的单调栈结构,单调递增(否则为单调递减)
  constructor(arr) {
    this.originArr = arr;
    this.stack = []; // 单调栈存放元素索引,是个二维数组结构
    this.records = []; // 记录每一个数离它左右两边最近且比它小的结构 [[左边最近比它小的索引,右边最近比它小的索引],...],其中-1表示不存在比它小的元素
    this.index = 0;

    this.doStack();
  }

  doStack() {
    for (let i = 0; i < this.originArr.length; i++) {
      this.insert(this.originArr[i], i);
    }

    // 清空单调递增元素
    if (this.stack.length) {
      this.pop();
    }
  }

  insert(num, index) {
    if (this.stack.length === 0) {
      this.stack.push([index]);
      return;
    }

    const lastIndex = this.stack.length - 1;
    const preNum = this.originArr[this.stack[lastIndex][0]];
    // 单调递增,直接插入索引
    if (preNum < num) {
      this.stack.push([index]);
    } else if (preNum === num) {
      // 相同num变成数组结构
      this.stack[lastIndex].push(index);
      console.log("adwww", this.stack);
    } else {
      // 单调性被破坏,当前num比最后一个数小,弹出到合适位置并记录
      this.pop(num, index);
    }
  }

  pop(num, index) {
    let preOneIndexArr;
    const isClearStage = num === undefined && index === undefined;

    while ((preOneIndexArr = this.stack.pop())) {
      let preOneNumIndex; // 弹出前一个数组保存的索引
      let preTwoIndexArr; // 弹出倒数第二个数组保存的索引
      for (let i = preOneIndexArr.length - 1; i >= 0; i--) {
        // 弹出开始记录
        preOneNumIndex = preOneIndexArr[i];
        preTwoIndexArr = this.stack[this.stack.length - 1];

        const left =
          preTwoIndexArr === undefined
            ? -1
            : preTwoIndexArr[preTwoIndexArr.length - 1]; // 取上一个数组最后一个元素才是左边最近的
        let right = -1;
        if (!isClearStage) {
          // 如果pop传递了参数,表面不是清空阶段,否则是清空阶段,遗留的元素右边没有比它小的元素
          right = index;
        }
        this.records[preOneNumIndex] = [left, right];
      }

      if (!isClearStage) {
        const preTwoIndexNum =
          preTwoIndexArr === undefined ? undefined : preTwoIndexArr[0];

        //非清空阶段,已有元素不满足单调性被全部清空,添加当前元素
        if (preTwoIndexNum === undefined) {
          this.stack.push([index]);
          break;
        }
        //满足单调递增了退出
        if (preTwoIndexNum < num) {
          this.stack.push([index]);
          break;
        }
        console.log("awd", preTwoIndexNum);
        // 相等保存到数组中
        if (preTwoIndexNum === num) {
          console.log("dawd", num);
          preTwoIndexArr.push(index);
          break;
        }
      }
    }
  }

  getRecords() {
    return this.records;
  }
}

const arr = [5, 5, 4, 3, 3, 6, 1, 2, 2, 0, 3];

const stack = new descStack(arr);
console.log(stack.getRecords());
/**
[
  [ -1, 2 ], [ -1, 2 ],
  [ -1, 3 ], [ -1, 6 ],
  [ -1, 6 ], [ 4, 6 ],
  [ -1, 9 ], [ 6, 9 ],
  [ 6, 9 ],  [ -1, -1 ],
  [ 9, -1 ]
]
*/

你可能感兴趣的:(前端算法,javascript,开发语言,ecmascript)