数据结构和算法再see

之前草草过了一遍数据结构,这次再来复习一下

时间复杂度:

执行当前算法所花费的时间

大O表示法

O(1) O(n) O(n2) O(llogn) ……

O(1),花费时间不受某个变量变化影响,除了循环递归意外,基本都是O1

1
2
3
4
5
6
7
8
9
10
11
12
const a = 1

const b = 2

function fn(num){

return num

}

fn(6)

O(n)

1
2
3
for(let i = 0;i < 100; i++){
console.log(i)
}

O(n平方)

1
2
3
4
5
for(let i = 0;i < 100; i++){
for(){

}
}

O(logN)

1
2
3
4
5
let i = 1
const n = 6
while(i < n){
i = i * 2
}

空间复杂度:

执行当前算法需要多少内存空间

大O表示法

O(1) O(n) O(n2) 。。。。。。

O(1)

1
let a = 1

O(n)

1
2
3
4
let arr = []
for(let i = 0;i < 100; i++){
arr.push(i)
}

O(n2平方)

1
2
3
4
5
6
7
let arr = []
for(let i = 0;i < 100; i++){
arr.push(i)
for(let j = 0;j < 100; j++){
arr[j].push(j)
}
}

1
2
3
4
后进先出
向后面添加元素,从后向前删除元素
上下电梯,最后一个进去,第一个出来
入栈出栈,栈底栈顶

栈-leetcode有效括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function fn(s){
let arr = []
for(let i = 0; i < s.length; i++){
const start = s[i]
if(s[i] === '(' || s[i] === '{' || s[i] === '['){
arr.push(s[i])
}else{
const end = arr[arr.length - 1]
if(start == ')' && end == '('
start == ']' && end == '['
start == '}' && end == '{'
){
arr.pop()
}else{
return false
}
}
}
return arr.length == 0
}
fn(str)

栈-leetcode删除字符串中所有相邻重复项

1
2
3
4
function fn(){

}
fn(str)

队列

先进先出

leetcode 933最近请求次数

1
2
3
4
class RecentCounter {

}

事件循环

js是一门单线程的浏览器脚本语言,因此诞生出了异步,同步任务会被推入堆或者执行栈,异步任务被推入异步任务队列,在执行栈中的任务完成后,会将异步队列中的任务依次推入执行栈执行,这就是事件循环

在异步任务中也分为宏任务和微任务,主线程查询任务队列,执行微任务,执行完微任务后,主线程查询任务队列,执行第一个宏任务,执行完毕后再查找所有微任务,再查找第一个宏任务

链表

多个元素组成的链表,不是按照顺序存储,而是按照next指针联系在一起的

js的原型链和链表结构相同

数组和链表区别

1:数组有序存储,删除或添加某个元素,其他元素会重新计算

2:链表不是按照顺序存储,没有下标,删除和添加元素不会影响其他元素

3:查找时,数组根据下标,链表只能从头或者从后找起

链表分为单向链表,双向链表,环形链表,,,,,,

instanceof原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let arr = [1,2,3]
console.log(arr instanceof Array) //true
console.log(arr instanceof Object) //true

const instanceof = (target,obj) => {
let p = target
while(p){
if(p == obj.prototype){
return true
}
p = p.__proto__
}
return false
}

console.log(instanceof([1,2,3],Array))

环形链表

leetcode-141环形链表

1
2
3
4
5
6
7
8
9
10
function hasCycle = function(head){
let f = head
let s = head
while( f! = null && f.next != null){
s = s.next
f = f.next.next
if( s = f ) return true
}
return false
}

leetcode-237删除链表中的节点

1
2
3
4
5
无法访问head,只能访问要删除的node,要删除的node不是末尾节点
function deleteEle(node){
node.val = node.next.val
node.next = node.next.next
}

leetcode-83删除排序链表中的重复元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
给一个head,升序序列,删除所有重塑元,使每个元素只出现一次
function deleteEle(head){
//我写的
let p = head
while( p.val = p.next.val ){
p.next.val = p.next.next.val
p.next.next = p.next.next.next
}
//视频写的
if(!head){
return head
}
let cur = head
while(cur.next){
if(cur.val = cur.next.val){
cur.next = cur.next.next
}else{
cur = cur.next
}
}
return head
}

