JavaScript 数据结构与算法

201 阅读6分钟

写在前面

改变原始数组

  1. 斐波那契
function Fibonacci(n) {
  if (n==1 || n == 2){
    return 1
  }
  return Fibonacci(n-1)+Fibonacci(n-2)
}

console.log(Fibonacci(30))
//非递归方法
function Fibonacci(num){
    var n1 = 1, n2 = 1 , n= 1
    for(var i = 2; i < num; i++){
      n = n1 + n2
      n1 = n2
      n2 = n
 }
 return n
}

0 字符串

var str = 'hello, july'
var a

a = str.length//11
a = str[0]//h

a = str.concat('hi','cool')//hello, julyhicool

a = str[0]//h
a = str.indexOf('h')//0, 如果没有就是  -1
a = str.lastIndexOf('h')

a = str.slice(0,2)//he

a = Array.isArray(a)//true
a = str.toUpperCase()//HELLO, JULY
a = str.toLowerCase()//hello, july

//字符串变数组
a = str.split(',')//[ 'hello', ' july' ]
a = str.split('')//['h', 'e', 'l', 'l', 'o', ',', ' ', 'j', 'u', 'l', 'y']
//数组变字符串
a = a.join('')//hello, july
a = a.toString()//hello, july

1 数组

 var n = [1,23,4,45,345,2,1]
 n.push(11)
 n.push(14,24)
 n.unshift(-2)//在首位添加
 n.pop()
 n.shift()//从首位删除
 console.log(n.splice(1,3))
 var n= b.concat(a,c)
 let ncopy = Array.of(...n)
 let one = Array(6).fill(1)
 console.log(num.reverse())
 console.log(num.sort())
 console.log(n.indexOf(2))
 console.log(n.includes(999))
 console.log(n.toString())
 var b= n.join('-')

获取数组中重复的数和次数

目标参数若是字符串,可通过split()方法转换为数组

var str="qwertyuiopasdfghjklzxcvbnmqazwsxaswazaaa";
var arr=str.split("");  // 转换为数组

function moreValue() {
  if (!arr) return false;
  if (arr.length === 1) return 1;
  let result = {}
  let maxNum = 0;//元素出现的次数
  let maxValue = null;//最大的对应的maxValue值
  // 循环对象,取出value值最大的对应的maxValue值
  for(let i = 0; i < arr.length;i++) {
    let val = arr[i]
    // 先循环数组,把元素作为key值,元素出现的次数为value值,塞进result对象中
    //循环数组塞进对象的操作用三元表达式判断,合并为一个for循环
    result[val] === undefined ? result[val] = 1 : result[val]++;
    if(result[val] > maxNum) {
      maxNum = result[val];
      maxValue = val
    }
  }
  return maxValue +','+ maxNum;
}

数组去重(三种方法)

function unique(arr) {
  var a = []
  a[0] = arr[0]

  for (let i = 0; i < arr.length; i++) {
    for (let k = 0; k < a.length; k++) {
      if (a[k] == arr[i]) {
        break
      }
      if (k == a.length-1) {
        a.push(arr[i])
      }
    }
  }
  return a
}

数组拍平

function fn(arr) {
  var arr1 = []
  arr.forEach(val =>{
    if ( val instanceof Array) {
      arr1 = arr1.concat(fn(val))
    } else {
      arr1.push(val)
    }
  })
  return arr1
}

输入二维数组和一个整数,判断二维数组中是否含有该整数

const arr = [1,5,9;3,8,12;6,9,19]; function hasNumber(array,target){}

解法 1:暴力法 遍历数组中的所有元素,找到是否存在。

时间复杂度是 O(N^2),空间复杂度是 O(1)

function Find(target, array) {
    const rowNum = array.length;
    if (!rowNum) {
        return false;
    }
    const colNum = array[0].length;
    for (let i = 0; i < rowNum; i++) {
        for (let j = 0; j < colNum; j++) {
            if (array[i][j] === target) return true;
        }
    }

    return false;
}

解法 2:观察数组规律

按照题目要求,数组的特点是:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。考虑以下数组:

1 2 3

4 5 6

7 8 9

在其中寻找 5 是否存在。过程如下:

