JS中的算法与数据结构之集合(Set)实例详解


Posted in Javascript onAugust 20, 2019

本文实例讲述了JS中的算法与数据结构之集合(Set)。分享给大家供大家参考,具体如下:

集合(Set)

同数学中所学的一样,集合(Set)是由一组无序但彼此之间又有一定关系性的成员构成,每个成员在集合中只能出现一次,不同于我们之前说的字典,链表之类的,它是一种包含了不同元素的数据结构(集合中的元素称为成员),从其定义中我们可以看出它具有两个很重要的特征:首先,集合中的成员是无序的,其次,集合中的成员是不相同的,即集合中不存在相同的成员。

实际上,很多编程语言中,集合并不是一种数据类型,但是如果你需要创建一个数据结构用来保存一些独一无二的元素时,集合就变得很有用了,接下来我们一起来看看JS中如何实现一个集合。

集合的定义

我们要实现一个集合,首先要对其一些定义做了解

  • 不包含任何成员的集合称为空集,包含一切可能成员的集合称为全集
  • 如果两个集合里的成员都完全相同,则称两个集合相等。
  • 如果一个集合所有成员都包含于另一个集合,则前一集合称为后一集合的一个子集

集合的操作

通常来说,集合的基本操作有以下三种:

  • 并集:将两个集合中的成员进行合并,得到一个新的集合
  • 交集:将两个集合中共同存在的成员组成的一个新的集合
  • 补集:属于一个集合而不属于另一个集合的成员组成的新的集合

集合的实现

集合(Set)的实现我们这里基于数组,用数组来存储数据,根据我们之前学习的以及上面提到的一些方法,我们可以将集合的构造函数定义如下(为了区别ES6的 set 类型,我们这里选择用 MySet 命名):

//构造函数

function MySet () {
  this.dataStore = [];      // 数据存储
  this.add = add;         // 添加成员
  this.remove = remove;      // 删除成员
  this.size = size;        // 集合元素个数
  this.union = union;       // 集合求并集
  this.intersect = intersect;   // 集合求交集
  this.subset = subset;      // 判断一个集合是否是另一集合的子集
  this.difference = difference;  // 集合求补集
  this.contains = contains;    // 判断某成员是否属于该集合
  this.show = show;        // 显示当前集合
}

我们第一个要实现的方法就是向集合中添加一个成员,即 add 方法

add:向集合中添加一个成员

//添加元素

function add (data) {
  //判断元素是否存在集合当中
  if( this.dataStore.indexOf( data ) < 0 ){
    this.dataStore.push(data);
    return true;
  }else{
    console.warn( 'Can not add ' + data + ', must already be in set');
    return false;
  }
}

我们之前提到,集合中的元素是独一无二的,因此,我们在将数据存储到数组之前,首先就是要确保该集合不存在该数据,因此,我们先用 indexOf 方法检查新加入的元素是否存在,如果找到了就返回该成员在数组中的位置;否则,就返回 -1 ,那么对应的 add 方法就可以定义返回布尔值,添加成功我们返回 true , 否则返回 false ,这样就可以明确告诉我们是否正确的插入了一个元素。

remove:删除集合中某个成员

//删除元素

function remove (data) {
  //判断元素是否存在集合当中
  var pos = this.dataStore.indexOf(data);
  if( pos > -1 ){
    this.dataStore.splice(pos,1);
    return true;
  }else{
    console.warn( data + ' is not in set');
    return false;
  }
}

这里,我们顺理成章的实现了删除方法,它跟 add 方法很类似,首先要检查待删除元素是否存在于数组中,如果存在,我们调用数组的 splice() 方法删除该元素并返回 true ,否则,直接返回 false ,表示集合中不存在该元素。

现在,我们可以完成集合的添加和删除,要测试这些方法之前,我们首先得定义 show 方法,该方法用来显示集合中的成员,该方法的实现很简答,只需返回我们定义的数组即可:

show:显示集合中的成员

// 显示集合成员

function show(){
  console.log(this.dataStore);
  return this.dataStore;
}

接着,我们这会来测试一下:

var fruits = new MySet();

// 添加成员
fruits.add('Apple');
fruits.add('Banana');
fruits.add('Pear');
fruits.show();       // ["Apple", "Banana", "Pear"]

// 添加重复成员
fruits.add('Apple');    // Can not add Apple, must already be in set

// 删除成员
fruits.remove('Banana');  
fruits.show();       // ["Apple", "Pear"]

// 删除不存在的成员
fruits.remove('Banana');  // Banana is not in set

