Data structure and algorithm--javascript (continuously updated...)

1. Introduction

1. Data structure
队列: An ordered set of items following 先进先出the (FIFO / First In First Out) principle; a queue adds new elements at the tail and removes elements from the head. The most recently added element must be at the end of the queue. (例如:去食堂排队打饭,排在前面的人先打到饭,先离开;排在后面的人后打到饭,后离开。)
insert image description here
栈:An 先进后出ordered collection that follows the (LIFO) principle; newly added or deleted elements are stored at the end of the stack, called the top of the stack, and the other end is 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. (例如:往口袋里面装东西,先装进去的放在最下面,后装进去的放在最上面,取出的时候只能从上往下取。)
insert image description here
insert image description here
链表:Stores an ordered collection of elements, but unlike an array, the elements in a linked list are not placed consecutively in memory; each element consists of a node that stores the element itself and a reference (pointer/link) to the next element.
insert image description here
集合:Consists of an unordered and unique (ie, non-repeating) set of items; this data structure uses the same mathematical concepts as finite sets, but applied to computer science data structures.

字典:A data structure in the form of [key, value] pairs, where the key name is used to query a specific element, similar to Object in Javascript.

哈希表:A data structure that is directly accessed according to the key value (Key value). Access records by mapping a key value to a location in the table to speed up lookups. This mapping function is called a hash function, and the array storing the records is called a hash table.
Given a table M, there is a function f(key). For any given key value key, if the address of the record in the table containing the key can be obtained after substituting the function, the table M is called a hash (Hash). table, the function f(key) is a hash (Hash) function.

树:A collection of n (n>=1) finite nodes with a hierarchical relationship; it is called a "tree" because it looks like an upside-down tree, that is to say, it has the root facing up and the leaves facing down Yes, it is basically a one-to-many relationship, and a tree can also be regarded as a special form of a graph.

图:A graph is an abstract model of a network structure; a graph is a group of nodes (vertices) connected by edges; any binary relationship can be represented by a graph, such as: road graphs, relationship graphs, and many-to-many relationships.

2. Algorithm
sorting algorithm:
冒泡排序(升序) : Compare two adjacent elements one by one, if the front element is larger than the back element, the order of the two is exchanged; the element item moves up to the correct order, as if the bubble rises to the surface, hence the name. (At least one element will appear in the correct position in each round of bubble sorting)

快速排序(升序): Select a benchmark value, and compare each element with the benchmark value. Elements smaller than the reference value are placed to the left of the reference value, elements larger than the reference value are placed to the right of the reference value, and the operation is performed recursively on the left and right sides. Usually the element in the middle is chosen as the reference value.

选择排序:Select the smallest (or largest) element from the data elements to be sorted each time, store it in the starting position of the sequence, and cycle until the sorting is completed.

插入排序:Insert a piece of data into the ordered data that has been sorted, so as to obtain a new ordered data whose number is increased by one. This algorithm is suitable for sorting a small amount of data.

归并排序:Divide the original sequence into smaller sequences, until each small sequence can no longer be divided, and then perform a merge, that is, merge the small sequences into a large sequence, compare and sort the merge process, until there is only one sorted large sequence at the end , the time complexity is O(n log n).
insert image description here
Intuitive comparison of various time complexities:
insert image description here
insert image description here
Search algorithm:
顺序搜索 : Let the target element be compared with each element in the list one by one until the element that is the same as the given element is found. The disadvantage is low efficiency.

二分搜索: In an ordered list, split it into two sublists based on the middle value, compare the target element with the middle value, and then recurse this method in the target sublist until the target element is found.

Other algorithms:
贪心算法 : When solving a problem, it does not consider the overall situation, and always makes a local optimal solution.

动态规划: When solving the problem, the global optimal solution is derived from the local optimal solution obtained by the above.

复杂度概念: The resources that a method needs to occupy during the entire life cycle of execution mainly include: time resources and space resources.

2. Data structure

Queue
概念 : An 先进先出ordered set of items following the (FIFO / First In First Out) principle; a queue adds new elements at the tail and removes elements from the head. The most recently added element must be at the end of the queue.

Implement a queue class in Javascript.
1. Create a class:

class Queue{
    
    
	 constructor(items) {
    
    
        this.items = items || []
    }
	
	// 1. 在末尾添加元素
	enqueue(element){
    
    
        this.items.push(element)
    }
	
	// 2. 在头部删除元素
	dequeue(){
    
    
        return this.items.shift()
    }
	
	// 3. 获取头部元素
	front(){
    
    
        return this.items[0]
    }

	// 4. 获取队列长度
	get size(){
    
    
        return this.items.length
    }

	// 5. 判断是否是空队列
	 get isEmpty(){
    
    
        return !this.items.length
    }

	// 6. 清空队列
	clear(){
    
    
        this.items = []
    }
}

2. Use class:

const queue = new Queue()  // 类的实例化
queue.isEmpty  // true

queue.enqueue('John')   // {items: ['John']}
queue.enqueue('Jack')   // {items: ['John','Jack']}
queue.enqueue('Camila')  // {items: ['John','Jack','Camila']}

queue.size  // 3
queue.isEmpty  // false

queue.dequeue()   // John
queue.dequeue()   // Jack
queue.dequeue()   // Camila

Priority Queue
概念 : 元素的添加和移除是基于优先级的,不在满足完全意义的先进先出。For example, in the order of boarding at the airport, first and business class passengers have priority over economy class passengers, and these passengers do not need to go through the normal queue to board the plane. Or go to the hospital to see a doctor, the doctor will give priority to the serious condition according to the severity of the condition.

To implement a queue class in Javascript:
1. Create a class:

class PriorityQueue {
    
    

    constructor() {
    
    
        this.items = []
    }

    enqueue(element, priority){
    
    
        const queueElement = {
    
     element, priority }
        if (this.isEmpty) {
    
    
            this.items.push(queueElement)
        } else {
    
    
// 在列表中找到第一个比后进入的元素的priority大的元素的位置,如果有,将这个元素插入这里。如果没有,将这个元素放在最后面。
            const preIndex = this.items.findIndex((item) => queueElement.priority < item.priority)
            if (preIndex > -1) {
    
    
                this.items.splice(preIndex, 0, queueElement)
            } else {
    
    
                this.items.push(queueElement)
            }
        }
    }

    dequeue(){
    
    
        return this.items.shift()
    }

    front(){
    
    
        return this.items[0]
    }

    clear(){
    
    
        this.items = []
    }

    get size(){
    
    
        return this.items.length
    }

    get isEmpty(){
    
    
        return !this.items.length
    }

    print() {
    
    
        console.log(this.items)
    }
}

2. Use class:

