프런트엔드 면접관이 준비해야 할 질문: Javascript Array에서 중복을 제거하는 방법. 제가 아는 한, 바이두(Baidu), 텐센트(Tencent), 샨다(Shanda) 등이 인터뷰에서 이런 질문을 한 적이 있습니다. 이 질문은 간단해 보이지만 사실은 숨겨진 위험을 내포하고 있습니다. 이 테스트는 이 기능을 구현하는 것뿐만 아니라 컴퓨터 프로그램 실행에 대한 심층적인 이해에 관한 것입니다.
이 목적을 달성하기 위해 총 세 가지 알고리즘을 생각해냈습니다.
Array.prototype.unique1 = function() { var n = []; //一个新的临时数组 for(var i = 0; i < this.length; i++) //遍历当前数组 { //如果当前数组的第i已经保存进了临时数组,那么跳过, //否则把当前项push到临时数组里面 if (n.indexOf(this[i]) == -1) n.push(this[i]); } return n; }
Array.prototype.unique2 = function() { var n = {},r=[]; //n为hash表,r为临时数组 for(var i = 0; i < this.length; i++) //遍历当前数组 { if (!n[this[i]]) //如果hash表中没有当前项 { n[this[i]] = true; //存入hash表 r.push(this[i]); //把当前数组的当前项push到临时数组里面 } } return r; }
Array.prototype.unique3 = function() { var n = [this[0]]; //结果数组 for(var i = 1; i < this.length; i++) //从第二项开始遍历 { //如果当前数组的第i项在当前数组中第一次出现的位置不是i, //那么表示第i项是重复的,忽略掉。否则存入结果数组 if (this.indexOf(this[i]) == i) n.push(this[i]); } return n; }
첫 번째와 세 번째 메서드는 모두 배열의 indexOf 메서드를 사용합니다. 이 방법의 목적은 배열에서 저장된 매개변수가 처음으로 나타나는 것을 찾는 것입니다. 분명히, js 엔진은 이 메소드를 구현할 때 대상을 찾을 때까지 배열을 탐색합니다. 그래서 이 기능은 시간을 많이 낭비하게 됩니다. 두 번째 방법은 해시 테이블을 사용합니다. 개체의 발생을 아래 첨자 형식으로 저장합니다. 첨자 참조는 indexOf를 사용하여 배열을 검색하는 것보다 훨씬 빠릅니다.
이 세 가지 방법의 효율성을 판단하기 위해 길이가 10,000인 난수 배열을 생성하는 테스트 프로그램을 만든 후 여러 가지 방법을 사용하여 실행 시간을 테스트했습니다. 결과는 두 번째 방법이 다른 두 가지 방법보다 훨씬 빠르다는 것을 보여줍니다. 하지만 메모리 사용량 측면에서는 추가적인 해시 테이블이 있기 때문에 두 번째 방법을 사용할 가능성이 더 높습니다. 이것이 바로 시간을 위한 공간이라고 불리는 것입니다. 이것은 테스트 페이지이며, 확인해 볼 수도 있습니다.
Array.prototype.unique4 = function() { this.sort(); var re=[this[0]]; for(var i = 1; i < this.length; i++) { if( this[i] !== re[re.length-1]) { re.push(this[i]); } } return re; }
이 방법의 아이디어는 배열을 먼저 정렬한 다음 인접한 두 값을 비교하는 것입니다. 정렬 시에는 JS 기본 정렬 방법을 사용합니다. JS 엔진은 내부적으로 빠른 정렬을 사용해야 합니다. 최종 테스트 결과, 이 방법의 실행 시간은 두 번째 방법에 비해 평균 3배 가량 길지만, 첫 번째와 세 번째 방법에 비해 훨씬 빠른 것으로 나타났다.