JavaScript 数组的 uniq 方法


Posted in Javascript onJanuary 23, 2008

给Array本地对象增加一个原型方法,它的用途是删除数组条目中重复的条目(可能有多个),返回值是一个包含被删除的重复条目的新数组。

形式化描述:
input
Array(size=N)
output
Array1=Array的无重复保序的子集,
无重复是指,对任意a,b属于Array1,a!=b
保序是指,若a在Array的下标小于b在Array的下标,则a在Array1中的下标也小于b在Array的下标
Array2=Array-Array1,保序
realazy给出了一个新解,思路非常清晰:顺序遍历访问每个元素,如果这个元素的值已经访问过了,则加入Array2,否则加入Array1。判断当前元素的值是否已经访问过所采用的方法是顺序遍历已经访问过的所有元素。 
易见该算法复杂度约O(N^2)。

我在他的算法框架下稍微做了一些改进,关键在于遍历过程中如何判断当前元素的值是否已经访问过。在原数组值域为正整数且极差(range=max value-min value)不太大的条件下,可以采用简单的"桶"算法。
准备一个长度为range的boolean数组b,初始化全为false。对于原数组中每个值value,如果b[value]=true,则表明这个值访问过,放入Array2,否则放入Array1同时令b[value]=true。 
这显然是O(N)的算法,代价是额外的空间复杂度range,而且要求原数组值域为正整数。
不难推广到值域为整数的情形,事实上只需考察桶号value-min(Array)即可转化为正整数的情形。

为了避免range太大造成的空间的浪费,在"桶"算法基础上改进为散列算法,具体说来是线性同余开散列法。目的是将值域压缩映射到一个可控的小的连续正整数子集中,同时保证不同的原象对应的相同的象的概率要尽可能小,也就是说桶与桶之间要尽量负载均衡。 
例如这是一个值域为实数的散列函数:
key=hashFun(value)=Math.floor(value)*37%91
这仍然是O(N)的算法,(显然O(N)是所有uniq算法的复杂度下界),好处是可以控制空间的开销,而且可以适应非整数值域,只需要设计相应的散列函数即可。

下面是桶(bucket)算法的实现:
   var resultArr = [],
       returnArr = [], 
       origLen = this.length,
       resultLen;
   var maxv=this[0],minv=this[0];
   for (var i=1; i<origLen; ++i){
       if(this[i]>maxv)maxv=this[i];
       else if(this[i]<minv)minv=this[i]; 
   }
   var blen=maxv-minv+1;
   var b=new Array(blen);
   for(var i=0;i<blen;++i)b[i]=false;
   for (var i=0; i<origLen; ++i){
       if (b[this[i]-minv]){
           returnArr.push(this[i]); 
       } else {
           resultArr.push(this[i]);
           b[this[i]-minv]=true;
       }
   }
   resultLen = resultArr.length;
   this.length = resultLen;
   for (var i=0; i<resultLen; ++i){ 
       this[i] = resultArr[i];
   }
   return returnArr;
下面是散列(hash)算法的实现
var shuffler = 37
var beta=0.007;
var origLen=this.length
var bucketSize=Math.ceil(origLen*beta);
var hashSet=new Array(bucketSize); 
var hashFun = function(value){
var key = (Math.floor(value)*shuffler)%bucketSize;
return key;
}
//init hashSet
for(var i=0;i<bucketSize;i++)hashSet[i]=new Array();
//
var ret=[],self=[];
var key,value; 
var bucket,openLen;
var everConflict;
for(var i=0;i<origLen;i++){
value=this[i];
key=hashFun(value);
bucket = hashSet[key];
openLen=bucket.length;//if(openLen>1)return;
everConflict=false; 
for(var j=0;j<openLen;j++){
 if(bucket[j]==value){
  ret.push(value);
  everConflict=true;
  break;
 }
}
if(!everConflict){
 bucket.push(value);
 self.push(value);
}
}
   selfLen = self.length;
   this.length = selfLen;
   for (i=0; i<selfLen; ++i){
       this[i] = self[i];
   }
//compute average bucket size
var lens=[],sum=0;
for(var i=0;i<hashSet.length ;++i){lens.push(hashSet[i].length);sum+=hashSet[i].length};
average=sum/hashSet.length;//watch lens,average
   return ret;

