ES6——之数据存储结构:Set、Map基本使用及实现原理(邻接链表详解)

ES6——之数据存储结构 Set、Map基本使用及实现原理

使用时必须要new:
1、Set:是ES6提供给我们的构造函数,能构造出一种新的储存数据的结构;
特点:只有属性值,成员值唯一(不重复)
用途:可以转换成数组,进行去重、取交集、并集、差集等操作;
Set的基本使用:创建时可传[]、’'等迭代数据;

const oS = new Set([1,2,false,1]);  //自带去重方法
const oS1 = new Set('abcdc');
//Set.prototype上的方法:
oS.add(4);          //增加值;
oS.add([1, 2]);
oS.delete(false);     //删值;
// oS.clear();        //清空Set;
oS.has(10);         //判断oS中有没有10,返回false;
oS.forEach(val => {  //遍历属性值;
    console.log(val)
});
for (const prop of oS){  //ES6-for of方法同样可以遍历迭代数据;
    console.log(prop)
}
console.log(Array.from(oS)); //将Set转换位数组;
console.log([...oS]);        //还可用...展开迭代数据;

//去重:
const o = {
    name : 'll'
};
const arr = [1,2,3,4,5,o, o, {name: 'ly'}];
const oS2 = new Set(arr);   
 //Set方法去重,很好的将对象一并去重,比自己写的方法功能完善;

//并集、交集、差集:
const arr1 = [1,2,3,2,3];
const arr2 = [2,3,4,5,4,5];
const arrB = new Set([...arr1, ...arr2])    //取并集;
const arr11 = new Set(arr1);
const arr22 = new Set(arr2);           //使arr2去重,并有has方法;
const arrJ = [...arr11].filter( ele => arr22.has(ele) );
//[ ].filter进行筛选,先把arr1数据进行去重;//return出arr22中有arr11中的值的值;取交集;

const arrC1 = [...arr11].filter( ele => !arr22.has(ele));  //找出arr1中arr2没有的值;
const arrC2 = [...arr22].filter( ele => !arr11.has(ele));  //找出arr2中zrr1没有的值;
const arrC = [...arrC1, ...arrC2]                    //取出并集即可取差集;

2、Map:与Set相似,只不过本质上是键值对的集合;
特点:key对应value,key和value唯一,任何值都可以当属性;
用途:可以让对象当属性,储存,去重等;
Map的基本使用:创建时可传参,注意格式 ([[’’,’’],[’’,’’]]);

const oM = new Map([['name', 'll'], ['age', 18], [123, true], [{}, 123] ]);
//Map.prototype上的方法:
const oM1 = new Map();
oM1.set('name', 'll')   //增加值;
oM1.set('age', '18');
const obj = {};
oM1.set(obj, 123);
oM1.get(obj);        //取值(注意:如果取引用值时,现将引用值存好在取)
// oM1.delete('name');  //删除
// oM1.clear();        //初始化‘’
oM1.has(10);         //判断oM1中有没有10,返回false;

oM1.forEach((ele, index, self) => {   //遍历Map;
    console.log(ele, index, self)
})

for(const val of oM1){             //ES6-for of方法同样可以遍历迭代数据;
    console.log(val[0], val[1])
}

Set、Map原理思想:(邻接链表思想)(Set原理相同,只是少了对value值的处理)
//1、链表的储存方式

const node3 = {
    next: null
}
const node2 = {
    next: node3
}
const node1 = {
    next: node2
}
console.log( node1.next.next.next );  ==>  null;

拓:
邻接链表储存解构详解:

class Node {             //创建一个Node的构造函数,函数中有next指针;
    constructor(key){
        this.key = key;
        this.next = null;
    }
}
// var node1 = new Node(1)
//     node1.next = new Node(2)
//     node1.next.next = new Node(3)  //这是一个链表,但调用方法太麻烦;

//封装inserNode方法:在链表中进行插入操作(生成链表);
function inserNode (startNode, node, num = Infinity) {  //参数num:有值时在该值后拆入节点,没值时=在最后插入节点;
    if (num < 1) {
        return;
    }
    while (startNode.next && --num >0){    //找到想传入节点的位置;
        startNode = startNode.next;        //让当前节点指向下一个节点;直到下一个节点为null时跳出循环
    }
    let nextNode = startNode.next;         //保存下一个节点的指针;
    startNode.next = node                  //让当前节点指向当前传入的node;
    node.next = nextNode;                  //让传入节点指向原来的下一个节点;即可实现任意位置插入节点;
}

let nodeHead = {type: 'head', next: null}
    inserNode(nodeHead, new Node(1))      //每一步都可以更改链表;
    inserNode(nodeHead, new Node(2))
    inserNode(nodeHead, new Node(3))
    inserNode(nodeHead, new Node(4))
    inserNode(nodeHead, new Node(5))
    // inserNode(nodeHead, new Node(6), 1)//在第一个节点后插入新节点;