从右上角开始遍历当前元素小于目标元素(3 < 5),根据数组特点,当前行中最大元素也小于目标元素,因此进入下一行当前元素大于目标元素(6 > 5),根据数组特点,行数不变,尝试向前一列查找找到 5

时间复杂度是 O(M+N),空间复杂度是 O(1)。其中 M 和 N 分别代表行数和列数。

function Find(target, array) {
    const rowNum = array.length;
    if (!rowNum) {
        return false;
    }
    const colNum = array[0].length;
    if (!colNum) {
        return false;
    }

    let row = 0,
        col = colNum - 1;
    while (row < rowNum && col >= 0) {
        if (array[row][col] === target) {
            return true;
        } else if (array[row][col] > target) {
            --col;
        } else {
            ++row;
        }
    }

    return false;
}

两个数组的交集和并集

  1. 对 Array 进行扩展
//数组功能扩展
//数组迭代函数
Array.prototype.each = function(fn){
 fn = fn || Function.K;
 var a = [];
 var args = Array.prototype.slice.call(arguments, 1);
 for(var i = 0; i < this.length; i++){
 var res = fn.apply(this,[this[i],i].concat(args));
 if(res != null) a.push(res);
 }
 return a;
};
//数组是否包含指定元素
Array.prototype.contains = function(suArr){
 for(var i = 0; i < this.length; i ++){
 	if(this[i] == suArr){
	 	return true;
	 }
 }
 return false;
}
//不重复元素构成的数组
Array.prototype.uniquelize = function(){
 var ra = new Array();
 for(var i = 0; i < this.length; i ++){
	 if(!ra.contains(this[i])){
	 	ra.push(this[i]);
	 }
 }
 return ra;
};
//两个数组的交集
Array.intersect = function(a, b){
 return a.uniquelize().each(function(o){return b.contains(o) ? o : null});
};
//两个数组的差集
Array.minus = function(a, b){
 return a.uniquelize().each(function(o){return b.contains(o) ? null : o});
};
//两个数组的补集
Array.complement = function(a, b){
 return Array.minus(Array.union(a, b),Array.intersect(a, b));
};
//两个数组并集
Array.union = function(a, b){
 return a.concat(b).uniquelize();
};

2 filter 它用于把Array的某些元素过滤掉,然后返回剩下的元素。

和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

回调函数

filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身:

var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
//交集
var intersect = a.filter(function(v){ return b.indexOf(v) > -1 })
//差集
var minus = a.filter(function(v){ return b.indexOf(v) == -1 })
//补集
var complement = a.filter(function(v){ return !(b.indexOf(v) > -1) })
  .concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}))
//并集
var unionSet = a.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}));

console.log("a与b的交集:", intersect);
console.log("a与b的差集:", minus);
console.log("a与b的补集:", complement);
console.log("a与b的并集:", unionSet);

合并两个数组并排序

  • 合并两个数组并排序