嗯,一切正常,我们可以来实现集合的一些高级操作了,我们先来看看 union (并集)的实现。

union:求集合并集

求集合的并集,就是要将两个集合合并成一个,并除去重复的元素,我们实现思路就是将第一个集合成员放到一个临时集合中,判断第二个集合的成员是否也属于第一个集合,如果为真,代表为重复元素,我们直接跳过该成员,否则将该成员加入临时集合,最后返回该集合即可;

那么,问题来了,我们要如何判断一个成员是否存在于该集合中?因此,我们需要一个辅助方法 contains(),它的实现也非常简单,直接用 indexOf 判断即可

//判断元素是否属于该集合

function contains (data) {
  if( this.dataStore.indexOf(data) > -1 ){
    return true;
  }else{
    return false;
  }
}

现在,我们可以定义 union 方法了

//求集合的并集

function union ( set ) {
  var tempSet = new MySet();
  for( var i = 0 ; i < this.dataStore.length ; i++ ){
    tempSet.add(this.dataStore[i]);
  }
  for( var i = 0 ; i< set.dataStore.length ; i++ ){
    if( !tempSet.contains(set.dataStore[i])){
      tempSet.dataStore.push(set.dataStore[i]);
    }
  }
  return tempSet;
}

这样,我们就可以就集合的并集了,

var fruits1 = new MySet();
fruits1.add('Apple');
fruits1.add('Banana');
fruits1.add('Pear');

var fruits2 = new MySet();
fruits2.add('Grape');
fruits2.add('Banana');
fruits2.add('Pear');
fruits2.add('Orange');

var union = fruits1.union( fruits2 );
union.show();              // ["Apple", "Banana", "Pear", "Grape", "Orange"]

成功了!我们可以来看看求集合的交集了。

intersect:求集合的交集

有了上面求并集的思路,那么交集的定义来说也相对简单,思路就是发现第一个集合的成员也属于第二个集合时,就将该成员加入到新的集合,最后返回新的集合即可;

//求集合的交集

function intersect (set) {
  var tempSet = new MySet();
  for(var i = 0 ; i < this.dataStore.length ; i++ ){
    if( set.contains(this.dataStore[i])){
      tempSet.add(this.dataStore[i]);
    }
  }
  return tempSet;
}

我们还是利用上面的两个集合接着求其交集:

var intersect = fruits1.intersect( fruits2 );
intersect.show();                // ["Banana", "Pear"]

下一个定义的操作是 subset ;

subset:判断集合是否是另一集合的子集

该方法首先要确定 该集合的长度是否小于待比较的集合。如果该集合比待比较集合还要大,那么肯定不是待比较集合的一个子集。只要当,待比较集合比较大时,才去判断集合类的成员是否都属于待比较集合,如果有一个不是,直接返回 false , 只有当所有元素都属于待比较集合的时候,我们才能说该集合是待比较集合的一个子集,该方法才会返回 true , 为了方便查看,我加入了console打印;

//子集判断

function subset (set) {
  if( this.size() > set.size() ){
    console.log('not a subset');
    return false;
  }else{
    for ( var i = 0 ; i < this.dataStore.length ; i++ ){
      if( !set.contains(this.dataStore[i])){
        console.log('not a subset');
        return false;
      }
    }
  }
  console.log(' a subset');
  return true;
}

我们看到上面用到了 size 方法,它的定义如下:

//返回集合长度

function size () {
  return this.dataStore.length;
}

我们保留上面的 fruits1 和 fruits2 , 新建一个 fruits3 来演示 subset 方法

var fruits3 = new MySet();
fruits3.add('Apple');
fruits3.add('Banana');
fruits3.add('Pear');
fruits3.add('Grape');
fruits3.add('Orange');

//子集判断

fruits1.subset( fruits2 );   // not a subset
fruits2.subset( fruits2 );   // a subset
fruits1.subset( fruits3 );   // a subset

看起来一切都很顺利,我们只剩最后一个 difference 方法,该方法返回一个新集合,该集合是由属于第一个集合而不属于第二个集合的成员组成的。

difference:补集

有了交集的思路,补集的实现就显得很自然了。

//补集

function difference (set) {
  var tempSet = new MySet();
  for( var i = 0 ; i < this.dataStore.length ; i ++ ){
    if( !set.contains(this.dataStore[i])){
      tempSet.dataStore.push( this.dataStore[i] );
    }
  }
  return tempSet;
}

我们测试一下:

fruits1.difference(fruits2).show();   // ['Apple']
fruits1.difference(fruits3).show();   // []
fruits2.difference(fruits1).show();   // ["Grape", "Orange"]