var node = new Node(6);
inserNode(nodeHead, node, 1000)   //插入最后
node.next = nodeHead.next.next;   //让最后一个node的next指向第三个node,形成链表中的环结构;


//面试题:判断链表结构中有没有环的方法;
//原理:取一个步长为1,一个步长为2的指针
//当这两个指针相等,并且不为null时,则链表中有环;
function findRing (nodeHead) {        
    let point1 = nodeHead.next;
    let point2 = point1 && point1.next; //nodeHead.next.next;
    while (point1 && point2) {          //point1 和 point2 都存在并且值不为null;
        if(point1 == point2) {
           console.log('有环')
            return true;
        }
        point1 = point1.next;
        point2 = point2.next;
        point2 = point2 && point2.next;
    }
    console.log('无环');
    return false
}
findRing(nodeHead);    //输出结果:有环;

//2、hash算法:将一些不定的值转换为特定的值(因为传入的值有可能是{}、’’、132等,通过hash算法将所有的{}存到1对象中,将所有的’ '存到2对象中,将所有的数字存到3对象中;)

//3、桶思想:用数组规定一个桶,规定桶的长度,每个长度时一个对象{ },并且里面有next属性;

Map的实现源码:

function myMap () {
    this.bLenght = 8;               //规定桶的长度;
    this.init();
}
myMap.prototype.init = function () {   //初始化一个桶:有8个对象;
    this.bucket = new Array(this.bLenght);
    for (var i = 0; i < this.bLenght; i++){
        this.bucket[i] = {           //让每一个长度为对象;
            type: '对象-' + i,
            next: null
        }
    }
}
myMap.prototype.makeHash = function (key) { //编写hash算法,传入的值变成[0,8)区间的值;
    let hash = 0;
    if(typeof key !== 'string'){
        if(typeof key == 'number'){  //传入的是数字或者NaN时,不是NaN时直接赋给hash;
            hash = Object.is(key, NaN) ? 0 : key;
        }else if(typeof key == 'object'){  //处理传入的是对象时: []、{}、 null等;
            hash = 1;
        }else if(typeof key == 'boolean'){ //boolean类型时,true转化为1;false转换为0;
            hash = key;
        }else{                      //剩下的 undefined function(){} 类型直接转化为2;
            hash = 2;
        }
    }else{                          //转换string形式的key;
        for(let i = 0; i < 3; i++){        //将传入的字符串转化为特定的数字;
            hash += key[i] ? key[i].charCodeAt(0) : 0;
        }
    }
    return hash % 8;    //将hash转化为[0,8)区间的值;可以分不到不同的桶里的对象中去;
}
myMap.prototype.set = function (key, value) {
    let hash =  this.makeHash(key);     //找到自己对应的hash值;
    let oTempB = this.bucket[hash];     //找到自己对应的桶里的对象;
    while (oTempB.next) {            //当桶里对象的next有值时:
        if (oTempB.next.key == key){  //如果传入的key和桶里的key相同;
            oTempB.next.value = value;//直接覆盖原来的值;
            return;                 //结束set任务;
        }else{       //否则让它直接指向下一个next=对象,跳出循环,进行下面的操作;
            oTempB = oTempB.next;
        }
    }
oTempB.next = {  
 //跳出循环时:当桶里对象的next没有值时,让next为一个{},直接赋值,没并且让其next为null;
        key : key,
        value : value,
        next : null
    }
}
myMap.prototype.get = function (key) {
    let hash = this.makeHash(key);
    let oTempB = this.bucket[hash];   //找到自己对应的桶里的对象;
    while (oTempB.next) {
        if (oTempB.key == key ){
            return oTempB.value;
        }else{
            oTempB = oTempB.next;
        }
    }
    return '没有找到该值';
}
myMap.prototype.delete = function (key) {
    let hash = this.makeHash(key);
    let oTempB = this.bucket[hash];
    while (oTempB.next) {
        if(oTempB.next.key == key){  //如果当前桶中对象的next中的key是传入的key时;
            oTempB.next = oTempB.next.next; 
//直接将上一层的next对象,连接到下一层next对象上,即可达到删除功能;
            return '删除成功'
        }else{                       //如果不是;
            oTempB = oTempB.next;   
 //将链表进入下一层;直到找到传入的key的那一层next对象,进行上面操作;
        }
    }
    return '该值不存在';
}
myMap.prototype.has = function (key) {
    let hash = this.makeHash(key);
    let oTempB = this.bucket[hash];
    while(oTempB.next){
        if (oTempB.next.key == key){
            return '有该值';
        }else{
            oTempB = oTempB.next;
        }
    }
    return "没有该值";
}
myMap.prototype.clear = function (key) {
    this.init();         //初始化函数;
}
//测试MyMap:
const oMM = new myMap();
oMM.set('name2', 'l6');
oMM.set('name2', 'l5');
oMM.set('fly', 'l6');
oMM.set({}, 456);
oMM.get('name1');
console.log(oMM); 
//与原Map方法结果一致;

利用myMap()可实现ES6中的Map();

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值