var a =[2,5,8,9];
var b=[7,9,7,9]
var c = a.concat(b).sort(function(a,b){return a-b }//升序
//回调函数里面返回值如果是:参数1-参数2;升序;  

  • 两个有序数组,合并后重新排序js实现

利用两个数组已经排好序这个条件,设置两个指针,分别指向两个数组

当其中一个小于另外一个就将小的数push到新数组中,后移指针

相等的话则全push到新数组中,两个指针全后移

直到一个指针指到数组结尾,将另一个数组直接加入到新数组中即可

let sortArr = (arr1, arr2) => {
  let [i,j] = [0,0];
  let newArr = [];
 
  while (i < arr1.length && j < arr2.length) {
    if (arr1[i] < arr2[j]) {
      newArr.push(arr1[i]);
      i++;
    } else if (arr1[i] > arr2[j]) {
      newArr.push(arr2[j]);
      j++;
    } else if (arr1[i] === arr2[j]) {
      newArr.push(arr1[i]);
      newArr.push(arr2[j]);
      i++, j++;
    }
  }
 
  // 将指针未移到末尾的部分取出,拼到新数组后面
  if (i < arr1.length) {
    return newArr.concat(arr1.splice(i));
  } else if (j < arr2.length) {
    return newArr.concat(arr2.splice(j));
  } else {
    return newArr;
  }
};
 
let arr1 = [1, 2, 3, 4, 6, 9, 11];
let arr2 = [2, 4, 6, 8, 12, 19, 33, 45];
console.log(sortArr(arr1, arr2));

数组的反转

截屏2021-06-13 10.12.41.png

截屏2021-06-13 10.12.23.png

//反转数组
function reverse(arr) {
  for (let i = 0; i < arr.length/2; i++) {
    [arr[i],arr[arr.length-1-i]]=[arr[arr.length-1-i],arr[i]]
  }
  return arr
}

对数组的遍历有那些api、迭代器函数 filter() forEach()some() every() map()

  • filter()
//   对数组中每一项运行回调函数,该函数返回结果是true的项组成的新数组
//     新数组是有老数组中的元素组成的,return为ture的项;
例:
    var arr = [111,222,333,444,555];
    var newArr = arr.filter(function (element, index, array) {
        //只要是奇数,就组成数组;(数组中辨别元素)
        if(element%2 === 0){
            return true;
        }else{
            return false;
        }
    })
 
    console.log(newArr); // [222, 444]
  • forEach()
// 和for循环一样;没有返回值;
例:
    var arr = [111,222,333,444,555];
    var sum = 0;
    var aaa = arr.forEach(function (element,index,array) {
        console.log(element); // 输出数组中的每一个元素
        console.log(index); // 数组元素对应的索引值
        console.log(array); // 数组本身 [111, 222, 333, 444, 555]
        sum += element; //数组中元素求和;
    });
    console.log(sum); // 数组元素加起来的和
    console.log(aaa);//undefined;没有返回值 所以返回undefined

  • some 与every some 会迭代数组中每个元素,直到函数返回true every 会迭代数组中每个元素,直到函数返回false
var isEven = function(x){return (x%2 == 0)}

var isEven =(x) => (console.log( x%2 == 0))
var numbers = [2,4,6,3]
numbers.some(isEven)
numbers.every(isEven)
  • map()

//  对数组中每一项运行回调函数,返回该函数的结果组成的新数组
//    return什么新数组中就有什么; 不return返回undefined; 对数组二次加工
例:
    var arr = [111,222,333,444,555];
    var newArr = arr.map(function (element, index, array) {
        if(index == 2){
            return element; // 这里return了 所以下面返回的值是333
        }
        return element*100; // 返回的元素值都乘上100后的值
    })
    console.log(newArr); // [11100, 22200, 333, 44400, 55500]

实现sum函数(闭包):

sum(1)(2)() 输出 3; sum(1)(2)(3)(4)() 输出10;

指的是将一个接受多个参数的函数 变为 接受一个参数返回一个函数的固定形式,这样便于再次调用,例如 实现add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;

function sum() {
  const _args = [...arguments];
  function fn() {
    _args.push(...arguments);
    return fn;
  }
  fn.toString = function() {
    return _args.reduce((sum, cur) => sum + cur);
  }
  return fn;
}

解析url参数


function GetRequest(url) {
  if (typeof url == "undefined") {
    var urla = decodeURI(location.search)//获取url中"?"符后的字符串
  } else {
    var urla  = "?" + url.split("?")[1]
  }
  var request = new Object()
  if (urla.indexOf("?") != -1){
    var str = urla.substr(1)
    strs = str.split("&")
    for (let i = 0; i < strs.length; i++) {
      request[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1])
    }
  }
  return request
}

var parms_2 = GetRequest('http://htmlJsTest/getrequest.html?uid=admin&rid=1&fid=2&name=小明');
console.log(parms_2); // {"uid":"admin","rid":"1","fid":"2","name":"小明"}

字符串数组中的最长公共前缀


function longestCommonPrefix(string) {
  if (!string.length){
    return ''
  }
  if (string.length === 1) {
    return string[0]
  }
  string.sort()
  let first = string[0]
  let end = string[string.length -1]
  let result = ''
  for (let i = 0; i < first.length; i++) {
    if (first[i] === end[i]) {
      result += first[i]
    } else {
      break
    }
  }
  return result
}

var a = ["abca","abc","abca","abc","abcc"]
console.log(longestCommonPrefix(a))

找最大值(5种方法)

var arr = [23,5,6,2,1]
// ES6
var a = Math.max(...arr)

// ES5
var a = Math.max.apply(null,arr)

//sort
a= arr.sort((num1,num2) =>{
  return num1 - num2 >0
})
 arr[0]

