js data structure and algorithm learning stack queue single linked list

Keywords: Javascript Algorithm data structure

Understanding data structures

Data structure is a way for computers to store and organize data. A data structure is a collection of data elements that have one or more specific relationships with each other-- Baidu Encyclopedia
We understand data structures from our own perspective.

  • Data structure is a way for computers to store and organize data. The amount of data in computers is very large. How to organize and store it in an efficient way? For a simple example, in a library, how to arrange books can make it very convenient to find books, and when there are new books, they can be quickly put into the correct position. The way to solve the problem is related to the organization of books (data). What kind of way to store and organize our data can make it more convenient to use data? This is i the problem of data structure.

Common data structures

Common data structures are queue, tree, heap, stack, array, linked list, graph, hash table, etc.
Each has a corresponding application scenario. Different data structures have different operation performance. Some query quickly, some insert quickly, and so on.

Definition of algorithm

  • A finite set of instructions. The description of each instruction does not depend on the language.
  • Accept some inputs (not required in some cases)
  • Generate output
  • Must terminate after limited steps.
  • Popular understanding is the solution / step logic to solve the problem. For example, the solution that the light bulb won't open.

array

The array capacity of common languages will not change automatically. It needs to be expanded (re apply for memory and put the original array into a new array). The performance of intermediate insertion and deletion is relatively low.
js encapsulation of arrays has been very perfect.

stack

Arrays can insert and delete data at task locations. Stack and queue are common restricted linear structures.
Stack is characterized by first in first out and last in first out. It can be understood as a bucket with only one entrance. What they enter first must be the last to come out.

Add elements can only be added at the top of the stack.
For example, function call stack.
A calls B and B calls C.
In the process of execution, A will be advanced into the stack. In A, B will be called, so B will also enter the stack, and C will be called in B, so C will also enter the stack. Finally, after the execution of C, first out of the stack, then out of the stack of B, and then out of the stack of A.
Function recursion can easily cause stack overflow because it constantly calls itself. For example, when A recursively calls A, it will continue to perform A stack entry operation. More times will lead to stack overflow.

Implementation of stack structure

Array based implementation.
Common operations for implementing stack:

 class Stack {
            constructor() {
                //Store all elements in the stack
                this.items = []
            }
            //Stack related operations

            //Enter the stack
            push(item) {
                this.items.push(item)
            }
            //Out of stack
            pop() {
                this.items.pop()
            }
            //Get the last element
            peek() {
                return this.items[this.items.length - 1]
            }
            //Air judgment
            isEmpty() {
                return !this.items.length
            }
            //number
            size() {
                return this.items.length
            }
            //String i form
            toString() {
                return this.items.toString()
            }
        }

        //Stack usage
        let s = new Stack()
        s.push(12)
        s.push(32)
        console.log('Current stack structure', s.items);
        s.pop()
        console.log('Current stack structure', s.items);
        console.log('Last element', s.peek());
        console.log('Is it empty', s.isEmpty());
        console.log('number:', s.size());
        console.log('String form:', s.toString());

The implementation of stack is relatively simple.

Application of stack

Decimal to binary.
The algorithm is to divide the decimal number by 2 and spell all the remainder from back to front. as
The decimal 10 is converted to binary
10/2 = 5...0
5/2 = 2...1
2/2 = 1...0
1/2 = 0...1
So the binary of 10 is 1010.
We can press the remainder into the bottom of the stack in turn and take it out at the end.

The result is correct.

Queue structure

Queue, another restricted linear table, is characterized by FIFO (first in, first out). It only allows deletion at the front end of the table and insertion at the back end of the table.
Life: line up to go to the bathroom..
Developing: print queue. Thread queue.

js implementation queue

  • Array based
  • Based on linked list (the follow-up linked list is being implemented)

Array based

The performance of array based queue is not too high because the queue is first in first out. Once the first element is deleted, other elements of the array will be offset one bit forward.

 //Common operations of queue: 
        // enqueue(element) adds one or more items to the end of the queue
        //  dequeue() removes the first item of the queue and returns the removed element
        // front() returns the first element in the queue.
        // isEmpty() null
        // size() number
        // toString converts the contents of the queue to string form.

 //Encapsulate queue class
        class Queue {
            constructor(){
                this.items = []
            }
            enqueue(...element){
                this.items.push(...element)
            }
            dequeue(){
                this.items.shift()
            }
            front(){
                return this.items[0]
            }
            isEmpty(){
                return !this.items.length
            }
            size(){
                return this.items.length
            }
            toString(){
                return this.items.join('')
            }
        }

As above, the packaging is completed. Then realize a simple case: beating drums and passing flowers
Rule: there are seven people in a row. Those who count to five are eliminated. Ask the person who wins the last, where is the starting position?
Idea: use the queue, put seven people into the queue, and then out of the queue in turn. For example, if the first one out of the queue is 1, not 5, put it at the end of the queue, and so on. When it comes to 5, it will be eliminated from the team, and when there is one left, it will know who it is.
Encapsulate a queue based function, the parameters are seven contestants and numbers, and the result is the winner
code:

 function Game(arr, num) {
            const queue = new Queue()
            //Join the team
            queue.enqueue(...arr)
            //Define variables
            let i = 1;
            //Execute when the queue length is greater than 1
            while (queue.size() > 1) {
                if (i !== num) {
                    // It's not fifth, it's going to be added to the end,
                    queue.enqueue(queue.front())
                    i++
                } else {
                    //Set i to 0 and start again
                    i = 0
                }
                //One execution will certainly remove one
                queue.dequeue()
            }
            //Return to the rest
            console.log('The winners are:', queue.front());
            return arr.indexOf(queue.front()) + 1
        }

        console.log(Game(['lili', 'cc', 'bb', 'dd', 'ee', 'gg', 'hh'], 5));