ok,到现在,我们完成了一个完整的 set 集合,是不是很棒!

本篇介绍的集合和 ES6 的集合略微有点差别,ES6 提供的 Set 数据结构,有很多现成的方法可以直接调用,很是方法,不是很了解的小伙伴可以参考我之前的一篇博文,关于ES6中Symbol 、Set 和 Map 一文,相信会有不错的收获~

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
JS控件的生命周期介绍
Oct 22 Javascript
js 异步操作回调函数如何控制执行顺序
Dec 24 Javascript
JS实现仿QQ效果的三级竖向菜单
Sep 25 Javascript
不得不分享的JavaScript常用方法函数集(上)
Dec 23 Javascript
js中利用cookie实现记住密码功能
Aug 20 Javascript
jQuery解析返回的xml和json方法详解
Jan 05 Javascript
图解Javascript——作用域、作用域链、闭包
Mar 21 Javascript
深入理解Node.js中通用基础设计模式
Sep 19 Javascript
利用Angular7开发一个Radio组件的全过程
Jul 11 Javascript
原生Vue 实现右键菜单组件功能
Dec 16 Javascript
vue实现信息管理系统
May 30 Javascript
js实现带有动画的返回顶部
Aug 09 Javascript
Vue + Element UI图片上传控件使用详解
Aug 20 #Javascript
微信小程序项目总结之记账小程序功能的实现(包括后端)
Aug 20 #Javascript
ES6中Symbol、Set和Map用法详解
Aug 20 #Javascript
Vue+Element UI+vue-quill-editor富文本编辑器及插入图片自定义
Aug 20 #Javascript
node中使用log4js4.x版本记录日志的方法
Aug 20 #Javascript
vue 获取视频时长的实例代码
Aug 20 #Javascript
vue+elementUI实现图片上传功能
Aug 20 #Javascript
You might like
简单的php数据库操作类代码(增,删,改,查)
2013/04/08 PHP
php 实现进制相互转换
2016/04/07 PHP
PHP用户验证和标签推荐的简单使用
2016/10/31 PHP
使用php完成常见的文件上传功能(推荐)
2017/01/13 PHP
jQuery使用一个按钮控制图片的伸缩实现思路
2013/04/19 Javascript
Node.js开发指南中的简单实例(mysql版)
2013/09/17 Javascript
jquery插件冲突(jquery.noconflict)解决方法分享
2014/03/20 Javascript
JavaScript控制各种浏览器全屏模式的方法、属性和事件介绍
2014/04/03 Javascript
innerHTML动态添加html代码和脚本兼容多个浏览器
2014/10/11 Javascript
jQuery插件Slider Revolution实现响应动画滑动图片切换效果
2015/06/05 Javascript
jQuery实现按钮只点击一次后就取消点击事件绑定的方法
2015/06/26 Javascript
JavaScript实现单击下拉框选择直接跳转页面的方法
2015/07/02 Javascript
JS实现回到页面顶部动画效果的简单实例
2016/05/24 Javascript
浅谈Webpack下多环境配置的思路
2018/06/27 Javascript
jQuery插件实现图片轮播效果
2020/10/19 jQuery
使用Typescript开发微信小程序的步骤详解
2021/01/12 Javascript
[40:16]TFT vs Mski Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
Python解析xml中dom元素的方法
2015/03/12 Python
Python使用Scrapy爬取妹子图
2015/05/28 Python
详解python3中socket套接字的编码问题解决
2017/07/01 Python
Python 函数基础知识汇总
2018/03/09 Python
10个Python小技巧你值得拥有
2018/09/29 Python
实例讲解python中的协程
2018/10/08 Python
浅谈python脚本设置运行参数的方法
2018/12/03 Python
浅析PyTorch中nn.Module的使用
2019/08/18 Python
浅谈Python 钉钉报警必备知识系统讲解
2020/08/17 Python
意大利专业化妆品品牌:KIKO MILANO
2017/02/01 全球购物
G-Form护具官方网站:美国运动保护装备
2019/09/04 全球购物
优秀员工个人的自我评价
2013/11/29 职场文书
九一八事变纪念日演讲稿
2014/09/14 职场文书
2014年个人委托书范本
2014/10/13 职场文书
2014年环保工作总结
2014/11/26 职场文书
酒店员工辞职信范文
2015/02/28 职场文书
2015年项目经理工作总结
2015/04/30 职场文书
Django实现在线无水印抖音视频下载(附源码及地址)
2021/05/06 Python
instantclient客户端 连接oracle数据库
2022/04/26 Oracle