//reduce
a = arr.reduce((num1,num2 )=>{
  return num1>num2 ? num1:num2
})

// for
let max= arr[0]
for (let i = 0; i < arr.length -1; i++) {
  max = max > arr[i+1]?max:arr[i+1]
}

console.log(a)

2 排序和搜索算法

冒泡排序 O(n2)

function bubbleSort(array) {
  for (let i = 0; i < array.length; i++) {
    for (let j = 0; j < array.length -1; j++) {
      if (array[j] > array[j +1]) {
        [array[j],array[j+1]] = [array[j+1],array[j]]
      }
    }
  }
  return array
}

// 改进冒泡
function modifyBubbleSort(array) {
  var n=array.length
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n-1-i; j++) {
      if (array[j] > array[j+1]) {
        [array[j],array[j+1]] = [array[j+1],array[j]]
      }
    }
  }return array
}

var arr = [3,1,5,6,2]
console.log(bubbleSort(arr))

选择排序 O(n2)

function selectSort(array) {
  var indexMin,temp, n=array.length
  for (let i = 0; i < n-1; i++) {
    indexMin = i
    for (let j = i+1; j < n; j++) {
      if (array[indexMin] > array[j]) {
        indexMin = j
      }
    }
    temp = array[i]
    array[i]= array[indexMin]
    array[indexMin]=temp
  }
  return array
}

插入排序O(n2)

function insertSort(array) {
  var n =array.length,j,temp
  for (let i = 0; i < n; i++) {
    j = i
    temp = array[i]
    while (j > 0 && array[j -1]>temp){
      array[j]=array[j-1]
      j--
    }
    array[j] = temp
  }
  return array
}

归并排序O(nlogn)


function mergeSort(arr) {
  const length = arr.length;
  if (length === 1) { //递归算法的停止条件,即为判断数组长度是否为1
    return arr;
  }
  const mid = Math.floor(length / 2);

  const left = arr.slice(0,  mid);
  const right = arr.slice(mid, length);

  return merge(mergeSort(left), mergeSort(right)); //要将原始数组分割直至只有一个元素时,才开始归并
}

function merge(left, right) {
  const result = [];
  let il = 0;
  let ir = 0;

  //left, right本身肯定都是从小到大排好序的
  while( il < left.length && ir < right.length) {
    if (left[il] < right[ir]) {
      result.push(left[il]);
      il++;
    } else {
      result.push(right[ir]);
      ir++;
    }

  }

  //不可能同时存在left和right都有剩余项的情况, 要么left要么right有剩余项, 把剩余项加进来即可
  while (il < left.length) {
    result.push(left[il]);
    il++;
  }
  while(ir < right.length) {
    result.push(right[ir]);
    ir++;
  }
  return result;
}
console.log(mergeSort([2,9,1,8,3,]))

快速排序 O(nlogn)最差情况O(n2)

//1 、随机选择数组中的一个数A,以这个数为基准;
// 2、其他数字跟这个数进行比较,比这个数小的放在其左边,大的放在其右边;
// 3、经过一次循环之后,A左边为小于A的,右边为大于A的;
// 4、这时候将左边和右边的数再递归上面的过程。

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr
  }
  let pivotIndex = Math.floor(arr.length / 2)
  let pivot = arr.splice(pivotIndex, 1)[0]
  let left = []
  let right = []
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }
  // 递归
  return quickSort(left).concat([pivot], quickSort(right))
}

console.log(quickSort([1,4,5,6,2]))

顺序搜索O(n2)和二分搜索 O(logn)(提前排好序)

// 顺序搜索
function search(array,item) {
  for (let i = 0; i < array.length; i++) {
    if (item === array[i]){
      return i
    }
  }
  return -1
}
var arr = [3,1,5,6,2]
console.log(search(arr,2))

//二分搜索
function binarySearch(array,item) {
  var low = 0,
    high = array.length -1,
    mid,element

  while (low <= high){
    mid = Math.floor((low+high)/2)
    element = array[mid]
    if (element <item) {
      low = mid+1
    } else if (element >item) {
      high = mid -1
    } else {
      return mid
    }
  }
  return -1
}

var arr = [1,2,3,4,5]
console.log(binarySearch(arr,2))

2 栈(后进先出)

es5


