[JavaScript data structure and algorithm] 1. Stack and leetcode actual combat

the stack

A stack is an ordered collection that follows the last-in-first-out (LIFO) principle. Newly added or to-be-deleted elements are stored at the same end of the stack, called the top of the stack, and the other end is called the bottom of the stack. In the stack, new elements are near the top of the stack, and old elements are near the bottom of the stack.

stack data structure

We need a data structure to hold the elements in the stack. Arrays can be selected. Arrays allow us to add or remove elements at any position. Since the stack follows the LIFO principle, it is necessary to restrict the insertion and deletion functions of elements. The following basically uses js to implement some stack methods:

  • push(element(s)): Add one (or several) new elements to the top of the stack.
  • pop(): Remove the element at the top of the stack and return the removed element at the same time.
  • peek(): Returns the element at the top of the stack without making any changes to the stack (this method does not remove the element at the top of the stack, just returns it).
  • isEmpty(): Returns true if there are no elements in the stack, otherwise returns false.
  • clear(): Removes all elements in the stack.
  • size(): Returns the number of elements in the stack. This method is very similar to the length property of an array
class Stack{
    
    
    constructor(){
    
    
        this.items = []
    }
    push(ele){
    
    
        this.items.push(ele)
    }
    pop(){
    
    
        return this.items.pop()
    }
    peek(){
    
    
        return this.items[this.items.length-1]
    }
    isEmpty(){
    
    
        return this.items.length===0
    }
    size(){
    
    
        return this.items.length
    }
    clear(){
    
    
        this.items=[]
    }
}

At this point, we basically have a clearer understanding of the data structure and methods of the stack, let's directly implement the link below.

leetcode application

1. 20. Effective brackets (easy)

Given a string s consisting only of '(', ')', '{', '}', '[', ']', determine whether the string is valid.

A valid string must satisfy:

An opening parenthesis must be closed with a closing parenthesis of the same type.
Opening parentheses must be closed in the correct order.

hint:

1 <= s.length <= 104
s consists only of brackets '()[]{}'

  • Idea: First of all, if the string can form effective brackets, the length must be an even number. We can traverse the string and store it temporarily when we encounter a left bracket. We expect that there will be a right bracket to match it. If we encounter a right bracket, check whether it is It can be matched with the latest temporary storage. This is consistent with the first-in-last-out characteristics of the stack data structure. So we can prepare a stack to store parenthesis pairs. When traversing the string, if we encounter a left parenthesis and put it on the stack, we will judge whether the right parenthesis can match the top element of the stack when we encounter a right parenthesis. At the end of the loop, we must also judge whether the stack is is empty, if not empty, is not a valid parenthesis matching string
  • Complexity analysis:
    • Time complexity: O(n), where nn is the length of string s.
    • Space complexity: O(n + ∣Σ∣), where Σ represents the character set, the string in this question only contains 6 kinds of brackets, ∣Σ∣=6. The number of characters in the stack is O(n), and the space used by the hash table is O(∣Σ∣), and the total space complexity can be obtained by adding
var isValid = function(s) {
    
    
    const n = s.length;
    if (n % 2 === 1) {
    
    //如果字符串能组成有效的括号,则长度一定是偶数
        return false;
    }
    const pairs = new Map([//用栈存储括号对
        [')', '('],
        [']', '['],
        ['}', '{']
    ]);
    const stk = [];
    for (let ch of s){
    
    //循环字符串
        if (pairs.has(ch)) {
    
    
          	//遇到右括号则判断右括号是否能和栈顶元素匹配
            if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
    
    
                return false;
            }
            stk.pop();
        } else {
    
    
            stk.push(ch);//如果遇到左括号入栈
        }
    };
    return !stk.length;//循环结束的时候还要判断栈是否为空
};

2. 232. Use a stack to implement a queue (easy)

Please use only two stacks to implement a first-in-first-out queue. Queues should support all operations supported by general queues ( push, pop, peek, empty):

Implementation MyQueueclass :

  • void push(int x)push element x to the end of the queue
  • int pop()removes and returns an element from the beginning of the queue
  • int peek()return the element at the beginning of the queue
  • boolean empty()If the queue is empty, return true; otherwise, returnfalse

