数据结构与算法--javascript(持续更新中...)

一. 概论

1. 数据结构
队列:一种遵循先进先出 (FIFO / First In First Out) 原则的一组有序的项;队列在尾部添加新元素,并从头部移除元素。最新添加的元素必须排在队列的末尾。(例如:去食堂排队打饭,排在前面的人先打到饭,先离开;排在后面的人后打到饭,后离开。)
在这里插入图片描述
栈:一种遵从先进后出 (LIFO) 原则的有序集合;新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另一端为栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。(例如:往口袋里面装东西,先装进去的放在最下面,后装进去的放在最上面,取出的时候只能从上往下取。)
在这里插入图片描述
在这里插入图片描述
链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的;每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针/链接)组成。
在这里插入图片描述
集合:由一组无序且唯一(即不能重复)的项组成;这个数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中。

字典:以 [键,值] 对为数据形态的数据结构,其中键名用来查询特定元素,类似于 Javascript 中的Object。

哈希表:根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中某个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

树:由 n(n>=1)个有限节点组成一个具有层次关系的集合;把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的,基本呈一对多关系,树也可以看做是图的特殊形式。

图:图是网络结构的抽象模型;图是一组由边连接的节点(顶点);任何二元关系都可以用图来表示,常见的比如:道路图、关系图,呈多对多关系。

2. 算法
排序算法:
冒泡排序(升序):逐一比较相邻两个元素,如果前面的元素比后面的元素大则交换两者顺序;元素项向上移动至正确的顺序,好似气泡上升至表面一般,因此得名。(冒泡排序每一轮至少有一个元素会出现在正确位置)

快速排序(升序):选择一个基准值,每一个元素与基准值比较。比基准值小的元素放在基准值左边,比基准值大的元素放在基准值右边,左右两边递归执行此操作。通常选择中间的元素作为基准值。

选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,以此循环,直至排序完毕。

插入排序:将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,此算法适用于少量数据的排序。

归并排序:将原始序列切分成较小的序列,只到每个小序列无法再切分,然后执行合并,即将小序列归并成大的序列,合并过程进行比较排序,只到最后只有一个排序完毕的大序列,时间复杂度为 O(n log n)。
在这里插入图片描述
各种时间复杂度的直观比较:
在这里插入图片描述
在这里插入图片描述
搜索算法:
顺序搜索:让目标元素与列表中的每一个元素逐个比较,直到找出与给定元素相同的元素为止,缺点是效率低下。

二分搜索:在一个有序列表,以中间值为基准拆分为两个子列表,拿目标元素与中间值作比较从而再在目标的子列表中递归此方法,直至找到目标元素。

其他算法:
贪心算法:在对问题求解时,不考虑全局,总是做出局部最优解的方法。

动态规划:在对问题求解时,由以求出的局部最优解来推导全局最优解。

复杂度概念:一个方法在执行的整个生命周期,所需要占用的资源,主要包括:时间资源、空间资源。

二. 数据结构

队列
概念:一种遵循先进先出 (FIFO / First In First Out) 原则的一组有序的项;队列在尾部添加新元素,并从头部移除元素。最新添加的元素必须排在队列的末尾。

在 Javascript 中实现一个队列类。
1.创建一个类:

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.使用类:

const queue = new Queue()  // 类的实例化
queue.isEmpty  // truequeue.enqueue('John')   // {items: ['John']}
queue.enqueue('Jack')   // {items: ['John','Jack']}
queue.enqueue('Camila')  // {items: ['John','Jack','Camila']}queue.size  // 3
queue.isEmpty  // falsequeue.dequeue()   // John
queue.dequeue()   // Jack
queue.dequeue()   // Camila

优先队列
概念元素的添加和移除是基于优先级的,不在满足完全意义的先进先出。例如机场登机的顺序,头等舱和商务舱乘客的优先级要高于经济舱乘客,这些乘客不需要通过正常排队登机。或是去医院看病,医生也会根据病情程度优先处理病情严重的。

在 Javascript 中实现一个队列类:
1.创建一个类:

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.使用类:

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}]}

循环队列
概念:为充分利用向量空间,克服"假溢出"现象将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。通俗来说:循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
假溢出: 队列的空间未利用完,但是却造成了元素的溢出。
在这里插入图片描述
基于首次实现的队列类,简单实现一个循环引用的示例:

class LoopQueue extends Queue {constructor(items) {super(items)}getIndex(index) {const length = this.items.lengthreturn index > length ? (index % length) : index}find(index) {return !this.isEmpty ? this.items[this.getIndex(index)] : null}
}

使用:

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'

栈:
概念:一种遵从先进后出 (LIFO) 原则的有序集合;新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另一端为栈底,出去的时候从栈顶开始出去。在栈里,新元素都靠近栈顶,旧元素都接近栈底。`

用javascript基于数组的方法实现一个栈的功能:

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())}
}

使用栈:

// 实例化一个栈
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

链表:
概念:存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的;每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针/链接)组成
分类:单向链表,双向链表,循环链表
链表和数组的区别
* 数组在添加或者删除元素的时候需要移动其他元素,链表不需要。链表需要使用指针,因此使用的时候需要额外注意一下。
* 数组可以访问其中任何一个元素,链表需要从头开始迭代,直到找到所需的元素。

链表的常见方法:
1、append(element):向列表尾部添加一个新的项
2、insert(position, element):向列表的特定位置插入一个新的项。
3、remove(element):从列表中移除一项。
4、removeAt(position):从列表的特定位置移除一项。
5、indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
6、getElementAt(index): 返回链表中特定位置的元素, 如果不存在这样的元素则返回undefined
7、isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
8、size():返回链表包含的元素个数。与数组的length属性类似。
9、toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值element。

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

单向链表:
在这里插入图片描述
生活中的例子:火车就可以看做链表,每一节都是由一节车厢和车厢之间的连接带组成,这个连接带就可以看成是指针。
在这里插入图片描述
用javascript来描述一个单向链表:

// 链表节点
class Node {constructor(element) {this.element = elementthis.next = null}
}// 单向链表
class LinkedList {constructor() {this.head = nullthis.length = 0}// 1. 追加元素// 向链表尾部追加数据可能有两种情况:// 链表本身为空, 新添加的数据是唯一的节点.// 链表不为空, 需要向其他节点后面追加节点.append(element) {const node = new Node(element)let current = null// 链表本身为空, 新添加的数据是唯一的节点.if (this.head === null) {this.head = node} else {// 链表不为空, 需要向其他节点后面追加节点.current = this.headwhile(current.next) {current = current.next}current.next = node}this.length++}// 2. 任意位置插入元素insert(position, element) {// 1.检测越界问题: 越界插入失败, 返回falseif (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 = currentthis.head = newNode} else {while (index++ < position) { // 向前赶, 直到找到当前位置positionprevious = currentcurrent = current.next}// index === position, 找到要插入的位置newNode.next = currentprevious.next = newNode}// 4.length+1this.length++}// 3. 从链表中任意移除一项removeAny(position) {//边界检查,越界返回falseif(position < 0 || position > this.length-1){return false}let current = this.headlet previous = nulllet index = 0// 如果删除第一个元素if(position == 0){this.head = current.next}else{// 不是删除第一个找到删除的位置while(index++ < position){previous = currentcurrent = current.next}previous.next = current.next}this.length--return current.element}// 4. 寻找元素下标findIndex(element) {let current = this.headlet index = 0//遍历链表直到找到data匹配的positionwhile (current) {if (element === current.element) {return index}current = current.nextindex++}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.headlet str = ''while (current) {str += ` ${current.element}`current = current.next}return str}
}

链表类的使用:

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'

双向链表:
概念:双向链表和普通链表的区别在于,在链表中, 一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素, 另一个链向前一个元素,如下图所示:
在这里插入图片描述
使用javacsript来实现一个双向链表类:

class Node{constructor(element){this.element = elementthis.next = null this.prev = null }
}// 双向链表
class DoublyLinkedList {constructor() { this.head = nullthis.tail = nullthis.length = 0}// 1. 任意位置插入insert(position,element){// 1.1 检测越界问题: 越界插入失败, 返回falseif (position < 0 || position > this.length) return falselet 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 = nodenode.next = currentcurrent.prev = node}}// 1.3 如果插在了末位else if(position == this.length) {this.tail = nodecurrent.next = nodenode.prev = current}else {// 如果插在了中间位置// 找到插入的位置while(idex++ < position){previous = currentcurrent = current.next}// 插入进去node.next = currentprevious.next = nodenode.pre = previouscurrent.pre = nodethis.length++return true}}// 2. 移除任意位置元素removeAny(position){// 2.1 边界检查,越界返回falseif(position < 0 || position > this.length-1) return falselet current = this.headlet previous = nulllet index = 0// 2.2 如果删除的是首位if(position == 0){this.head = current.nextthis.head.prev = null}else if(position == this.length - 1){// 2.3 如果删除的是末位this.tail = current.prethis.tail.next = null }else {// 2.4 中间项// 找到删除的位置while(index++ < position){previous = currentcurrent = current.next}previous.next = current.nextcurrent.prev = current.next.prevthis.length--return current.element}} 
}

循环链表:
概念:循环链表可以像单向链表一样只有单向引用,也可以向双向链表一样具有双向引用。
单向循环链表:最后一个元素指向下一个元素的指针(tail.next)不是引用null, 而是指向第一个元素(head),如下图所示:
在这里插入图片描述
双向循环链表:最后一个元素指向下一个元素的指针tail.next不是nul,而是指向第一个元素(head),同时第一个元素指向前一个元素的指针head.prev不是null,而是最后一个元素tail。如图所示:
在这里插入图片描述
链表的优势:无需移动链表中的元素,就能轻松地添加和移除元素。因此,当你需要添加和移除很多元素 时,最好的选择就是链表,而非数组。

集合:
概念:集合是由一组无序且唯一(不能重复)的项组成的。目前在ES6中已经内置了Set的实现。
集合中常用的一些方法
add() 添加元素
delete() 删除元素,返回布尔值,删除成功返回true,删除失败返回false
has() 判断是否含有某个元素,返回布尔值
clear() 清空set数据结构
size 没有括号,返回此结构长度

在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

在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] = valuereturn 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) // []

对集合可以执行如下操作:
并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。

并集:
并集的数学概念:集合A和B的并集,表示为A∪B,定义如下:A∪B = { x | x∈A V x∈B },意思是x(元素)存在于A中,或x存在于B中。如图:

在这里插入图片描述
基于刚才的 Set 类实现一个并集方法:

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
}

交集:
并集的数学概念:集合A和B的交集,表示为A∩B,定义如下:A∩B = { x | x∈A ∧ x∈B },意思是x(元素)存在于A中,且x存在于B中。如图:

在这里插入图片描述
基于刚才的 Set 类实现一个交集方法:

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

差集:
差集的数学概念:集合A和B的差集,表示为A-B,定义如下:A-B = { x | x∈A ∧ x∉B },意思是x(元素)存在于A中,且不x存在于B中。如图:
在这里插入图片描述
基于刚才的 Set 类实现一个差集 A-B 的方法:

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

子集:
子集的数学概念:集合A是B的子集,或者说集合B包含了集合A,如图:
在这里插入图片描述
基于刚才的 Set 类实现一个子集方法:

// 在这里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))}
}

字典:
集合、字典、散列表都可以存储不重复的数据。字典以键值对的形式存储数据,类似于javascript中的Object对象。

在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}, [])}
}

使用字典:

const dictionary = new Dictionary()
dictionary.set('Wangjiajia', 'Wangjiajia@email.com')
dictionary.set('Wangtongtong', 'Wangtongtong@email.com')
dictionary.set('davide', 'davide@email.com') console.log(dictionary)  // {items:{'Wangjiajia', 'Wangjiajia@email.com','Wangtongtong', 'Wangtongtong@email.com','davide', 'davide@email.com'}}
console.log(dictionary.keys)  // ['Wangjiajia','Wangtongtong','davide']
console.log(dictionary.values) // [Wangjiajia@email.com','Wangtongtong@email.com','davide@email.com']
console.log(dictionary.items)  // {'Wangjiajia': 'Wangjiajia@email.com','Wangtongtong': 'Wangtongtong@email.com','davide':'davide@email.com'}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15832.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[网络安全提高篇] 一二一.恶意软件动态分析Cape沙箱Report报告的API序列批量提取详解

终于忙完初稿,开心地写一篇博客。 “网络安全提高班”新的100篇文章即将开启,包括Web渗透、内网渗透、靶场搭建、CVE复现、攻击溯源、实战及CTF总结,它将更加聚焦,更加深入,也是作者的慢慢成长史。换专业确实挺难的,Web渗透也是块硬骨头,但我也试试,看看自己未来四年究…

超细,设计一个“完美“的测试用例,用户登录模块实例...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 好的测试用例一定…

【Windows】Redis集群部署

集群是如何进行工作的 Redis采用哈希槽来处理数据与节点之间的映射关系&#xff0c;一个集群共有16384 个哈希槽&#xff0c;每个key通过 CRC16算法计算出一个16bit的值&#xff0c;再对16384取模&#xff0c;得到对应的哈希槽&#xff0c;集群通过维护哈希槽与节点的关系来得…

vue开发:vue的插槽功能讲解

vue的插槽 举一个生活中的例子&#xff1a;比如装修房子的时候我们会在很多地方预留出一些插孔&#xff0c;可能要插电冰箱&#xff0c;插电式&#xff0c;插充电器等&#xff0c;反正就是你觉得预留在这个位置的插座一定有用&#xff0c;这个预留的插座就类似我们今天要说的插…

Meta为全天候AR眼镜设计了AI系统的八大指导方针

众所周知&#xff0c;Meta不仅局限在Quest这类VR头显上&#xff0c;同时还在打造更轻量化的AR眼镜&#xff0c;目标就是让产品更好的融入到人们的日常生活中去。除了硬件上轻量化以外&#xff0c;在功能和交互体验上也至关重要&#xff0c;例如自然交互方式&#xff0c;比如手势…

【算法】十大排序算法以及具体用例算法题

文章目录 1:冒泡排序2:选择排序3:插入排序4:希尔排序5:堆排序6:计数排序7:基数排序8:快速排序9:归并排序10:桶排序 源代码下载 1:冒泡排序 /** 冒泡排序是内部排序* 冒泡排序将会每一次都从头开始遍历* 每一次遍历都会把最大的数据放到最后一个* 因此每一次都可以少遍历一个元…

透彻理解 UART 通信的基本方法

UART是一种异步全双工串行通信协议&#xff0c;由 Tx 和 Rx 两根数据线组成&#xff0c;因为没有参考时钟信号&#xff0c;所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数&#xff0c;从而按照相同的速率进行通信。 异步通信以一个字符为传输单位…

MySQL性能瓶颈定位慢查询

目录 1 性能优化的思路2 引言3 MySQL慢查询日志3.1 慢查询参数3.2 开启慢查询日志&#xff08;临时&#xff09;3.3 开启慢查询日志&#xff08;永久&#xff09;3.4 慢查询测试 4 MySQL性能分析 EXPLAIN4.1 概述4.2 EXPLAIN字段介绍4.2.1 id字段4.2.2 select_type 与 table字段…

mac苹果电脑,怎么把mkv转换mp4格式

mac苹果电脑&#xff0c;怎么把mkv转换mp4格式&#xff1f;如果你是一名mac苹果电脑的用户&#xff0c;在电脑上下载到mkv格式的视频后会发现它使用起来非常的麻烦&#xff0c;甚至不能直接打开播放。mkv其实也是一种时间比较久远的视频文件格式&#xff0c;但是不知道是什么原…

责任链模式

责任链模式 概述优缺点应用场景Java 代码示例Spring 代码示例场景一场景二场景三 概述 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它通过将请求的发送者和接收者解耦&#xff0c;使多个对象都有机会处理请求。在这个…

RabbitMQ ---- 消息队列

RabbitMQ ---- 消息队列 1. MQ 的相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 的分类1.4 MQ 的选择 2. RabbitMQ2.1 RabbitMQ 的概念2.2 四大核心概念2.3 RabbitMQ 核心部分2.4 各个名词介绍2.5 安装 1. MQ 的相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面…

ARM架构(寄存器点灯)

文章目录 前言一、LED原理图二、使用寄存器点灯的步骤三、如何操作寄存器四、实际操作1.使能GPIO端口2.将引脚设置为输出模式3.设置输出状态 五、全部代码总结 前言 本篇文章我们来讲解一下如何使用寄存器点亮一个LED灯&#xff0c;一般对于新人来说都是使用HAL库或者标准库来…