const priorityQueue = new PriorityQueue()
priorityQueue.enqueue('Wangjiajia', 2) // {items: [{element: 'Wangjiajia', priority: 2}]}
priorityQueue.enqueue('Wangtongtong', 1) // {items: [{element: 'Wangtongtong', priority: 1},{element: 'Wangjiajia', priority: 2}]}
priorityQueue.enqueue('Davide', 4)  // {items: [{element: 'Wangtongtong', priority: 1},{element: 'Wangjiajia', priority: 2},{element: 'Davide', priority: 4}]}
priorityQueue.enqueue('Tom', 3)  // {items: [{element: 'Wangtongtong', priority: 1},{element: 'Wangjiajia', priority: 2},{element: 'Tom', priority: 3},{element: 'Davide', priority: 4}]}
priorityQueue.enqueue('James', 2)  // {items: [{element: 'Wangtongtong', priority: 1},{element: 'Wangjiajia', priority: 2},{element: 'James', priority: 2},{element: 'Tom', priority: 3},{element: 'Davide', priority: 4}]}

In order to make full use of the vector space and overcome the "false overflow" phenomenon, the circular queue
概念: imagines the vector space as a ring connected end to end, and calls this vector a circular vector. The queue stored in it is called a circular queue (Circular Queue). In layman's terms: the circular queue is to connect the sequential queues end to end, and the table storing the queue elements is logically regarded as a ring, which becomes a circular queue.
假溢出:The space of the queue is not used up, but it causes overflow of elements.
insert image description here
Based on the queue class implemented for the first time, a simple implementation of a circular reference example:

class LoopQueue extends Queue {
    
    

    constructor(items) {
    
    
        super(items)
    }

    getIndex(index) {
    
    
        const length = this.items.length
        return index > length ? (index % length) : index
    }

    find(index) {
    
    
        return !this.isEmpty ? this.items[this.getIndex(index)] : null
    }
}

use:

const loopQueue = new LoopQueue(['Surmon'])
loopQueue.enqueue('SkyRover')
loopQueue.enqueue('Even')
loopQueue.enqueue('Alice')
console.log(loopQueue.size, loopQueue.isEmpty) // 4 false