function Stack() {
  let items = []
  this.push = function (element) {
    items.push(element)
  }
  this.pop = function () {
    return items.pop()
  }
  this.peek =function () {
    return items[items.length-1]
  }
  this.isEmpty =function () {
    return items.length == 0
  }
  this.size = function () {
    return items.length
  }
  this.clear = function () {
    items = []
  }
  this.print = function () {
    console.log(items.toString())
  }
}

es6

class Stack {
  constructor() {
    this.item = []
  }
  push(element){
    this.item.push(element)
  }
  pop(){
    this.item.pop()
  }
  peek(){
    return this.item[this.item.length-1]
  }
  isEmpty(){
    return this.item.length==0
  }
  size(){
    return this.item.length
  }
}
let stack = new Stack()
console.log(stack.isEmpty())
stack.push(1)
stack.push(4)
stack.push(32)
console.log(stack.peek())
stack.push(99)
console.log(stack.size())
console.log(stack.isEmpty())
stack.pop()
stack.pop()
console.log(stack.size())
stack.print()

3 队列(先进先出)

es5

function Queue() {
  let items = []
  this.enqueue = function(element){
    items.push(element)
  }
  this.dequeue = function () {
    items.shift()
  }
  this.front = function () {
    return items[0]
  }
  this.isEmpty =function () {
    return items.length == 0
  }
  this.size = function () {
    return items.length
  }
  this.print = function () {
    console.log(items.toString())
      }
}

es6


let Queue = (function () {
  const items = new WeakMap()
  class Queue2 {
    constructor() {
      items.set(this,[])
    }
    enqueue(element){
      let q = items.get(this)
      q.push(element)
    }
    dequeue(){
      let q = items.get(this)
      let r = q.shift()
      return r
    }
    size(){
      let q = items.get(this)
      return q.length
    }
    isEmpty(){
      let  q = items.get(this)
      return q.length == 0
    }
    front(){
      let q = items.get(this)
      return q[0]
    }
    print(){
      let q = items.get(this)
      console.log(items.toString())
    }
  }
  return Queue2
})();

let queue = new Queue()
console.log( queue.isEmpty())
queue.enqueue('ll')
queue.enqueue('ss')
queue.enqueue('qq')
queue.print()
console.log(queue.size())
console.log(queue.isEmpty())
queue.dequeue()
queue.dequeue()
queue.print()

优先队列


function PriorityQueue() {
  let items = []
  function QueueElememt(element,priority) {
    this.elements = element
    this.priority = priority
  }

  this.enqueue = function (element,priorty) {
    let queueElememt = new QueueElememt(element,priorty)
    let added = false
    for (let i = 0; i < items.length; i++){
      if (queueElememt.priority < items[i].priority) {
        items.splice(i,0,queueElememt)
        added = true
        break
      }
    }
    if (!added){
      items.push(queueElememt)
    }
  }
  this.print =function () {
    for (let i = 0; i < items.length; i++) {
      console.log(`${items[i].elements}-${items[i].priority}`)
    }
  }
}

let pq = new PriorityQueue()
pq.enqueue('jack',3)
pq.enqueue('sam',2)
pq.enqueue('lucy',2)
pq.enqueue('tim',1)
pq.print()

循环队列-击鼓传花


function Queue() {
  let items = []
  this.enqueue = function(element){
    items.push(element)
  }
  this.dequeue = function () {
    items.shift()
  }
  this.size = function () {
    return items.length
  }
}

function f(nameList ,num) {
  let queue =new Queue()

  for (let i = 0; i < nameList.length; i++) {
    queue.enqueue(nameList[i])
  }
  let eliminated = ''
  while (queue.size ()>1){
    for (let i = 0; i <num; i++) {
      queue.enqueue(queue.dequeue())
    }
    eliminated = queue.dequeue()
    console.log(eliminated ,'quit')
  }
  return queue.dequeue()

}

let names = ['a','v','d','s','q','e','t','u']
let winner = f(names,7)
console.log(winner)

4 链表