leetcode-206反转链表

1
2
3
4
5
6
7
8
9
10
11
function reserve(head){
let prev = null
let curr = head
while(curr){
const next = curr.next
curr.next = prev
prev = curr
curr = next
}
return prev
}

字典和哈希表

字典

键值对的存储,类似js对象,js对象的键在某些情况会转换成对象类型,字典类型键不会转换类型

1
2
3
4
5
6
7
let map = new Map()
map.set('key',123)
map.get('key')
map.has('key')
map.delete('key')
map.clear()
console.log(map.size) //length

哈希表

和字典相比,省去了按照插入顺序排列的查找

又称为散列表

基于数组,可以通过下标hash值访问元素

js没有哈希表,哈希表是字典的一种实现

字典是根据添加顺序排列的,哈希表不是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
实现哈希表
class Hash {
constructor{
this.table = []
}
hashCode(key){
let hash = 0
for(let i = 0; i < key.length; i++){
hash += key.charCodeAt(i)
}
return hash
}
put(key,value){
let hashKey = this.hashCode(key)
this.table[hashKey] = value
}
get(key){
let hashKey = this.hashCode(key)
return this.table[hashKey]
}
}
let hash = new Hash()

leetcode哈希表1两数之和

1
2
3
4
5
6
7
8
9
10
11
给定一个数组,给定一个目标值,求数组中和为目标值的两个整数,返回下标
function computed(nums, target){
let map = new Map()
for(let i = 0; i < nums.length; i++){
num = target - nums[i]
if(map.has(num)){
return [map.get(num),i]
}
map.set(nums[i],i)
}
}

leetcode哈希表存在重复元素

1
2
3
4
5
6
7
8
9
10
11
12
给定一个数组,判断是否存在重复元素
如果一个值在该数组中至少出现两次,函数返回true,如果数组中每个元素都不相同,返回false
function same(nums){
let map = new Map()
for(let i = 0; i < nums.length;i++){
if(map.has(nums[i])){
return true
}
map.set(nums[i],i)
}
return false
}

leetcode哈希表349两个数组交集

1
2
3
4
5
给定两个数组,编写函数计算交集
function com(nums1,nums2){
let set = new Set(nums)
return [...new Set(nums1)].filter(val => set.has(val))
}

判断一个字符串中出现最多的字符并统计次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function num(str){
let maxNum = 0
let maxStr = ''
let map = new Map()
for(let item of str){
map.set(item, (map.get(item) || 0) + 1)
}
for(let [key,value] of map){
if(value > maxNum){
maxNum = value
maxStr = key
}
}
return [maxStr,maxNum]
}

leetcode哈希表1207独一无二的出现次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
给予一个都是整数项的数组,统计每个项出现的次数,如果每个数出现的次数都是独一无二的,返回true,否则返回false
function U(arr){
const map = new Map()
for(let item of arr){
if(map.has(item)){
map.set(item,map.get(item) + 1)
}else{
map.set(item,1)
}
}
const set = new Set()
for(let [key,value] of map){
set.add(value)
}
return set.size == map.size
}

leetcode 03无重复字符的最长子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
给定一个字符串s,找出其中不含重复字符的最长子串的长度
如果是aassdd
去重后卫asd,最长子串为3
function Max(s){
自己想的两种方式,但是不知道为什么无法通过测试用例,我自己在浏览器调试的时候是可以的
// 后续搞明白了,没注意看题目,是最长子串,不是最长序列,题目我也没整明白,整成了去重后的长度,放这里记录一下错误
1
// let map = new Map()
// let arr = s.split('')
// for(let i = 0; i < arr.length; i++){
// if(map.has(arr[i])){
// continue
// }else{
// map.set(arr[i],(map.get(arr[i]) || 0) + 1)
// }
// }
// return map.size

2
// let set = new Set()
// for(let i = 0; i < s.length; i++){
// set.add(s[i])
// }
// return set.size

涉及到了滑动窗口的概念
let map = new Map()
let l = 0
let num = 0
for(let i = 0; i < s.length; i++){
if(map.has(s[i]) && map.get(s[i]) >= l){
l = map.get(s[i]) + 1
}
num = Math.max(num,i - l + 1)
map.set(s[i],i)
}
return num
}