(loopQueue.find(26) // 'Evan'
(loopQueue.find(87651) // 'Alice'

Stack:
概念 : An 先进后出ordered collection that follows the (LIFO) principle; newly added or to-be-deleted elements are stored at the end of the stack, called the top of the stack, and the other end is the bottom of the stack, and go out from the top of the stack when going out. In the stack, new elements are near the top of the stack, and old elements are near the bottom of the stack. `

Use javascript to realize the function of a stack based on the array method:

class Stack {
    
    

    constructor() {
    
    
        this.items = []
    }

    // 入栈
    push(element) {
    
    
         this.items.push(element)
    }

    // 出栈
    pop() {
    
    
        return this.items.pop()
    }

    // 末位
    get peek() {
    
    
        return this.items[this.items.length - 1]
    }

    // 是否为空栈
    get isEmpty() {
    
    
        return !this.items.length
    }

    // 尺寸
    get size() {
    
    
        return this.items.length
    }

    // 清空栈
    clear() {
    
    
        this.items = []
    }

    // 打印栈数据
    print() {
    
    
        console.log(this.items.toString())
    }
}

Use the stack:

// 实例化一个栈
const stack = new Stack()
console.log(stack.isEmpty) // true

// 添加元素
stack.push(5)   // [5]
stack.push(8)   // [5,8]

// 读取属性再添加
console.log(stack.peek) // 8
stack.push(11)  // [5,8,11]
stack.pop()  // 11
console.log(stack.size) // 3
console.log(stack.isEmpty) // false

Linked list:
概念: stores an ordered collection of elements, but 不同于数组,链表中的元素在内存中并不是连续放置的;each element is stored by one 元素本身的节点和一个指向下一个元素的引用(指针/链接)组成.
分类:Singly linked list, doubly linked list, circular linked list
链表和数组的区别:
* The array needs to move other elements when adding or deleting elements, but the linked list does not. Linked lists need to use pointers, so you need to pay extra attention when using them.
* The array can access any of its elements, and the linked list needs to iterate from the beginning until the desired element is found.

Common methods for linked lists:
1、append(element): Add a new item to the end of the list
2、insert(position, element):Insert a new item at a specific position in the list.
3、remove(element):Remove an item from the list.
4、removeAt(position):Remove an item from a specific position in the list.
5、indexOf(element):Returns the index of the element in the list. Returns -1 if the element is not present in the list.
6、getElementAt(index):Returns the element at a specific position in the linked list. If there is no such element, it returns undefined.
7、isEmpty():If the linked list does not contain any elements, it returns true. If the linked list length is greater than 0, it returns false.
8、size():Returns the number of elements contained in the linked list. Similar to the length property of an array.
9、toString():Since the list item uses the Node class, it is necessary to override the default toString method inherited from the JavaScript object so that it only outputs the value element of the element.

整体操作方法和数组非常类似, 因为链表本身就是一种可以代替数组的结构.
使用javascript描述一个单向链表

One-way linked list:
insert image description here
An example in life: a train can be regarded as a linked list, and each section is composed of a carriage and a connecting belt between the carriages. This connecting belt can be regarded as a pointer.
insert image description here
Use javascript to describe a one-way linked list:

// 链表节点
class Node {
    
    
    constructor(element) {
    
    
        this.element = element
        this.next = null
    }
}

// 单向链表
class LinkedList {
    
    
    constructor() {
    
    
        this.head = null
        this.length = 0
    }

    // 1. 追加元素
    // 向链表尾部追加数据可能有两种情况:
	// 链表本身为空, 新添加的数据是唯一的节点.
	// 链表不为空, 需要向其他节点后面追加节点.
    append(element) {
    
    
        const node = new Node(element)
        let current = null
        // 链表本身为空, 新添加的数据是唯一的节点.
        if (this.head === null) {
    
    
            this.head = node
        } else {
    
    
       		// 链表不为空, 需要向其他节点后面追加节点.
            current = this.head
            while(current.next) {
    
    
                current = current.next
            }
            current.next = node
        }
        this.length++
    }

    // 2. 任意位置插入元素
    insert(position, element) {
    
    
        // 1.检测越界问题: 越界插入失败, 返回false
    	if (position < 0 || position > this.length) return false

	    // 2.找到正确的位置, 并且插入数据
	    // 定义要插入的变量newNode, current当前节点, previous上一个节点
	    let newNode = new Node(element)
	    let current = this.head // 初始值为head, 对第一个元素的引用
	    let previous = null // 存储当前current的上一个节点
	    let index = 0  // 位置
	
	    // 3.判断是否列表是否在第一个位置插入
	    if (position == 0) {
    
    
	        newNode.next = current
	        this.head = newNode
	    } else {
    
    
	        while (index++ < position) {
    
     // 向前赶, 直到找到当前位置position
	            previous = current
	            current = current.next
	        }
	        // index === position, 找到要插入的位置
	        newNode.next = current
	        previous.next = newNode
	    }
	    
	    // 4.length+1
	    this.length++
    }

    // 3. 从链表中任意移除一项
    removeAny(position) {
    
    
         //边界检查,越界返回false
        if(position < 0 || position > this.length-1){
    
    
            return false
        }
         let current = this.head
         let previous = null
         let index = 0
         // 如果删除第一个元素
         if(position == 0){
    
    
			this.head = current.next
		 }else{
    
    
		 	// 不是删除第一个找到删除的位置
			while(index++ < position){
    
    
				previous = current
				current = current.next
			}
			previous.next = current.next
		}
        this.length--
        return current.element
    }

    // 4. 寻找元素下标
    findIndex(element) {
    
    
        let current = this.head
        let index = 0
        //遍历链表直到找到data匹配的position
        while (current) {
    
    
            if (element === current.element) {
    
    
                return index
            }
            current = current.next
            index++
        }
        return false
    }

    // 5. 从链表的特定位置移除一项。
    remove(element) {
    
    
        const index = this.indexOf(element)
        return this.removeAt(index)
    }

	// 6. 判断是否为空链表
    isEmpty() {
    
    
        return !this.length
    }

	// 7. 获取链表长度
    size() {
    
    
        return this.length
    }

    // 8. 转为字符串
    toString() {
    
    
        let current = this.head
        let str = ''
        while (current) {
    
    
            str += ` ${
      
      current.element}`
            current = current.next
        }
        return str
    }
}

The use of linked list class:

const linkedList = new LinkedList()

console.log(linkedList)  //  LinkedList {head: null, length: 0}
// 1. 在末尾追加元素
linkedList.append(2)  // LinkedList {head: {element:2,next:null}, length: 1}
linkedList.append(4)  // LinkedList {head: {element:2,next:{element:4,next:null}}, length: 2}
linkedList.append(6) // LinkedList {head: {element:2,next:{element:4,next:{element:6,next:null}}}, length: 3}

// 2. 在任意位置插入一个元素
linkedList.insert(2, 18) // LinkedList {head: {element:2,next:{element:4,next:{element:18,next:{element:6,next:null}}}}, length: 4}

// 3. 从链表中任意位置删除一项
linkedList.removeAny(2) // 18

// 4. 寻找元素下标
linkedList.findIndex(6)  // 2
linkedList.findIndex(18)  // false
linkedList.findIndex(20)  // false

// 5. 从链表的特定位置移除一项。
linkedList.remove(4)

// 6. 判断是否为空链表
linkedList.isEmpty()  // false

// 7. 获取链表长度
linkedList.size()  // 2

// 8. 转为字符串
linkedList.toString()  // ' 2 4 18 6'

Doubly linked list:
概念: The difference between a doubly linked list and an ordinary linked list is that in a linked list, a node has only a link to the next node, while in a doubly linked list, the links are bidirectional: one links to the next element, and the other links forward An element, as shown in the figure below:
insert image description here
use javacsript to implement a doubly linked list class:

class Node{
    
    
	constructor(element){
    
    
		this.element = element
		this.next = null 
		this.prev = null 
	}
}

// 双向链表
class DoublyLinkedList {
    
    
    constructor() {
    
     
        this.head = null
        this.tail = null
        this.length = 0
    }

	// 1. 任意位置插入
	insert(position,element){
    
    
		// 1.1 检测越界问题: 越界插入失败, 返回false
    	if (position < 0 || position > this.length) return false
    	let newNode = new Node(element)
	    let current = this.head // 初始值为head, 对第一个元素的引用
	    let previous = null // 存储当前current的上一个节点
	    let index = 0  // 位置
	    // 1.2 如果插在了首位
	    if(position == 0){
    
    
			// 空链表
			if(!head){
    
    
				this.head = node 
				this.tail = node 
			}else{
    
    
				this.head = node
				node.next = current
				current.prev = node
			}
		}
		// 1.3 如果插在了末位
		else if(position == this.length) {
    
    
			this.tail = node
			current.next = node
            node.prev = current
		}else {
    
    
			// 如果插在了中间位置
			// 找到插入的位置
			while(idex++ < position){
    
    
				previous = current
	            current = current.next
			}
			// 插入进去
			node.next = current
            previous.next = node
            node.pre = previous
            current.pre = node
			this.length++
            return true
		}
	}
	
	// 2. 移除任意位置元素
	removeAny(position){
    
    
		// 2.1 边界检查,越界返回false
		if(position < 0 || position > this.length-1) return false
		let current = this.head
        let previous = null
        let index = 0
		// 2.2 如果删除的是首位
		if(position == 0){
    
    
			this.head = current.next
			this.head.prev = null
		}else if(position == this.length - 1){
    
    
			// 2.3 如果删除的是末位
			this.tail = current.pre
			this.tail.next = null 
		}else {
    
    
			// 2.4 中间项
			// 找到删除的位置
			while(index++ < position){
    
    
				previous = current
				current = current.next
			}
			previous.next = current.next
			current.prev = current.next.prev
			this.length--
         	return current.element
		}
	} 
}

Circular linked list:
概念: A circular linked list can have only one-way references like a singly linked list, or it can have two-way references like a doubly linked list.
单向循环链表: The pointer from the last element to the next element (tail.next) is not a reference to null, but points to the first element (head), as shown in the figure below: Two-way circular linked list: the pointer
insert image description here
tail from the last element to the next element. next is not null, but points to the first element (head), and the pointer head.prev of the first element points to the previous element is not null, but the last element tail. As shown in the figure:
insert image description here
The advantages of the linked list: elements can be easily added and removed without moving the elements in the linked list. Therefore, when you need to add and remove many elements, the best choice is a linked list instead of an array.

Collections:
概念 : A collection is an unordered set of unique (no duplicate) items. At present, the implementation of Set has been built into ES6.
集合中常用的一些方法:
add() Add element
delete() Delete element, return Boolean value, return true if deletion is successful, return false if deletion fails
has() Determine whether an element is contained, return Boolean value
clear() Clear set data structure
size No parentheses, return The length of this structure

Using collections in ES6:

const arr = [1,2,2,3,3,3,4,4,5]
const str = 'wangjiajiawwwjiajiawwwww'
// 1.添加元素
new Set(arr)   // Set{1,2,3,4,5}
new Set(str )    // Set{'w', 'a', 'n', 'g', 'j', 'i'}
new Set(arr).add(8)    // Set{1,2,3,4,5,8}

// 2.删除元素 
new Set(arr).delete(2)    // true
new Set(arr).delete(9)    // false

// 3.判断是否含有某个元素
new Set(arr).has(2)    // true
new Set(arr).has(9)    // false

// 4.清空set数据结构
new Set(arr).clear()  // undefined

// 5.没有括号,返回此结构长度
new Set(arr).size    // 5

Using collections in javascript:

// hasOwnProperty(propertyName)方法 是用来检测属性是否为对象的自有属性,如果是,返回true,否则返回false; 参数propertyName指要检测的属性名;
// hasOwnProperty() 方法是 Object 的原型方法(也称实例方法),它定义在 Object.prototype 对象之上,所有 Object 的实例对象都会继承 hasOwnProperty() 方法。
class Set {
    
    
    constructor() {
    
    
        this.items = {
    
    }
    }

    has(value) {
    
    
        return this.items.hasOwnProperty(value)
    }

    add(value) {
    
    
        if (!this.has(value)) {
    
    
            this.items[value] = value
            return true
        }     
        return false
    }

    remove(value) {
    
    
        if (this.has(value)) {
    
    
            delete this.items[value]
            return true
        }
        return false
    }

    get size() {
    
    
        return Object.keys(this.items).length
    }

    get values() {
    
    
        return Object.keys(this.items)
    }
}
const set = new Set()
set.add(1)
console.log(set.values)  // ["1"] 
console.log(set.has(1))  // true 
console.log(set.size) // 1 
set.add(2) 
console.log(set.values)  // ["1", "2"] 
console.log(set.has(2))  // true 
console.log(set.size) // 2 
set.remove(1) 
console.log(set.values) // ["2"] 
console.log(set.has(1))  // false
set.remove(2) 
console.log(set.values) // []

The following operations can be performed on collections:
并集 : Given two collections, returns a new collection containing all the elements in both collections.
交集: Given two collections, returns a new collection containing elements common to both collections.
差集: Given two collections, returns a new collection containing all elements that are present in the first collection and not present in the second collection.

Union:
The mathematical concept of union: the union of sets A and B, denoted as A∪B, defined as follows: A∪B = { x | x∈AV x∈B }, meaning that x (element) exists in A in, or x exists in B. As shown in the picture:

insert image description here
Implement a union method based on the Set class just now:

union(otherSet) {
    
    
    const unionSet = new Set()
    // 先添加其中一个集合的元素放在unionSet中
    this.values.forEach((v, i) => unionSet.add(this.values[i]))
    // 在把另一个集合的元素放入unionSet中
    otherSet.values.forEach((v, i) => unionSet.add(otherSet.values[i]))
    return unionSet
}

Intersection:
The mathematical concept of union: the intersection of sets A and B, expressed as A∩B, defined as follows: A∩B = { x | x∈A ∧ x∈B }, meaning that x (element) exists in A , and x exists in B. As shown in the picture:

insert image description here
Implement an intersection method based on the Set class just now:

intersection(otherSet) {
    
    
    const intersectionSet = new Set()
    // 从集合A开始循环判断,如果这个元素也在集合B中,那就说明这个元素是集合A,B公有的。这时候把这个元素放到一个新的集合中
    this.values.forEach((v, i) => {
    
    
        if (otherSet.has(v)) {
    
    
            intersectionSet.add(v)
        }
    })
    return intersectionSet
}

Difference set:
The mathematical concept of difference set: the difference set of sets A and B, denoted as AB, defined as follows: AB = { x | x∈A ∧ x∉B }, meaning that x (element) exists in A, and does not exist in B. As shown in the figure:
insert image description here
the method of implementing a difference set AB based on the Set class just now:

difference(otherSet) {
    
    
	// 从集合A开始循环判断,如果这个元素不在集合B中。说明这个元素是A私有的,此时把这个元素放入一个新的集合中。
    const differenceSet = new Set()
    this.values.forEach((v, i) => {
    
    
        if (!otherSet.has(v)) {
    
    
            differenceSet.add(v)
        }
    })
    return differenceSet
}

Subset:
The mathematical concept of subset: set A is a subset of B, or set B contains set A, as shown in the figure:
insert image description here
implement a subset method based on the Set class just now:

// 在这里this代表集合A,otherSet代表集合B
subset(otherSet){
    
    
	if(this.size > otherSet.size){
    
    
		return false
	}else{
    
    
		// 只要A里面有一个元素不在B里面就说明A不是B的子集,后面元素不用在判断了
		this.values.every(v => !otherSet.has(v))
	}
}

Dictionaries:
Sets, dictionaries, and hash tables can all store unique data. A dictionary stores data in the form of key-value pairs, similar to the Object object in javascript.

Implementing a dictionary in javascript:

class Dictionary {
    
    
    constructor() {
    
    
        this.items = {
    
    }
    }

    set(key, value) {
    
    
        this.items[key] = value
    }

    get(key) {
    
    
        return this.items[key]
    }

    remove(key) {
    
    
        delete this.items[key]
    }

    get keys() {
    
    
        return Object.keys(this.items)
    }

    get values() {
    
    
        /*
        也可以使用ES7中的values方法
        return Object.values(this.items)
        */

        // 在这里我们通过循环生成一个数组并输出
        return Object.keys(this.items).reduce((r, c, i) => {
    
    
            r.push(this.items[c])
            return r
        }, [])
    }
}

Use a dictionary:

const dictionary = new Dictionary()
dictionary.set('Wangjiajia', '[email protected]')
dictionary.set('Wangtongtong', '[email protected]')
dictionary.set('davide', '[email protected]') 

console.log(dictionary)  // {items:{'Wangjiajia', '[email protected]','Wangtongtong', '[email protected]','davide', '[email protected]'}}
console.log(dictionary.keys)  // ['Wangjiajia','Wangtongtong','davide']
console.log(dictionary.values) // [[email protected]','[email protected]','[email protected]']
console.log(dictionary.items)  // {'Wangjiajia': '[email protected]','Wangtongtong': '[email protected]','davide':'[email protected]'}

Hash table:

  1. 根据关键码值(Key value)而直接进行访问的数据结构。Access records by mapping a key value to a location in the table to speed up lookups. This mapping function is called a hash function, and the array storing the records is called a hash table.
  2. 给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

Schematic diagram of a hash table:
insert image description here
哈希表冲突:When a hash value is obtained through a hash function, it is very likely that data has already been stored at the subscript of this value, and a conflict occurs at this time.
解决冲突:Chain address method, open address method.

Implement a hashtable in javascript:

function HashTable(){
    
    
	// 初始化哈希列表
	// 1. 设置哈希表数组
	this.item = []
	// 2. 设置哈希表长度
	this.limit = 10
	// 3. 设置表中数据量
	this.total = 0

	// 设置哈希函数
	HashTable.prototype.hashFunc = function(key, limit){
    
    
		let keyvalue = 0
        for(let i=0; i<key.length; i++){
    
    
            keyvalue=keyvalue * 12 + key.charCodeAt(i)
        }
        //使用取余运算计算hashCode
	    return keyvalue%limit
	}

	// 增、改
	 HashTable.prototype.put = function(key, value){
    
    
		// 根据hashCode找到对应下标修改或者添加数据
        let index = this.hashFunc(key, this.limit)
        let arr = this.item[index]
        // 如果这个下标所处的位置已经存放过数据,即arr有值,此时存在哈希表冲突
        if(arr){
    
    
			let completed = false
            // 遍历数组,如果发现重名数据则直接修改
            arr.forEach(item=>{
    
    
                if(key===item[0]){
    
    
                    completed = true
                    return item[1] = value
                }
            })
            // 如果没有重名则在末尾添加新数据
            if(!completed){
    
    
				arr.push([key, value])
                this.total++
			}
		}else{
    
    
            //如果basket为null则重新创建数组
            this.item[index] = [[key, value]]
            this.total++
        }
	}
	// 查
	HashTable.prototype.get = function(key){
    
    
        let index = this.hashFunc(key, this.limit)
        let basket = this.item[index]
        //如果basket为null则没有对应数据
        if(basket===undefined){
    
    
            return null
        }else{
    
    
            //如果有basket, 则遍历basket,遍历完没有对应key则不存在对应数据
            for(let i = 0; i < basket.length; i++){
    
    
                if(key===basket[i][0]) return basket[i][1]
            }
            return null
        }
    }
    // 删
    HashTable.prototype.remove = function(key){
    
    
        let index = this.hashFunc(key, this.limit)
        let basket = this.item[index]
        //如果basket为null则没有对应数据
        if(!basket){
    
    
            return null
        }else{
    
    
            //如果有basket, 则遍历basket,遍历完没有对应key则不存在对应数据
            for(let i = 0; i < basket.length; i++){
    
    
                if(key===basket[i][0]){
    
    
                    this.total--
                    return basket.splice(i, 1)
                }
            }
            return null
        }
    }
	//求表长
    HashTable.prototype.size = function(){
    
    
        return this.total
    }
 
    //判断表空
    HashTable.prototype.isEmpty = function(){
    
    
        return this.total===0
    }
}

Tree:

  1. 概念:是一种天然具有递归属性的非顺序数据结构,是一种特殊的图(无向无环)。It is composed of n nodes and has a certain hierarchical relationship.

  2. 根结点:树的最顶端的节点,根节点只有一个

  3. 子节点:除根节点之外,并且本身下面还连接有节点的节点。

  4. 叶子结点:自己下面不再连接有节点的节点(即末端),称为叶子节点(又称为终端结点),度为0

  5. Depth: the number of nodes from the node to the root node

  6. Height: the maximum of all node depths

  7. Ordinary tree:
    insert image description here

  8. Binary tree:

    • Each node of the tree can have at most two child nodes
    • The binary tree can be empty, that is, there are no nodes. If it is not empty, it is composed of two disjoint binary trees with left and right subtrees.
      insert image description here
  9. Binary search tree:

    • The key value of the non-empty left subtree is less than the key value of the root node
    • The key value of the non-empty right subtree is greater than the key value of the root node
    • Both left and right subtrees are binary search trees
      insert image description here
  10. Full binary tree and complete binary tree A full
    binary tree means that each node has two child nodes, and the left and right subtrees are symmetrical. A complete binary tree has branches that are not completely symmetrical.
    insert image description here

  11. The red-black binary tree
    红黑二叉树本质上就是一种二叉搜索树,maintains the balance of the binary tree through specific operations during operations such as insertion and deletion. 红黑二叉树本身是复杂的, but its worst-case running time is also very good, and it is efficient in practice: it can do lookup, insertion, and deletion in O(log n) time, where n is the number of elements in the tree.
    The red-black binary tree must meet the following requirements on the basis of the binary search tree:

    • Node must be red or black
    • The root node must be black
    • If the node is red, then its child nodes must be black (not necessarily vice versa)
    • There cannot be two consecutive red nodes from the root node to any leaf node
    • From any node to each of its leaf nodes, the number of black nodes on all routes is the same
      insert image description here

Implement some operations of binary tree with javascript:

// 封装二叉树节点
class Node {
    
    
  constructor(data){
    
    
    this.data = data
    this.left = null
    this.right = null
  }
}

// 封装二叉搜索树
class BinarySearchTree {
    
    
  constructor(){
    
    
    this.root = null  // 定义二叉树根节点
  }

  // 插入节点
  insert(key){
    
    
    const newNode = new Node(key)
    // 定义插入节点的函数,比根节点小的放左边,比根节点大的放右边
    function insertNode(node, newNode){
    
    
      if(newNode.key < node.key){
    
    
        // 判断根节点的左侧是否有节点,如果没有节点则直接插入,如果有节点则递归执行insertNode函数
        if(node.left == null){
    
    
          node.left = newNode
        }else{
    
    
          insertNode(node.left, newNode)
        }
      }else{
    
    
        // 判断根节点的右侧是否有节点,如果没有节点则直接插入,如果有节点则递归执行insertNode函数
        if(node.right == null){
    
    
          node,right = newNode
        }else{
    
    
          insertNode(node.right, newNode)
        }
      }
    }
    // 如果是空二叉树,则插入的节点就是根节点,否则正常插入
    if(!this.root){
    
    
      this.root = newNode
    }else{
    
    
      insertNode(this.root, newNode)
    }
  }
}

Use of the binary search tree class:

const tree = new BinarySearchTree()
tree.insert(11)
tree.insert(7)
tree.insert(5)
tree.insert(3)
tree.insert(9)
tree.insert(8)
tree.insert(10)
tree.insert(15)
tree.insert(13)
tree.insert(12)
tree.insert(14)
tree.insert(20)
tree.insert(18)
tree.insert(25)

insert image description here

Binary tree traversal: the process of visiting each node of the tree and performing certain operations on them

  • 前序遍历:First visit the root node, then visit the left node, and finally visit the right node (the same operation is performed for the left and right subtrees, 根左右)
  • 中序遍历: First visit the left node, then visit the root node, and finally visit the right node,(左根右)
  • 后序遍历: Visit the left node first, then the right node, and finally the root node.(左右根)

Preorder traversal:

function preorder(root){
    
    
  // 排除根节点为空的情况
  if(!root) return
  // 1. 访问根节点
  console.log(root.key)  // 11 7 5 3 6 9 8 10 15 13 12 14 20 18 25
  // 2. 递归对根节点的左子树进行先序遍历
  preorder(root.left)
    // 3. 递归对根节点的右子树进行先序遍历
  preorder(root.right)
}
preorder(tree)

insert image description here

Inorder traversal:

const inorder = (root) => {
    
    
	if(!root) return
	// 1. 递归对根节点的左子树进行中序遍历
	inorder(root.left)
	// 2. 访问根节点
	console.log(root.key)  // 3 5 6 7 8 9 10 11 12 13 14 15 18 20 25
	// 3. 递归对根节点的右子树进行中序遍历
	inorder(root.right)
}
inorder(tree)

insert image description here

Postorder traversal:

const postorder = (root) => {
    
    
	if (!root) return
	// 1. 递归对根节点的左子树进行后序遍历
	postorder(root.left)
	// 2. 递归对根节点的左子树进行后序遍历
	postorder(root.right)
	// 3. 访问根节点
	console.log(root.key)  // 3 6 5 8 10 9 7 12 14 13 18 25 20 15 11
}
postorder(tree)

insert image description here
Search for minimum and maximum values:
minimum on the far left, maximum on the right

function minNum(node) {
    
    
  const minNode = node => {
    
    
      return node ? (node.left ? minNode(node.left) : node.key) : null
  }
  return minNode(node || this.root)
}

function maxNum(node) {
    
    
  const maxNode = node => {
    
    
      return node ? (node.right ? maxNode(node.right) : node.key) : null
  }
  return maxNode(node || this.root)
}
minNum(tree.root)  // 3
maxNum(tree.root)  // 25

Search for a specific value:

function search(node,key){
    
    
  const searchNode = (node,key)=>{
    
    
    if(node === null)return false
    if(node.key === key) return true
    return searchNode( key < node.key ? node.left : node.right, key)
  }
  return searchNode(node, key)
}
search(tree.root,3)  // true
search(tree.root,3)  // false

picture:

  1. Concept: a data structure consisting of a set of vertices and the edges connecting them
  2. Representation method: G=(V, E) V is the set of vertices in graph G, and E is the set of edges in graph G.

Undirected graph: The edge connecting the vertices has no direction
insert image description here
说明 : the vertices connected by an edge are called adjacent vertices, such as A and B are adjacent, A and D are adjacent, A and C are adjacent, A and E are not adjacent. The degree of a vertex is the number of its neighbors. For example: A has degree 3 and E has degree 2. A graph is called a cyclic graph if there is a cycle, and a graph is said to be connected if there is a path between every two vertices.

Directed graphs: edges connecting vertices have directions
insert image description here
说明: A graph is strongly connected if there is a path between every two vertices in both directions. For example, C and D are strongly connected, while A and B are not.

Weighted graph: The edges of the graph are given weights.
insert image description here
Representation of the graph:

  1. adjacency matrix
  2. adjacency list

Adjacency matrix:
1. A two-dimensional array can be used to represent the structure of the graph.
2. When the graph is a sparse graph, a lot of space in this two-dimensional array will be assigned a value of 0, wasting computer storage space.
3. Schematic diagram of the adjacency matrix
insert image description here

说明:Represented by a two-dimensional array, if the two vertices are adjacent, then this value is recorded as 1, otherwise it is recorded as 0. This method may record a lot of 0, resulting in a waste of computer performance. Because even if this vertex has only one adjacent vertex, we are iterating a whole row. For example: the I vertex above.

Adjacency list: It consists of a vertex and a list of other vertices adjacent to this vertex, which can reduce memory consumption.
insert image description here
Create the graph using javascript:

class Graph {
    
    
  constructor(){
    
    
    this.vertices = []  // 用来保存顶点的数组
    this.adjList = {
    
    } // 用来保存边的字典,可以直接用一个对象代替。
  }
  // 添加顶点
  addVertex(v){
    
    
    this.vertices.push(v)
    this.adjList[v] = []
  }
  // 添加边
  addEdge(v,w){
    
    
    this.adjList[v].push(w)
    this.adjList[w].push(v)
  }
  // 返回字符串
  graphToString(){
    
    
    return this.vertices.reduce((r,v) => {
    
    
      return this.adjList[v].reduce((r,w) => {
    
    
        return r + `${
      
      w}`
      },`${
      
      r}\n${
      
      v} => `)
    },'')
  }
}
const graph = new Graph();

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'].forEach(c => graph.addVertex(c))

graph.addEdge('A', 'B')
graph.addEdge('A', 'C')
graph.addEdge('A', 'D')
graph.addEdge('C', 'D')
graph.addEdge('C', 'G')
graph.addEdge('D', 'G')
graph.addEdge('D', 'H')
graph.addEdge('B', 'E')
graph.addEdge('B', 'F')
graph.addEdge('E', 'I')

console.log(graph.graphToString())

insert image description here
Graph traversal: visit each vertex of the graph once, and there can be no repeated visits

  1. Breadth-first-search BFS
  2. Depth-first traversal (Depth-first-search DFS)

广度优先遍历:Queue, by storing vertices in the queue, the first vertex in the queue is explored first. The breadth-first search algorithm traverses the graph starting from the first specified vertex, and visits all its neighbors first, just like visiting one layer of the graph at a time. Simply put, it is to access vertices layer by layer, as shown in the figure below:
insert image description here
Implement breadth-first traversal in javascript:

breadthFirst(v, callback) {
    
    
    const read = []
    const adjList = this.adjList
    let pending = [v || this.vertices[0]]

    const readVertices = vertices => {
    
    
      vertices.forEach(key => {
    
    
        read.push(key)
        pending.shift()
        adjList[key].forEach(v => {
    
    
          if (!pending.includes(v) && !read.includes(v)) {
    
    
              pending.push(v)
          }
        })
        if (callback) callback(key)
        if (pending.length) readVertices(pending)
      })
    }
    readVertices(pending)
  }

insert image description here
深度优先遍历:The depth-first search algorithm will start from the first specified vertex to traverse the graph, follow the path until the last vertex of this path is visited, and then backtrack and explore the next path. In other words, it visits vertices in depth and then in breadth, as shown in the following figure:
insert image description here
Implementing depth-first traversal in javascript:

deepFirst(callback) {
    
    
    const read = []
    const adjList = this.adjList
    const readVertices = vertices => {
    
    
      vertices.forEach(key => {
    
    
        if (read.includes(key)) return false
        read.push(key)
        if (callback) callback(key)
        if (read.length !== this.vertices.length) {
    
    
          readVertices(adjList[key])
        }
      })
    }
    readVertices(Object.keys(adjList))
}

insert image description here
Algorithm:
Sorting Algorithm:
1.冒泡排序: Bubble Sort Compares any two adjacent items and swaps them if the first is greater than the second. In the worst case, compare n rounds, where n is the length of the array.

const arr = [2,4,1,9,8,7,6,5,3]
function bubbleSort(arr){
    
    
  if(!Array.isArray(arr)){
    
    
    console.log('请输入一个数组!')
    return false
  }
  const len = arr.length
  for(let i=0; i<len; i++){
    
    
    for(let j=0; j<len - i; j++){
    
    
      if(arr[j] > arr[j+1]){
    
    
        [arr[j], arr[j+1]] = [arr[j+1], arr[j]]
      }
    }
  }
  return arr
}
const res = bubbleSort(arr)  // [1, 2, 3, 4, 5,6, 7, 8, 9]

Simulation diagram:
insert image description here

2.快速排序

// 1. 选择基准值,一般选择数组中间的值并且向下取整
// 2. 定义左右两个数组,比基准值小的放在左边,比基准值大的放在右边
// 3. 递归左右两个数组
// 4. 终止条件是左右两个数组都只有一个元素
const arr = [49, 38, 65, 97, 76, 13, 27, 49]
function quickSort(arr){
    
    
  // 终止条件是左右两个数组都只有一个元素
  if(arr.length<2){
    
    
    return arr
  }
  // 定义左右两个数组
  const leftArr = [],
    rightArr = []
  
  // 获取基准值
  const index = Math.floor(arr.length/2)
  const baseValue = arr.splice(index,1)

  // 比基准值小的放在左边,比基准值大的放在右边
  for(let i of arr){
    
    
    if(i < baseValue){
    
    
      leftArr.push(i)
    }else{
    
    
      rightArr.push(i)
    }
  }

  // 递归左右两个数组
  return [...quickSort(leftArr), ...baseValue, ...quickSort(rightArr)]
}
const res = quickSort(arr)
console.log(res);  // [13, 27, 38, 49,49, 65, 76, 97]

3.选择排序:Find the smallest value in the data structure and put it first, then find the second smallest value and put it second, and so on.

// 选择排序:每次找到最小值放在最前面
const arr = [49, 38, 65, 97, 76, 13, 27, 49]
function choseSort(arr){
    
    
  for(let i=0; i<arr.length; i++){
    
    
    // 假设最小值元素对应的下标为 i
    let minIndex = i
    // 寻找最小值
    for(let j=i+1; j<arr.length; j++){
    
    
      if(arr[minIndex] > arr[j]){
    
    
        minIndex = j
      }
    }
    // 交换数据,第一轮交换第一个数和最小数的位置,以此类推
    [arr[i],arr[minIndex]] = [arr[minIndex],arr[i]]
  }
  return arr
}
const res = choseSort(arr)
console.log(res);  // [13, 27, 38, 49,49, 65, 76, 97]

4.插入排序:A real optimization of the number of loops is to always treat the left side of a set of unordered sequences as ordered.

// 1. 第一次是首位,因为只有一个元素
// 2. 第二次取出数组中第二个元素,从后向前遍历左侧的有序数列,找到第一个比这个数大的数,将取出来的数插到这个数的前面
// 3. 如果没有比取出来的数大的,则将这个数插在左侧有序数列的最后面
// 4. 重复步骤2,3,每次向后取一个元素
const arr = [49, 38, 65, 97, 76, 13, 27, 49]
function insertSort(arr){
    
    
  for(let i=1; i<arr.length; i++){
    
    
    // 取出来的目标数
    let targetNum = arr[i],j = i-1
    // 从后向前遍历
    while(arr[j] > targetNum){
    
    
      // 将目标数插入到比它大的数的前面
      arr[j+1] = arr[j];
      j--
    }
    // 没有比目标数大的数,则目标数就在原位,不需要改动
    arr[j+1] = targetNum
  }
  return arr
}
const res = insertSort(arr)
console.log(res);  // [13, 27, 38, 49,49, 65, 76, 97]

5.归并排序: Merge sort is a divide and conquer algorithm. The idea is to split the original array into smaller arrays until each small array has only one position, and then merge the smaller arrays into larger arrays until there is only one large array that is sorted.

// 合并两个有序数组
// 将待排序序列不断地二分,直到只有一个元素。
// 递归地将左右两个子序列进行归并排序。
// 合并两个有序子序列,生成一个新的有序序列。

// 合并地具体操作如下:
// 创建一个临时数组,用于存储合并结果。
// 比较左右两个子序列的第一个元素,将较小的元素放入临时数组,并将对应子序列的指针向后移动一位。
// 重复上述比较和放入操作,直到其中一个子序列为空。
// 将另一个非空子序列的剩余元素依次放入临时数组。
// 将临时数组的元素复制回原始数组相应位置。
function mergeSort(arr) {
    
    
  if (arr.length <= 1) {
    
    
    return arr;
  }
  
  const mid = Math.floor(arr.length / 2);
  const left = arr.slice(0, mid);
  const right = arr.slice(mid);
  
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    
    
  let result = [];
  let i = 0;
  let j = 0;
  
  while (i < left.length && j < right.length) {
    
    
    if (left[i] < right[j]) {
    
    
      result.push(left[i]);
      i++;
    } else {
    
    
      result.push(right[j]);
      j++;
    }
  }
  
  return result.concat(left.slice(i)).concat(right.slice(j));
}

// 示例用法
const arr = [5, 3, 8, 4, 2, 9, 1, 7, 6];
const sortedArr = mergeSort(arr);
console.log(sortedArr);  // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Search algorithm:
顺序搜索: The most basic search algorithm is to compare each element in the data structure with the element we are looking for. This search algorithm is very inefficient.

const arr = [5,4,3,2,1]
const arr = [5,4,3,2,1]
function sequentialSearch(arr, value){
    
    
  for(let i of arr){
    
    
    if(i == value)return '搜索到了'
  }
  return '没有搜索到'
}
const res = sequentialSearch(arr, 3)
console.log(res,'res==');  // 搜索到了

insert image description here

二分搜索

// 二分搜索
// 1. 要求被搜索的数据结构已经排序,选择中间值
// 2. 如果中间值等于目标值,则找到,算法执行完毕
// 3. 如果中间值大于目标值,则说明目标值在中间值的左侧,此时在左侧在找到中间值,在比较,以此类推
// 4. 如果中间值小于目标值,则说明目标值在中间值的右侧,此时在右侧在找到中间值,在比较,以此类推
const arr = [1,2,3,4,5,6,7,8]
function BinarySearchTree(arr, value){
    
    
  // arr是一个已经排序好的数组,如果是无序的先进行排序
  let left = 0
  let right = arr.length - 1
  while(left <= right){
    
    
    // 中间值的下标
    let mid = Math.floor((left + right) / 2)
    if(arr[mid] == value){
    
    
      return `找到了下标为:${
      
      mid}`
    }else if(arr[i] > value){
    
    
      right = mid -1
    }else{
    
    
      left = mid +1
    }
  }
  return '找不到目标值'
 }
  const res = BinarySearchTree(arr,5)  // '找到了下标为:4'
  const res = BinarySearchTree(arr,9)  // '找不到目标值'

Fibonacci sequence : the first two numbers are 1, and the third number is the sum of the first two numbers.

function fibonacci(n){
    
    
  // 要求n是大于0的整数
  if(n < 3){
    
    
    return 1
  }else{
    
    
    return fibonacci(n-1) + fibonacci(n-2)
  }
}
fibonacci(1)  // 1
fibonacci(2)  // 1
fibonacci(3)  // 2
fibonacci(6)  // 8

insert image description here
Factorial sequence : the current number is equal to multiplying from 1 to the current number. For example: f(3)=1 2 3

function factorial(n){
    
    
  // 要求n是大于0的整数
  if(n == 1){
    
    
    return 1
  }else{
    
    
    return factorial(n-1) * n
  }
}
factorial(1)  // 1
factorial(2)  // 2
factorial(3)  // 6
factorial(4)  // 24
factorial(5)  // 120

Divide and conquer : It decomposes a complex problem into multiple relatively simple and independent sub-problems, then recursively solves these sub-problems, and finally combines them to obtain the solution of the original problem. The specific steps are: Divide the original problem into
分解(Divide):multiple the same or similar sub-problems. This step is usually implemented using recursion.
解决(Conquer):Solve subproblems recursively. If the subproblems are small enough, they can be solved directly.
合并(Combine):Combine the solutions of the subproblems into the solution of the original problem.
For example: merge sort, quick sort

Dynamic programming : It decomposes a complex problem into multiple interrelated sub-problems, and solves the original problem by repeatedly solving the sub-problems.
Common dynamic programming problems are: Fibonacci sequence, climbing stairs, robbing houses, knapsack problem, longest common subsequence problem, coin change, all-source shortest path of graph.

爬楼梯问题:Suppose you are climbing stairs. It takes n steps before you can reach the top of the building. You can climb 1 or 2 steps at a time. How many different ways can you get to the top of the building?

// 动态规划,爬楼梯
// 示例 1:
// 输入:n=2
// 输出:2
// 解释:有两种爬楼梯的方法:
// 1. 1 阶 + 1 阶
// 2. 2 阶

// 示例 2:
// 输入:n = 3
// 输出:3
// 解释:有三种方法可以爬到楼顶。
// 1. 1 阶 + 1 阶 + 1 阶
// 2. 1 阶 + 2 阶
// 3. 2 阶 + 1 阶

// 解题思路:可以假设在第n-1个台阶爬一个台阶,或者在第n-2个台阶爬两个台阶。则问题就变为了:f(n)=f(n-1) + f(n-2)
// f(n-1) 为爬到第n-1个台阶的办法,f(n-2)为爬到第n-2个台阶的办法
// 时间复杂度O(n)
// 空间复杂度O(n)
function climbStairs(n){
    
    
  // 规定n是大于等于1的正整数
  if(n==1){
    
    
    return 1
  }else{
    
    
    // 存放f(n)的数组
    let dp = [1,1]
    for(let i=2;i<=n;i++){
    
    
      dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
  }
}

// 优化算法:变量替换数组
// 时间复杂度O(n)
// 空间复杂度O(1)
function climbStairs(n){
    
    
  if(n==1) return 1
  let dp0 = 1,dp1 = 1
  for(let i=2;i<=n;i++){
    
    
    [dp0, dp1] = [dp1, dp0 + dp1]
  }
  return dp1
}
climbStairs(5)  // 8
climbStairs(8)  // 34

取值问题: The maximum amount of money that can be obtained without passing through two adjacent rooms at the same time

// 示例 1:
// 输入:[1,2,3,1]
// 输出:4
// 解释:1 号房屋 (金额 = 1) ,然后 3 号房屋 (金额 = 3)。
//      获取到的最高金额 = 1 + 3 = 4 。

// 示例 2:
// 输入:[2,7,9,3,1]
// 输出:12
// 解释:1 号房屋 (金额 = 2), 3 号房屋 (金额 = 9),接着5 号房屋 (金额 = 1)。
//      获取到的最高金额 = 2 + 9 + 1 = 12 。

// 解题思路:到第 k 间房能获取到的最大金额,应该是前 k-2 间房获取到的最大金额加上第 k 间房的金额。
// 或者是 前 k-1 间房获取到的最大金额。
// 最终结果就是这两种情况下取最大值。
// f(k): 前 k 间房获取的最大值,a(k): 第 k 间房的金额。
// f(k) = Math.max(f(k-2) + a(k), f(k-1))
function getmoney(list){
    
    
  if(list.length == 0)return 0
  // 存放能获取到的最大金额的数组,i表示房间数
  let dp = [0, list[0]]
  for(let i=2;i<=list.length;i++){
    
    
    dp[i] = Math.max(dp[i-2] + list[i-1], dp[i-1])
  }
  return dp[list.length]
}
getmoney([2,5,3,1,9,4,7,6])  // 21
getmoney([2,8,3,1,9,4,7,6])  // 24

Greedy algorithm : 期望通过每一个阶段的局部最优解,从而得到全局最优解(it is just an expectation, and may not actually get the global optimal solution). It does not compute larger patterns like dynamic programming.

Similarities and differences between greedy algorithm and dynamic programming:
similarities:

  1. Both have optimal substructures, and either greedy selection strategies (greedy) or overlapping subproblems (dp).
  2. It can be understood that the greedy algorithm is a special dynamic programming algorithm.

difference:

  1. The greedy algorithm has less application scope and solves the local optimal solution. The optimal solution of each stage depends on the previous stage. The final solution obtained is the inference of all local optimal solutions, not necessarily the global optimal solution, and the solution process is faster, which is a top-down solution process.
  2. Dynamic programming: Similar to the exhaustive method, all feasible solutions are solved, and finally the global optimal solution is obtained through the backtracking method
  3. Greedy algorithm solving process:
    sort all candidate solutions according to certain rules;
    judge whether one is selected as the optimal solution according to the greedy selection strategy (a certain loop implementation).
  4. Dynamic programming solution process:
    ①Store all feasible solutions to the problem in a list;
    ②Cycle write recursive formula (the part that can be written recursively)
    ③Use the backtracking method to derive the optimal solution.
const coinArr = [1,0.5,5,2,10,50,20,100]
const amount = 43
function changeAmount(coins, amount){
    
    
  // 把钱币面额数组从大到小排序
  coins.sort((a,b)=>b-a)
  // 存储找零结果
  let result = []
  for (let i = 0;i<coinArr.length;i++){
    
    
    while(amount>=coins[i]){
    
    
      // 将最大面值放进数组
      result.push(coinArr[i])
      // 减去已经找零的金额
      amount -= coinArr[i]
    }
  }
  // 能找开的时候amount一定是等于零的
  if(amount>0){
    
    
    return '找不开零钱'
  }
  return result
}
console.log(changeAmount(coinArr,amount));

insert image description here

Guess you like

Origin blog.csdn.net/du_aitiantian/article/details/131556048