illustrate:

  • You can only use standard stack operations -- that is push to top, only peek/pop from topthe , size, , and is emptyoperations are legal.
  • Your language may not support stacks. You can use list or deque (double-ended queue) to simulate a stack, as long as it is a standard stack operation.
  • Idea: use the stack to simulate the queue, no specific algorithm is involved, and the mastery of the stack and the queue is mainly investigated. Using a first-in-last-out stack to simulate a first-in-first-out queue, a stack to simulate certainly cannot meet the needs, so two stacks are used here: one input stack and one output stack .

    • When pushing data, the data is only stored in the input stack;
    • When popping, if the output stack is empty, then import all the data into the stack (note that it is all imported), and then pop the data from the stack. If the output stack is not empty, just pop the data directly from the stack. Finally, if both the push and pop are empty, it means that the simulated queue is empty.
  • Complexity analysis:

    • Time complexity: push and empty are O(1), pop and peek are amortized O(1). For each element, there are at most two pushes and two pops, so the amortized complexity is O(1).
    • Space complexity: O(n). where n is the total number of operations. For the case of n push operations, there will be n elements in the queue, so the space complexity is O(n).
var MyQueue = function () {
    
    
    //准备两个栈
    this.stackIn = [];
    this.stackOut = [];
};

/** 
 * @param {number} x
 * @return {void}
 */
MyQueue.prototype.push = function (x) {
    
    
    this.stackIn.push(x);
};

/**
 * @return {number}
 */
MyQueue.prototype.pop = function () {
    
    
    // pop的时候判断输出栈是否为空
    const size = this.stackOut.length;
    if (size) return this.stackOut.pop(); //不为空则输出栈出栈
    
    while (this.stackIn.length) {
    
     //输出栈为空,则把输入栈所有的元素加入输出栈
        this.stackOut.push(this.stackIn.pop());
    }
    return this.stackOut.pop();
};

/**
 * @return {number}
 */
MyQueue.prototype.peek = function () {
    
    
    //查看队头的元素 复用pop方法,由于元素已经从输出栈pop,需要再次push到输出栈
    const x = this.pop(); 
    this.stackOut.push(x);
    return x;
};

/**
 * @return {boolean}
 */
MyQueue.prototype.empty = function () {
    
    
    return !this.stackIn.length && !this.stackOut.length
};

3. 155. Minimum stack (easy)

Design a stack that supports pushthe , pop, topoperations, and can retrieve the smallest element in constant time.

Implementation MinStackclass :

  • MinStack()Initialize the stack object.
  • void push(int val)Push the element val onto the stack.
  • void pop()Removes the element at the top of the stack.
  • int top()Get the element at the top of the stack.
  • int getMin()Get the smallest element in the stack.
  • Idea: Define two stacks, stack and min_stack. The stack is normally pushed, and the min_stack will only push the smaller elements that need to be pushed into the stack and the top of the min_stack stack. In this way, each time min_stack is deleted, the top element of the stack can be guaranteed to be the minimum value. getMin returns the top element of min_stack, and top returns the top element of stack.
  • Complexity Analysis
    • Time complexity: For all operations in the title, the time complexity is O(1). Because stack insertion, deletion, and read operations are all O(1), each operation we define calls stack operations at most twice.
    • Space complexity: O(n), where n is the total number of operations. In the worst case, we will insert n elements consecutively, and the space occupied by the two stacks is O(n).
var MinStack = function() {
    
    
    this.stack = [];
    this.min_stack = [Infinity];
};

/** 
 * @param {number} val
 * @return {void}
 */
MinStack.prototype.push = function(val) {
    
    
    this.stack.push(val);
    // min_stack只会push需要入栈和栈顶中较小的元素
    this.min_stack.push(Math.min(this.min_stack[this.min_stack.length - 1], val));
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    
    
    this.stack.pop();
    this.min_stack.pop();
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    
    
    // 返回stack栈顶元素
    return this.stack[this.stack.length - 1];
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
    
    
    // 返回min_stack栈顶元素
    return this.min_stack[this.min_stack.length - 1];
};

4. 946. Verify stack sequence (medium)

pushedGiven poppedtwo sequences and , each of which has unique values , returns only if they could be the result of a sequence of push and pop operations on an initially empty stack; trueotherwise, returns false.

hint:

  • 1 <= pushed.length <= 1000
  • 0 <= pushed[i] <= 1000
  • pushedAll elements of are different from each other
  • popped.length == pushed.length
  • poppedis a permutation pushedof
  • Idea: Use the stack to simulate the process of getting out of the poppedstack indexand pushing it into the stack. When the element at the pointed position is consistent with the element at the top of the stack, pop out the stack and index++finally judge whether the stack has completely traversed the popped.
  • Complexity analysis:
    • Time complexity O(n), the elements in pushed are pushed into the stack and popped out of the stack once;
    • space complexityO(n)