function LinkedList() {
  let Node =function (element) {
    this.element =element
    this.next = null
  }
  let length = 0
  let head = null

  this.append = function (element) {
    let node = new Node(element),
      current
    if (head === null){
      head = node
    }else {
      current=head
      while(current.next){
        current = current.next
      }
      current.next = node
    }
    length++
  }
  this.insert = function (position,element) {
    if (position >= 0 && position <= length) {
      let node = new Node(element),
      current = head,
      previous,
      index = 0
      if (position === 0) {
        node.next = current
        head = node
      } else {
        while (index++ <position){
          previous = current;
          current = current.next
        }
        node.next = current
        previous.next = node
      }
      length++
      return true
    } else {
      return false
    }
  }
  this.removeAt = function (position) {
    if (position >= 0 && position <length){
      let current = head,
      previous, index = 0
      if (position === 0) {
        head = current.next
      }else {
        while (index++ <position){
          previous = current
          current = current.next
        }
        previous.next= current.next
      }
      length--
      return current.element
    }else {
      return null
    }
  }
  this.indexOf = function (element) {
    let current = head,
    index = -1
    while (current){
      if (element = current.element) {
        return index
      }
      index++
      current = current.next
    }
    return -1
  }

  this.remove = function (element) {
    let index = this.indexOf(element)
    return this.removeAt(index)
  }
  this.isEmpty = function () {
    return length === 0
  }
  this.size =function () {
    return length
  }
  this.getHead = function () {
    return head
  }
  this.toString = function () {
    let current = head,str = ''
    while (current){
      str +=current.element +(current.next ? 'n':'')
      current = current.next
    }
    return str
  }
  this.print = function () {
    console.log(Node.toString())
  }
}

let list = new LinkedList();
list.append(15)
list.append(20)
console.log(list.indexOf(20))
console.log(list.insert(2,99))
console.log(list.isEmpty())
console.log(list.removeAt(3))
console.log(list.toString())

链表反转(利用递归实现)

思路 递归的基线条件:遍历到末节点(node.next === null) 递归的递归条件:node.next !== null 当遇到末节点时,返回末节点,前一节点接收末节点,并把末节点的next设置为自身,返回前一节的,继续下去 考虑特殊情况:undefined和null


function reverseList(head){
  // 闭包
  if (head === undefined || head === null) return null
  var reverHead
  var origHead = head

  var re = function (head) {
    if (head.next === null) {
      reverHead = head
      return head
    } else {
      var node = reverse(head.next)
      node.next = head
      if (origHead === head) {
        head.next = null
        return reverHead
      } else {
        return head
      }
    }
  }
  return re(head)
}

双向链表


function DoubleList() {
  let Node = function (element) {
    this.element = element
    this.prev = null
    this.next = null
  }
  let length = 0
  let head = null
  let tail = null
  this.insert = function (position, element) {
    if (position >= 0 && position <= length) {
      let node = new Node(element),
        current = head,
        previous,
        index = 0
      if (position === 0) {
        if (!head) {
          head = node
          tail = node
        } else {
          node.next = current
          current.prev = node
          head = node
        }
      } else if (position === length) {
        current = tail
        current.next = node
        node.prev = current
        tail = node
      } else {
        while (index++ < position) {
          previous = current
          current = current.next
        }
        node.next = current
        previous.next = node
        current.prev = node
        node.prev = previous
      }
      length++
      return true
    } else {
      return false
    }
  }
  this.removeAt = function (position) {
    if (position >= 0 && position < length) {
      let current = head,
        previous,
        index = 0
      if (length === 1) {
        tail = null
      } else {
        head.prev = null
      }
    } else if (position === length - 1) {
      current = tail
      tail = current.prev
      tail.next = null
    } else {
      while (index++ < position) {
        previous = current
        current = current.next
      }
      previous.next = current.next
      current.next.prev = previous
    }
    length--
    return current.element
  }
}

5 集合 Set ES6

const obj = new Set()
//增加
obj.add(1)
obj.add(2)
obj.add(['aa','bbb'])
obj.add(2)
obj.add(5)
console.log(obj)

//判断有无
console.log(obj.has(5))
obj.has(6)

//删除
obj.delete(5)
console.log( obj.delete(6))
console.log(obj)

//遍历
obj.forEach(n => console.log(n))
//总数
console.log(obj.size)
//清除所有元素
console.log(obj.clear())
let set = new Set()
set.add(1)
set.add(3)
set.add(5)
set.add(7)
console.log(set)
console.log(set.values())
console.log(set.size)
console.log(set.has(1))
set.delete(1)

