摘要:上一篇數據結構與算法鏈表寫在前面說明數據結構與算法系列文章的代碼和示例均可在此找到一集合集合數據結構集合是一種包含不同元素的數據結構。集合中的元素成為成員。
上一篇:JS數據結構與算法_鏈表
寫在前面說明:JS數據結構與算法 系列文章的代碼和示例均可在此找到一、集合Set 1.1 集合數據結構
集合set是一種包含不同元素的數據結構。集合中的元素成為成員。集合的兩個最重要特性是:集合中的成員是無序的;集合中不允許相同成員存在
計算機中的集合與數學中集合的概念相同,有一些概念我們必須知曉:
不包含任何成員的集合稱為空集;包含一切可能的成員為全集
如果兩個成員完全相同,則稱為兩個集合相等
如果一個集合中所有的成員都屬于另一個集合,則前一個集合被稱為后一個集合的子集
另外還有交集/并集/差集,下面會一一實現
1.2 集合的實現一般集合包含下面幾個方法:
add 向集合添加一個新的項
remove 從集合移除一個值
has 如果值在集合中,返回true,否則返回false
clear 移除集合中的所有項
size 返回集合所包含元素的數量。與數組的length屬性類似
values 返回一個包含集合中所有值的數組
union 兩個集合的并集
intersection 兩個集合的交集
difference 兩個集合的差集
isSubsetOf 判斷是否為子集
下面將基于對象實現基礎的集合(數組和隊列也可實現集合,點擊查看)
class Set { constructor() { this._items = {}; this._length = 0; } // 添加成員時,如果已有成員則不操作。以[value: value]的形式存儲在對象中 add(value) { if (this.has(value)) return false; this._items[value] = value; this._length += 1; return true; } // 移除成員時,如果沒有對應成員則不操作 remove(value) { if (!this.has(value)) return false; delete this._items[value]; this._length -= 1; return true; } values() { return Object.values(this._items); } has(value) { return this._items.hasOwnProperty(value); } clear() { this._items = {}; this._length = 0; } size() { return this._length; } isEmpty() { return !this._length; } }
(1)并集的實現
將兩個集合中的元素依次添加至新的集合中,并返回改集合
// 并集 union(otherSet) { const unionSet = new Set(); const values = this.values(); values.forEach(item => unionSet.add(item)); const otherValues = otherSet.values(); otherValues.forEach(item => unionSet.add(item)); return unionSet; }
(2)交集的實現
以集合A作為參考,遍歷集合B依次對比成員,B中的成員存在A中則添加至新集合C中,最后返回C
// 交集 intersection(otherSet) { const intersectionSet = new Set(); const values = this.values(); values.forEach(item => { if (otherSet.has(item)) { intersectionSet.add(item); } }) return intersectionSet; }
(3)差集的實現
以集合A作為參考,遍歷集合B依次對比成員,B中的成員不存在A中則添加至新集合C中,最后返回C
// 差集 difference(otherSet) { const differenceSet = new Set(); const values = this.values(); values.forEach(item => { if (!otherSet.has(item)) { differenceSet.add(item); } }) return differenceSet; }
注意:A.difference(B)與B.difference(A)計算參考不同
(4)子集的實現
以集合A作為參考,遍歷集合B依次對比成員,B中的所有成員均存在A中則為其子集,否則不是
// 子集 isSubsetOf(otherSet) { if (this.size() > otherSet.size()) return false; const values = this.values(); for (let i = 0; i < values.length; i += 1) { const item = values[i]; if (!otherSet.has(item)) return false; } return true; }1.3 ES6中的Set
ES6中提供了新的數據結構Set,它類似于數組,但是成員的值都是唯一的,沒有重復的值
提供了一下幾個方法:
add(value) 添加某個值,返回Set結構本身
delete(value) 刪除某個值,返回一個布爾值,表示刪除是否成功
has(value) 返回一個布爾值,表示該值是否為Set的成員
clear() 清除所有成員,沒有返回值
size 屬性,返回成員總數
創建:
直接通過數組創建:new Set([1,2,3,4])
先實例再添加:const set = new Set(); set.add(1);
遍歷:
keys() 返回鍵名的遍歷器
values() 返回鍵值的遍歷器
entries() 返回鍵值對的遍歷器
forEach()/for-of 使用回調函數遍歷每個成員
二、字典Dictionary 2.1 字典數據結構集合表示一組互不相同的元素(不重復的元素)。在字典中,存儲的是鍵-值對,其中鍵名是用來查詢特定元素的。字典和集合很相似,集合以值-值對的形式存儲元素,字典則是以鍵-值對的形式來存儲元素。字典也稱作映射
類比:電話號碼簿里的名字和電話號碼。要找一個電話時,先找名字,名字找到了,緊挨著他的電話號碼也就想找到了,這里的鍵是指你用來查找的東西,值時查找得到的結果
2.2 字典的實現一般字典包括下面幾種方法:
set(key,value) 向字典中添加新元素
remove(key) 通過使用鍵值來從字典中移除鍵值對應的數據值
has(key) 如果某個鍵值存在于這個字典中,則返回true,反之則返回false
get(key) 通過鍵值查找特定的數值并返回
clear() 將這個字典中的所有元素全部刪除
size() 返回字典所包含元素的數量。與數組的length屬性類似
keys() 將字典所包含的所有鍵名以數組形式返回
values() 將字典所包含的所有數值以數組形式返回
下面將基于對象實現基礎的字典
class Dictionary { constructor() { this._table = {}; this._length = 0; } set(key, value) { if (!this.has(key)) { this._length += 1; } this._table[key] = value; } has(key) { return this._table.hasOwnProperty(key); } remove(key) { if (this.has(key)) { delete this._table[key]; this._length -= 1; return true; } return false; } get(key) { return this._table[key]; } clear() { this._table = {}; this._length = 0; } size() { return this._length; } keys() { return Object.keys(this._table); } values() { return Object.values(this._table); } }
這里添加成員時,并未考慮key為對象的情況,以至于會出現如下情況:
const obj = {}; obj[{a: 1}] = 1; obj[{a: 2}] = 2; console.log(obj[{a: 1}]); // 2 // 對象形式的鍵會以其toSting方法的結果存儲 obj; // {[object Object]: 2}
在ES6中支持key值為對象形式的字典數據結構Map,其提供的方法如下:
提供了一下幾個方法:
set(key, value) set方法設置鍵名key對應的鍵值為value,然后返回整個Map結構
get(key) get方法讀取key對應的鍵值,如果找不到key,返回undefined
delete(value) 刪除某個值,返回一個布爾值,表示刪除是否成功
has(value) 返回一個布爾值,表示該值是否為Map的成員
clear() 清除所有成員,沒有返回值
size 屬性,返回成員總數
創建:
直接通過數組創建:const map = new Map([ ["name", "張三"], ["title", "Author"] ]);
先實例再添加:const map = new Map();
遍歷:
keys() 返回鍵名的遍歷器
values() 返回鍵值的遍歷器
entries() 返回鍵值對的遍歷器
forEach()/for-of 使用回調函數遍歷每個成員
三、哈希表/散列表 3.1 哈希表數據結構散列表也叫哈希表(HashTable也叫HashMap),是Dictionary類的一種散列表實現方式
(1)哈希表有何特殊之處:
數組的特點是尋址方便,插入和刪除困難;而鏈表的特點是尋址困難,插入和刪除方便。哈希表正是綜合了兩者的優點,實現了尋址方便,插入刪除元素也方便的數據結構
(2)哈希表實現原理
哈希表就是把Key通過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,然后將該數字對數組長度進行取余,取余結果就當作數組的下標,將value存儲在以該數字為下標的數組空間里。而當使用哈希表進行查詢的時候,就是再次使用哈希函數將key轉換為對應的數組下標,并定位到該空間獲取value,如此一來,就可以充分利用到數組的定位性能進行數據定位
下面是將key中每個字母的ASCII值之和作為數組的索引(哈希函數)的圖例:
(3)數組的長度為什么選擇質數
書中有如下說明:
散列函數的選擇依賴于鍵值的數據類型。如果鍵是整數,最簡單的散列函數就是以數組的長度對鍵取余。在一些情況下,比如數組的長度為10,而鍵值都是10的倍數時,就不推薦使用這種方式了。這也是數組的長度為什么要是質數的原因之一。如果鍵是隨機的整數,而散列函數應該更均勻地分布這些鍵,這種散列方式稱為除留余數法3.2 哈希表的實現
我們為哈希表實現下面幾個方法:
hashMethod 哈希函數,將字符串轉換成索引
put 添加鍵值
get 由鍵獲取值
remove 移除鍵
class HashTable { constructor() { this._table = []; } // 哈希函數【社區中實踐較好的簡單哈希函數】 hashMethod(key) { if (typeof key === "number") return key; let hash = 5381; for (let i = 0; i < key.length; i += 1) { hash = hash * 33 + key.charCodeAt(i); } return hash % 1013; } put(key, value) { const pos = this.hashMethod(key); this._table[pos] = value; } get(key) { const pos = this.hashMethod(key); return this._table[pos]; } remove(key) { const pos = this.hashMethod(key); delete this._table[pos]; } print() { this._table.forEach((item, index) => { if (item !== undefined) { console.log(index + " --> " + item); } }) } }
當然了,一個簡單的哈希函數,將不同的字符串轉換成整數時,很有可能會出現多個不同字符串轉換后對應同一個整數,這個就需要進行沖突的處理
3.3 處理沖突的方法(1)分離鏈接
分離鏈接法包括為散列表的每一個位置創建一個鏈表并將元素存儲在里面。它是解決沖突的
最簡單的方法,但是它在HashTable實例之外還需要額外的存儲空間
(2)線性探查
當想向表中某個位置加入一個新元素的時候,如果索引 為index的位置已經被占據了,就嘗試index+1的位置。如果index+1的位置也被占據了,就嘗試 index+2的位置,以此類推四、bitMap算法 4.1 bitMap數據結構
bitMap數據結構常用于大量整型數據做去重和查詢,《Bitmap算法》這篇文章中是基于Java語言及數據庫優化進行解釋的圖文教程
bitMap是利用了二進制來描述狀態的一種數據結構,下面介紹其簡單的原理:
(1)思考下面的問題
街邊有8棧路燈,編號分別是1 2 3 4 5 6 7 8 ,其中2號,5號,7號,8號路燈是亮著的,其余的都處于不亮的狀態,請你設計一種簡單的方法來表示這8棧路燈亮與不亮的狀態。
路燈 1 2 3 4 5 6 7 8 狀態 0 1 0 0 1 0 1 1
將狀態轉化為二進制parseInt(1001011, 2);結果為75。一個Number類型的值為32個字節,它可以表示32棧路燈的狀態。這樣在大數據量的處理中,bitMap就有很大的優勢。
(2)位運算介紹
按位與&: 3&7=3【011 & 111 --> 011】
按位或|: 3|7=7【011 | 111 --> 111】
左位移<<: 1<<3=8【1 --> 1000】
(3)實踐
一組數,內容以為 3,6,7,9,請用一個整數來表示這些四個數
var value = 0; value = value | 1<<3; // 1000 value = value | 1<<6; // 1001000 value = value | 1<<7; // 11001000 value = value | 1<<9; // 1011001000 console.log(value); // 712
這樣,十進制數712的二進制形式對應的位數為1的值便為數組中的樹值
4.2 bitMap的實現通過上面的介紹,我們可以實現一個簡單的bitMap類,有下面兩個方法:
addMember添加成員
isExist成員是否存在
分析:
單個數值既能表示0~32的值,若以數組作為基礎,bitMap能容納的成員由數組長度決定64*數組長度
addMember添加成員:數組/位數向下取整表示所在索引,數組/位數取余表示所在二進制的位數
isExist成員是否存在:添加成員的反向計算
我們先實現基礎讀寫位的方法
export const BIT_SIZE = 32; // 設置位的值 export function setBit(bitMap, bit) { const arrIndex = Math.floor(bit / BIT_SIZE); const bitIndex = Math.floor(bit % BIT_SIZE); bitMap[arrIndex] |= (1 << bitIndex); } // 讀取位的值 export function getBit(bitMap, bit) { const arrIndex = Math.floor(bit / BIT_SIZE); const bitIndex = Math.floor(bit % BIT_SIZE); return bitMap[arrIndex] & (1 << bitIndex); }
進而根據上面的方法得到下面的類
class BitMap { constructor(size) { this._bitArr = Array.from({ length: size }, () => 0); } addMember(member) { setBit(this._bitArr, member); } isExist(member) { const isExist = getBit(this._bitArr, member); return Boolean(isExist); } } // 驗證 const bitMap = new BitMap(4); const arr = [0, 3, 5, 6, 9, 34, 23, 78, 99]; for(var i = 0;i < arr.length;i++){ bitMap.addMember(arr[i]); } console.log(bitMap.isExist(3)); // true console.log(bitMap.isExist(7)); // false console.log(bitMap.isExist(78)); // true
注意:這種結構也有其局限性
數據集要求較為緊湊,[1, 1000000]這種結構空間利用過低,不利于發揮bitMap的優勢
僅對整數有效(當然,我們可以通過哈希函數將字符串轉換為整型)
4.3 bitMap的應用(1)大數據排序
要求:有多達10億無序整數,已知最大值為15億,請對這個10億個數進行排序
分析:大數據的排序,傳統的排序方式相對內存占用較大,使用bitMap僅占原內存的(JS中為1/64,Java中為1/32)
實現:模擬大數據實現,如下(最大值為99)
const arr = [0, 6, 88, 7, 73, 34, 10, 99, 22]; const MAX_NUMBER = 99; const ret = []; const bitMap = new BitMap(4); arr.forEach(item => { bitMap.addMember(item); }) for (let i = 0; i <= MAX_NUMBER; i += 1) { if (bitMap.isExist(i)) ret.push(i); } console.log(ret); // [ 0, 6, 7, 10, 22, 34, 73, 88, 99 ]
(2)兩個集合取交集
要求:兩個數組,內容分別為[1, 4, 6, 8, 9, 10, 15], [6, 14, 9, 2, 0, 7],請用BitMap計算他們的交集
分析:利用isExist()來篩選相同項
實現:
const arr1 = [1, 4, 6, 8, 9, 10, 15]; const arr2 = [6, 14, 9, 2, 0, 7]; const intersectionArr = [] const bitMap = new BitMap(); arr1.forEach(item => bitMap.addMember(item)) arr2.forEach(item => { if (bitMap.isExist(item)) { intersectionArr.push(item); } }) console.log(intersectionArr); // [6, 9]
BitMap數據結構的用法原不止如此,我們可以通過哈希函數將字符串轉換成整數,再進行處理。當然,我們應該始終牢記BitMap必須是相對較為緊密的數字,否則無法發揮BitMap的最大功效
上一篇:JS數據結構與算法_鏈表
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108941.html
今天和大家講講JS字典。所謂的JS字典其實和顯示中常用漢語字典不一樣,編程中的字典類似,兩者都有一個特點,就是一一對應(yi yi dui ying),或者說是映射。 日常中的字典通常以**【鍵,值】** 對的形成存儲,主要是由于以鍵值對的形式存儲,這樣的話更有利于可以通過key來獲取value 比如存儲用戶信息: { 'username':'一碗周'...
小編寫這篇文章的主要目的,主要是給大家介紹,關于python中,共現矩陣代碼實現方式的問題,下面就給大家進行詳細的解答?! ython共現矩陣實現 最近在學習python詞庫的可視化,其中有一個依據共現矩陣制作的可視化,感覺十分炫酷,便以此復刻。 項目背景 本人利用搜索引擎爬蟲,以此用來獲取各大博客網站的文章,在進行jieba分詞,得到每篇文章的關鍵詞,對這些關鍵詞進行共現矩陣的可視...
小編寫這篇文章的主要目的,主要是介紹關于Python的一些知識,其中的內容主要還是涉及到其基本的數據類型,那么,到底有多少種的數據類型呢?下面就給大家詳細解答下?! ython中主要有8種數據類型:number(數字)、string(字符串)、list(列表)、tuple(元組)、dict(字典)、set(集合)、Boolean(布爾值)、None(空值)。 其中Python有六個標準的數...
摘要:下一篇數據結構與算法鏈表寫在前面說明數據結構與算法系列文章的代碼和示例均可在此找到原計劃是把你不知道的三部全部看完的,偶然間朋友推薦了數據結構與算法的一套入門視頻,學之。手頭上恰好有學習數據結構與算法的書籍,便轉而先把數據結構與算法學習。 下一篇:JS數據結構與算法_鏈表 寫在前面 說明:JS數據結構與算法 系列文章的代碼和示例均可在此找到 原計劃是把《你不知道的Javascript》...
摘要:小結實現了字典和哈希表感覺沒有想象中那么困難,當然這還是開始。 本系列所有文章:第一篇文章:學習數據結構與算法之棧與隊列第二篇文章:學習數據結構與算法之鏈表第三篇文章:學習數據結構與算法之集合第四篇文章:學習數據結構與算法之字典和散列表第五篇文章:學習數據結構與算法之二叉搜索樹 字典 不是新華字典哦,這里的字典也稱作_映射_,是一種數據結構,跟set集合很相似的一種數據結構,都可以用來...
閱讀 2785·2021-11-04 16:15
閱讀 3458·2021-09-29 09:35
閱讀 4031·2021-09-22 15:45
閱讀 1416·2019-08-30 15:55
閱讀 1687·2019-08-30 15:44
閱讀 2710·2019-08-29 12:56
閱讀 2697·2019-08-26 13:30
閱讀 2168·2019-08-23 17:00