分层数据关系

深度优先遍历

从根节点出发,尽可能深的搜索树的节点,往一个节点深处遍历,遍历完后再遍历下一个节点一直遍历

技巧

1:访问根节点

2:对根节点children挨个进行深度优先遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const tree = {
val:'a',
children:[{
val:'b',
children:[{
val:'d',
children:[{
val:'e',
children:[]
}]
}]
},{
val:'c'
}]
}

const fn = (root) => {
//if(root.children){
console.log(root.val)
root.children.forEach(fn)
//}
}


fn(tree)

广度优先遍历

优先查找离根节点最近的节点,再查找最近的节点的子节点

技巧:

1:新建一个队列,吧根节点入队列

2:吧队头出队

3:吧对头的children挨个入队

4:重复2,3

1
2
3
4
5
6
7
8
9
10
function fn = (root) => {
const arr = [root]
while(arr.length > 0){
const obj = arr.shift()
console.log(obj.val)
obj.forEach(item => {
arr.push(item)
})
}
}

二叉树前序遍历

多叉树

dom结构

二叉树

子节点只会有两个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const tree = {
val:'a',
left:[{
val:'b',
left:[],
right:[]
}],
right:[{
val:'c',
left:[{
val:'d';
left:[],
right:[]
}],
right:[]
}]
}

leetocde144二叉树前序遍历前序遍历/先序遍历

根,左,右

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function tree(root){
1:递归版
let arr = []
let fn = (node) => {
if(node){
//先根节点
arr.push(node.value)
//遍历左子树
fn(node.left)
//遍历右子树
fn(node.right)
}
}
fn(root)
return arr //[1,2,3,4,5]

//2:通过栈的形式,非递归版
if(!root)return []
let arr = []
let stack = [root]
while(stack.length){
let o = stack.pop()
arr.push(o.val)
p.right && stack.push(o.right)
p.left && stack.push(o.left)
}
return arr
}

leetcode94二叉树中序遍历

左,根,右

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function fnn(root){
1:递归版
const arr = []
const fn = ( root ) => {
if(!root)return
fn(root.left);
arr.push(root.val)
fn(root.right)
}
fn(root)
return arr

2:非递归版
const arr = []
const stack = []
let o = root
while(stack.length || o){
while(o){
stack.push(o)
o = o.left
}
const n = stack.pop()
arr.push(n.value)
o = n.right
}
return arr
}

leetcode145二叉树后序遍历

左右根

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function tree(root){
1:递归版
const arr = []
const fn = (node) => {
if(node){
fn(node.left)
fn(node.right)
arr.push(node.value)
}
}
fn(root)
return arr

2:
if(!root)return []
const arr = []
const stack = [root]
while(stack.length){
const o = stack.pop()
arr.unshift(o.value)
o.left && stack.push(o.left)
o.right && stack.push(o.right)
}
return arr
}

leetcode111二叉树最小深度

1
2
3
4
5
6
7
8
9
10
11
12
function tree(root){
if(!root)return 0
const stack = [ [root,1] ]
while(stack.length){
const [o,n] = stack.shift()
if(!o.left || !o.right){
return n
}
if(o.left)stack.push([o.left,n+1])
if(o.right)stack.push([o.right,n+1])
}
}

leetcode104二叉树最大深度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function tree(root){
if(!root)return 0
const stack = [root]
let num = 0
while(stack.length){
let len = stack.length
num++
while(len--){
const o = stack.shift()
o.left && stack.push(o.left)
o.right && stack.push(o.right)
}
}
return num
}

leetcode104翻转二叉树

1
2
3
4
5
6
7
8
9
function tree(root){
if(root == null)return null
let tem = root.left
root.left = rot.right
root.right = tem
tree(root.left)
tree(root.right)
return root
}

leetcode100相同的树

1
2
3
4
5
6
7
判断两个树结构相同,值相同
function tree(p,q){
if(p == null && q == null)return true
if(p == null || q == null)return false
if(p.val != q.val)return false
return tree(p.left,q.left) && tree(p.right,q.right)
}

loading……