let setA = new Set()
setA.add(1)
setA.add(2)
setA.add(3)

let setB = new  Set()
setB.add(4)
setB.add(5)
setB.add(6)
// 并集
let unionAb = new Set()
for (let x of setA) unionAb.add(x)
for (let x of setB) unionAb.add(x)
console.log(unionAb)
//交集
let intersection = function (setA,setB) {
  let intersectionSet = new Set()
  for (let x of setA) {
    if (setB.has(x)) {
      intersectionSet.add(x)
    }
  }
  return intersectionSet
}
let interAB = intersection(setA,setB)
//差集
let diff = function (setA, setB) {
  let diffSet = new Set()
  for (let x of setA) {
    if (!setB.has(x)){
      diffSet.add(x)
    }
  }
  return diffSet
}
let diffAb = diff(setA,setB)

5.5 Map ES6

const obj2 = new Map()
//增加
obj2.set('one','abc')
console.log(obj2)//Map(1) { 'one' => 'abc' }
obj2.set('two',221)
console.log(obj2)//Map(2) { 'two' => '221', 'abc' => 221 }

//取值
console.log(obj2.get('one'))//abc
console.log(obj2.get('two'))//221
console.log(obj2.get('three'))//undefined

//删除
console.log(obj2.delete('one'))//true

//数量
console.log(obj2.size)//1

//清除所有元素
console.log(obj2.clear())//undefined

console.log(map.has('lida'))
console.log(map.keys())
console.log(map.values())


var weekmap = new WeakMap()
var ob1 = {name:'gad'}
var ob2 = {name: 'san'}
var ob3 = {name:'ll'}

weekmap.set(ob1,'gad@qq.com')
weekmap.set(ob2,'sam@qq.com')
weekmap.set(ob3,'ll@qq.com')

console.log(weekmap.has(ob1))
console.log(weekmap.get(ob3))
weekmap.delete(ob2)

6 字典 和散列表

字典

function Dictionary() {
  var items = {}
  this.has = function (key) {
    return key in items
  }
  this.set = function (key,value) {
    items[key] = value
  }
  this.delete = function (key) {
    if (this.has(key)){
      delete items[key]
      return true
    }
    return false
  }
  this.get = function (key) {
    return this.has(key) ? items[key]:undefined
  }
  this.values = function () {
    var values = []
    for (let k in items) {
      if (this.has(k)) {
        values.push(items[k])
      } 
    }
    return values
  }
  this.keys = function () {
    return Object.keys(items)
  }
  this.getItems = function () {
    return items
  }
}
var dict = new Dictionary()
dict.set('tony','tony@qq.com')
dict.set('som','som@qq.com')
dict.set('jack','jack@qq.com')
dict.set('lily','lily@qq.com')
console.log(dict.has('som'))
console.log(dict.keys())
console.log(dict.values())
console.log(dict.get('lily'))
dict.delete('som')

HashMap(HashTable 散列表)


function HashTable() {
  var table = []
  //hash 函数 ,是私有方法
  var loseCode = function (key) {
    var hash = 0
    for (let i = 0; i < key.length; i++) {
      hash += key.charCodeAt(i)
    }
    return hash%37
  }

  this.put = function (key,value) {
    var position = loseCode(key)
    console.log(position,'-',key)
    table[position] =value
  }
  this.get = function (key) {
    return table[loseCode(key)]
  }
  this.remove = function (key) {
    table[loseCode(key)] = undefined
  }
}
var hash = new HashTable()
hash.put('lida','lida@qq.com')
hash.put('yould','yould@qq.com')
hash.put('miky','miky@qq.com')
console.log(hash.get('lida'))
console.log(hash.get('llll'))
hash.remove('yould')
//社区中推崇的hash 函数
var djb2HashCode = function (key) {
  var hash = 5381
  for (let i = 0; i < key.length; i++) {
    hash = hash *33 + key.charCodeAt(i)
  }
  return hash % 1013
}

7 树

BST