The answer is the second cc.

Priority queue

Similar to queue, but with the concept of priority, for example, aircraft boarding, first-class business class has a higher priority than economy class. In the computer, the task priorities processed by each thread are different, and we can sort them according to different task priorities.

realization

We first create a class for each element.;


The element is created at the time of insertion, and the for loop determines the priority and inserts the array.


The priority queue is implemented successfully.

Linked list

Like arrays, linked lists can be used to store a series of elements, but the implementation mechanism is completely different.

Array:

To create an array, you need to apply for a continuous memory space with a fixed size. When the array needs to be expanded, you usually apply for a larger array, and then copy the elements in the array. Compare cost performance.
The cost of inserting data at the beginning or middle of the array is very high, and a large number of elements need to be displaced.

Linked list

A linked list is different from an array. The elements in the linked list are not continuous in memory. Each element has two parts: 1. The node that stores the element itself. 2. A reference (pointer) to the next element

  • advantage:
    The memory is not continuous. You can make full use of the computer memory to realize flexible memory management.
    It is not necessary to determine the size at the time of creation, and the size can be extended indefinitely.
    When inserting and deleting data, the time complexity is o(1)
  • Disadvantages:
    When accessing any element, you need to traverse from the beginning (the array can be obtained directly through the index)
    The elements cannot be accessed through the following table. They need to be accessed one by one.
  • The simplest example of linked list is the train. The train connects each carriage together through pointers, and each carriage has its own content.

Implement linked list

 class Node {
            constructor(data) {
                this.data = data
                this.next = null
            }
        }
        class LinkedList {
            constructor() {
                //Head pointer
                this.head = null
                //Length of linked list
                this.length = 0

            }

            append(data) {
                const element = new Node(data)
                if (!this.head) {
                    this.head = element
                } else {
                    let current = this.head

                    //Traverse to find the last node
                    while (current.next) {
                        current = current.next
                    }
                    //insert
                    current.next = element
                }
                this.length++

            }

            // //Insert at a specific location
            insert(position, data) {
                if (typeof position !== 'number' || position < 0 || position > this.length) {
                    return false
                }
                const element = new Node(data)
                //Insert first
                if (position === 0) {
                    element.next = this.head
                    this.head = element
                } else {
                    //Insert the middle bit
                    let current = this.head
                    let index = 1
                    //Use the previous bit of the insertion position for operation. For example, insert into the fourth, point element.next to the next of the third, and then re point the next of the third to element
                    while (index++ < position) {
                        // For example, when position = 4 and index = 4, current points to the third because it is executed twice
                        current = current.next
                    }
                    // Make element the fourth
                    element.next = current.next
                    current.next = element

                    // for (let i = 1; i < position; i++) {
                    //     if (i === position - 1) {
                    //         element.next = current.next
                    //         current.next = element
                    //         break;
                    //     }
                    //     current = current.next
                    // }
                }
                this.length++
            }

            // //Get the element of the corresponding position
            get(position) {
                if (typeof position !== 'number' || position < 0 || position >= this.length) {
                    return undefined
                }
                let current = this.head
                let index = 0
                while (index++ < position) {
                    current = current.next
                }
                return current.data
            }

            // //Returns the index of the element in the list
            indexOf(data) {
                let current = this.head
                let index = 0
                while (current) {
                    if (current.data === data) {
                        return index
                    }
                    current = current.next
                    index++
                }
                return -1
            }

            // //Modify an element at a location
            update(position, data) {
                if (typeof position !== 'number' || position < 0 || position >= this.length) {
                    return false
                }
                let current = this.head
                let index = 0
                while (index++ < position) {
                    current = current.next
                }
                current.data = data
            }

            // //Removes an item from a specific location in the list
            removeAt(position) {
                if (typeof position !== 'number' || position < 0 || position >= this.length) {
                    return false
                }
                let current = this.head
                let index = 0
                // Find the previous node that should be deleted, such as deleting 2. At this time, the current is the node pointed to by 1.
                while (index++ < position - 1) {
                    current = current.next
                }
                let element = current.next.next //Save the third node
                current.next.next = null //Break the relationship between the second node and the third node
                current.next = element // The first node points to the third node
                this.length--
                return true
            }

            // Remove an item from the list
            remove(element) {
                let current = this.head
                let nextElement;
                let isSuccess;

                //If the first one is
                if (current.data === element) {
                    nextElement = this.head.next
                    this.head = element
                    element.next = nextElement
                    return true
                }

                // Find the previous node that should be deleted, such as deleting 2. At this time, the current is the node pointed to by 1.
                while (current) {
                    if (current.next.data === element) {
                        nextElement = current.next.next //Save next node
                        current.next.next = null //Disconnect the next node from the next node
                        current.next = nextElement //Connect next node
                        isSuccess = true
                        break;
                    } else {
                        current = current.next
                    }
                }

                //Delete successfully
                if (isSuccess) {
                    this.length--
                    return true
                }

                return false
            }

            isEmpty() {
                return !!this.length
            }

            size() {
                return this.length
            }

            toString() {
                let current = this.head
                let str = ''
                while (current) {
                    str += `,${current.data.toString()}`
                    current = current.next
                }
                return str.slice(1)
            }


        }

The main difficulties in implementing the linked list are several methods, specific location insertion, specific location deletion and so on. The idea is to use the loop to traverse from beginning to end to find the corresponding node and do the corresponding thing.
If a specific location is inserted, you need to find the node before the specific location, save its next node, point its next pointer to the inserted element, and then point the next pointer of the inserted element to the next node. That's it.

Posted by misterfine on Wed, 27 Oct 2021 17:39:51 -0700