/**
 * @param {number[]} pushed
 * @param {number[]} popped
 * @return {boolean}
 */
var validateStackSequences = function(pushed, popped) {
    
    
    const stack = [];//用栈模拟出栈入栈的过程
    let index = 0;
    const len = pushed.length;
    for (let i = 0; i < len; i++) {
    
    
        stack.push(pushed[i]);
      	//当popped中index指向的位置的元素和stack栈顶的元素一致时,出栈 并且 index++
        while (stack.length && index<len && popped[index] === stack[stack.length - 1]) {
    
    
            stack.pop();
            index++;
        }
 
    }
    return index === len; // 最后判断 index是否完全遍历popped
};

5. 445. Adding Two Numbers II (medium)

You are given two non- empty linked lists representing two non-negative integers. The highest digit of the number is at the beginning of the linked list. Each of their nodes stores only a single digit. Adding these numbers returns a new linked list.

You can assume that neither number starts with a zero other than the number 0.

hint:

  • The length of the linked list is [1, 100]
  • 0 <= node.val <= 9
  • The input data ensures that the number represented by the linked list has no leading 0
  • Idea: push the nodes of the two linked lists into the stack, and then pop out the stack continuously, calculate the value and carry of each position, and concatenate them into a new linked list

  • Complexity Analysis

    • Time complexity: O(max(m,n)), where m and n are the lengths of the two linked lists respectively. We need to traverse all the locations of the two linked lists, and processing each location only takes O(1) time.

    • Space complexity: O(m + n), where m and n are the lengths of the two linked lists, respectively. Space complexity mainly depends on the space we use to put the content of the linked list into the stack

var addTwoNumbers = function(l1, l2) {
    
    
    const stack1 = [];
    const stack2 = [];
    while (l1 || l2) {
    
    //两链表入栈
        if (l1) {
    
    
            stack1.push(l1.val);
            l1 = l1.next;
        }
        if (l2) {
    
    
            stack2.push(l2.val);
            l2 = l2.next;
        }
    }
    let carry = 0;
    let ansList = null;
    while (stack1.length || stack2.length || carry !== 0) {
    
    //不断出栈
        const s1 = stack1.length ? stack1.pop() : 0;
        const s2 = stack2.length ? stack2.pop() : 0;
        let val = s1 + s2 + carry;
        carry = parseInt(val / 10);//计算进位
        val = val % 10;//计算当前节点的值
        const curNode = new ListNode(val);
        curNode.next = ansList;//向链表前插入新节点
        ansList = curNode;//重新赋值ansList
    }
    return ansList;
};

6. 682. Baseball game (easy)

You are now the scorer for a special baseball game. The game consists of several rounds, and the scores of past rounds may affect the scores of later rounds.

When the game starts, the record is blank. You will get a string list of record operations ops, where ops[i]is ithe operation you need to record, opsfollowing the rules below:

  1. Integer x- Indicates the new score for this roundx
  2. "+"- Indicates that the new score obtained in this round is the sum of the previous two scores. The question data guarantees that there are always two valid preceding scores when recording this operation.
  3. "D"- Indicates that the new score obtained in this round is double the previous score. The question data guarantees that there is always a valid preceding score when recording this operation.
  4. "C"- Indicates that the previous score was invalid and removed from the record. The question data guarantees that there is always a valid preceding score when recording this operation.

Please return the sum of all scores in the record.

  • Complexity: time complexity O(n), space complexityO(n)
/**
 * @param {string[]} ops
 * @return {number}
 */
var calPoints = function (ops) {
    
    
    let ans = []
    for (let i = 0; i < ops.length; i++) {
    
    
        switch(ops[i]) {
    
    
            case 'C':
                ans.pop();
                break;
            case 'D':
                ans.push(+ans[ans.length - 1] * 2);
                break;
            case '+':
                ans.push(+ans[ans.length - 1] + +ans[ans.length - 2]);
                break;
            default:
                ans.push(+ops[i])
        }
    }
    return ans.reduce((a, b) => a + b, 0);
};

Guess you like

Origin blog.csdn.net/qq_38987146/article/details/126506758