//声明结构
function BinarySeacheTree() {
  var Node = function (key) {
    this.key = key
    this.left = null
    this.right = null
  }
  var root = null
  this.inset = function (key) {
    var newNode = new Node(key)
    if (root === null) {
      root = newNode
    } else {
      insertNode(root, newNode)
    }
  }
  var insertNode = function (node,newNode) {
    if (newNode.key <node.key) {
      if (node.left === null) {
        node.left = newNode
      } else {
        insertNode(node.left,newNode)
      }
    } else {
      if (node.right === null) {
        node.right = newNode
      } else {
        insertNode(node.right,newNode)
      }
    }
  }
  //中序遍历
  this.inOrderTraverse = function (callback) {
    var inOrderNode =function (node,callback) {
      if (node !== null){
        inOrderNode(node.left,callback)
        callback(node.key)
        inOrderNode(node.right,callback)
      }
    }
  }
  
  //找最小节点
  this.min = function () {
    return minNode(root)
  }
  var minNode = function (node) {
    if (node){
      while (node && node.left !== null){
        node = node.left
      }
      return node.key
    }
    return null
  }
  //找最小节点
  this.max = function () {
    return maxNode(root)
  }
  var maxNode = function (node) {
    if (node){
      while (node && node.right !== null){
        node = node.right
      }
      return node.key
    }
    return null
  }
  //搜索特定的值
  this.search = function (key) {
    return searchNode(root,key)
  }
  var searchNode = function (node,key) {
    if(node === null){
      return false
    }
    if (key < node.key) {
      return searchNode(node.left,key)
    }else if (key > node.key) {
      return searchNode(node.right,key)
    } else {
      return true
    }
  }
  //移出一个节点
  this.remove = function (key) {
    root = removeNode(root,key)
  }
  var removeNode = function (node,key) {
    if (node === null) {
      return null
    }
    if (key < node.key) {
      node.left = removeNode(node.left,key)
      return node
    } else {//键=node.key
      // 情况一:一个叶节点
      if (node.left === null&& node.right === null) {
        node = null
        return node
      }
      // 情况二:只有一个子节点的节点
      if (node.left === null) {
        node = node.right
        return node
      } else if(node.right === null){
        node = node.left
        return node
      }
      // 情况三:有两个子节点的节点
      var aux = findMinNode(node.right)
      node.key = aux.key
      node.right = removeNode(node.right,aux.key)
      return node

      var findMinNode = function (node) {
        while (node && node.left !== null){
          node = node.left
        }
        return node
      }
    }
  }
  //前序遍历
  this.preOrderTraverse = function (callback) {
    var preOrderNode =function (node,callback) {
      if (node !== null){
        callback(node.key)
        preOrderNode(node.left,callback)
        preOrderNode(node.right,callback)
      }
    }
  }
  //后序遍历
  this.postOrderTraverse = function (callback) {
    var postOrderNode =function (node,callback) {
      if (node !== null){
        postOrderNode(node.left,callback)
        postOrderNode(node.right,callback)
        callback(node.key)
      }
    }
  }

}


var tree = new BinarySeacheTree()
tree.inset(11)

 tree.inset(15)
  tree.inset(5)
tree.inset(1)
tree.inset(2)
tree.inset(13)
tree.inset(16)
tree.inset(15)
tree.inset(19)
tree.inset(10)

function printNode(value) {
  console.log(value)
}
tree.inOrderTraverse(printNode)

9 深度优先算法 & 广度优先算法

截屏2021-04-13 07.58.03.png

const data = [
    {
        name: 'a',
        children: [
            { name: 'b', children: [{ name: 'e' }] },
            { name: 'c', children: [{ name: 'f' }] },
            { name: 'd', children: [{ name: 'g' }] },
        ],
    },
    {
        name: 'a2',
        children: [
            { name: 'b2', children: [{ name: 'e2' }] },
            { name: 'c2', children: [{ name: 'f2' }] },
            { name: 'd2', children: [{ name: 'g2' }] },
        ],
    }
]

// 深度遍历, 使用递归
function getName(data) {
    const result = [];
    data.forEach(item => {
        const map = data => {
            result.push(data.name);
            data.children && data.children.forEach(child => map(child));
        }
        map(item);
    })
    return result.join(',');
}

// 广度遍历, 创建一个执行队列, 当队列为空的时候则结束
function getName2(data) {
    let result = [];
    let queue = data;
    while (queue.length > 0) {
        [...queue].forEach(child => {
            queue.shift();
            result.push(child.name);
            child.children && (queue.push(...child.children));
        });
    }
    return result.join(',');
}

console.log(getName(data))
console.log(getName2(data))