用k*10000个0~k*100的随机整数测试计算时间(ms)
k 1 2 3 4 5
realazy 240 693 1399 2301 3807 
bucket 55 101 141 219 293
hash 214 411 654 844 1083
测试框架借鉴了http://realazy.org/lab/uniq.html
测试环境Firefox2.0.0.6/Ubuntu7.10/2.66GHzP4/1024MBDDR 

Javascript 相关文章推荐
可以支持多中格式的JS键盘
May 02 Javascript
解析jQuery与其它js(Prototype)库兼容共存
Jul 04 Javascript
js如何判断用户是在PC端和还是移动端访问
Apr 24 Javascript
JavaScript DSL 流畅接口(使用链式调用)实例
Mar 15 Javascript
js实现两点之间画线的方法
May 12 Javascript
js实现的万能flv网页播放器代码
Apr 30 Javascript
BootStrap响应式导航条实例介绍
May 06 Javascript
JavaScript实现自定义媒体播放器方法介绍
Jan 03 Javascript
AngularJS 文件上传控件 ng-file-upload详解
Jan 13 Javascript
JS中实现函数return多个返回值的实例
Feb 21 Javascript
AngularJS 限定$scope的范围实例详解
Jun 23 Javascript
微信小程序使用Promise简化回调
Feb 06 Javascript
Javascript 更新 JavaScript 数组的 uniq 方法
Jan 23 #Javascript
Javascript 各浏览器的 Javascript 效率对比
Jan 23 #Javascript
Javascript 写的简单进度条控件
Jan 22 #Javascript
用jQuery实现检测浏览器及版本的脚本代码
Jan 22 #Javascript
零基础学JavaScript最新动画教程+iso光盘下载
Jan 22 #Javascript
用js 让图片在 div或dl里 居中,底部对齐
Jan 21 #Javascript
jquery 插件 人性化的消息显示
Jan 21 #Javascript
You might like
PHP+MYSQL 出现乱码的解决方法
2008/08/08 PHP
PHP中基本HTTP认证技巧分析
2015/03/16 PHP
ThinkPHP简单使用memcache缓存的方法
2016/11/15 PHP
动态改变textbox的宽高的js
2006/10/26 Javascript
(jQuery,mootools,dojo)使用适合自己的编程别名命名
2010/09/14 Javascript
在JS中解析HTML字符串示例代码
2014/04/16 Javascript
nodejs教程之入门
2014/11/21 NodeJs
DOM基础教程之使用DOM设置文本框
2015/01/20 Javascript
js代码验证手机号码和电话号码是否合法
2015/07/30 Javascript
js基础知识(公有方法、私有方法、特权方法)
2015/11/06 Javascript
JavaScript设计模式经典之工厂模式
2016/02/24 Javascript
浅析JavaScript中浏览器的兼容问题
2016/04/19 Javascript
微信小程序 选择器(时间,日期,地区)实例详解
2016/11/16 Javascript
js中bool值的转换及“&amp;&amp;”、“||”、 “!!”详解
2017/12/21 Javascript
教你如何用node连接redis的示例代码
2018/07/12 Javascript
JS实现简易贪吃蛇游戏
2020/08/24 Javascript
python通过pil将图片转换成黑白效果的方法
2015/03/16 Python
Python 序列的方法总结
2016/10/18 Python
使用Python3制作TCP端口扫描器
2017/04/17 Python
解决pandas使用read_csv()读取文件遇到的问题
2018/06/15 Python
Python实现深度遍历和广度遍历的方法
2019/01/22 Python
python实现二级登陆菜单及安装过程
2019/06/21 Python
python 识别登录验证码图片功能的实现代码(完整代码)
2020/07/03 Python
python怎么对数字进行过滤
2020/07/05 Python
拿来就用!Python批量合并PDF的示例代码
2020/08/10 Python
用canvas做一个DVD待机动画的实现代码
2019/04/12 HTML / CSS
Footshop罗马尼亚:最好的运动鞋选择
2019/09/10 全球购物
Feelunique中文官网:欧洲最大化妆品零售电商
2020/07/10 全球购物
小学生操行评语
2014/04/22 职场文书
医学求职信
2014/05/28 职场文书
骨干教师申报材料
2014/12/17 职场文书
教师工作表现评语
2014/12/31 职场文书
司考复习计划
2015/01/19 职场文书
2015年安全教育月活动总结
2015/03/26 职场文书
MySQL8.0.18配置多主一从
2021/06/21 MySQL
nginx lua 操作 mysql
2022/05/15 Servers