近期在学习数据结构的东西,基于ES6标准,写了一些自定义结构类
数据结构:Typescript版本
1、栈Stack
class Stack {
constructor() {
this.count = 0;
this.items = {};
}
push(element) {
this.items[this.count] = element;
this.count++;
}
pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
isEmpty() {
return this.count === 0;
}
size() {
return this.count;
}
clear() {
/* while (!this.isEmpty()) {
this.pop();
} */
this.items = {};
this.count = 0;
}
toString() {
if (this.isEmpty()) {
return '';
}
let objString = `${this.items[0]}`;
for (let i = 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
栈方法:
push(el or els): 添加元素,只添加到栈顶(可以是一个或多个)
pop: 移除元素,遵循栈原则,移除最后添加的元素,并返回溢出的元素
peek: 返回栈顶的元素
isEmpty: 栈是否为空
clear: 清空栈
size: 栈的元素个数
toString: 转换栈内容为字符串
实例
2、队列和双端队列
2.1、队列
场景:电脑打印文件,添加进入队列的文件按先后顺序打印
class Queue {
constructor() {
this.count = 0;
this.lowestCount = 0;
this.items = {};
}
enqueue(element) {
this.items[this.count] = element;
this.count++;
}
dequeue() {
if (this.isEmpty()) {
return undefined;
}
const result = this.items[this.lowestCount];
delete this.items[this.lowestCount];
this.lowestCount++;
return result;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.lowestCount];
}
isEmpty() {
return this.size() === 0;
}
clear() {
this.items = {};
this.count = 0;
this.lowestCount = 0;
}
size() {
return this.count - this.lowestCount;
}
toString() {
if (this.isEmpty()) {
return '';
}
let objString = `${this.items[this.lowestCount]}`;
for (let i = this.lowestCount + 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
队列方法:
enqueue(el or els): 添加元素到队列,只能向队列尾部添加(可以是一个或多个)
dequeue: 移除队列第一项,队列最先放入的数据,返回被移除的元素
peek: 返回第一个添加的元素
isEmpty: 队列是否为空
clear: 清空队列
size: 队列的元素个数
toString: 输出队列元素转换的字符串
2.2、双端队列
场景:电影院排队,一个人刚买了票,需要再询问一些简单信息,可以直接回到队伍头部;在队伍末尾的人如果不想看电影,可以直接离开队伍
class Deque {
constructor() {
this.count = 0;
this.lowestCount = 0;
this.items = {};
}
addFront(element) {
if (this.isEmpty()) {
this.addBack(element);
} else if (this.lowestCount > 0) {
this.lowestCount--;
this.items[this.lowestCount] = element;
} else {
for (let i = this.count; i > 0; i--) {
this.items[i] = this.items[i - 1];
}
this.count++;
this.items[0] = element;
}
}
addBack(element) {
this.items[this.count] = element;
this.count++;
}
removeFront() {
if (this.isEmpty()) {
return undefined;
}
const result = this.items[this.lowestCount];
delete this.items[this.lowestCount];
this.lowestCount++;
return result;
}
removeBack() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}
peekFront() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.lowestCount];
}
peekBack() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
isEmpty() {
return this.size() === 0;
}
clear() {
this.items = {};
this.count = 0;
this.lowestCount = 0;
}
size() {
return this.count - this.lowestCount;
}
toString() {
if (this.isEmpty()) {
return '';
}
let objString = `${this.items[this.lowestCount]}`;
for (let i = this.lowestCount + 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
双端队列方法
addFront(el): 在双端队列前端添加新元素
addBack(el): 在双端队列后端添加新元素
removeFront: 从双端队列前端移除第一个元素
removeBack: 从双端队列后端移除第一个元素
peekFront: 返回双端队列的前端第一个元素
peekBack: 返回双端队列的后端第一个元素
isEmpty: 双端队列是否为空
clear: 清空双端队列
size: 双端队列元素个数
toString: 转换为字符串输出
实例
3、链表
3.1、单链表
场景:火车,每节车都是链表元素,车间的链接就是指针
// 链表元素节点
Node {
constructor(element, next) {
this.element = element;
this.next = next;
}
}
function defaultEquals(a, b) {
return a === b;
}
LinkedList {
constructor(equalsFn = defaultEquals) {
this.equalsFn = equalsFn;
this.count = 0;
this.head = undefined;
}
push(element) {
const node = new Node(element);
let current;
if (this.head == null) {
// catches null && undefined
this.head = node;
} else {
current = this.head;
while (current.next != null) {
current = current.next;
}
current.next = node;
}
this.count++;
}
getElementAt(index) {
if (index >= 0 && index <= this.count) {
let node = this.head;
for (let i = 0; i < index && node != null; i++) {
node = node.next;
}
return node;
}
return undefined;
}
insert(element, index) {
if (index >= 0 && index <= this.count) {
const node = new Node(element);
if (index === 0) {
const current = this.head;
node.next = current;
this.head = node;
} else {
const previous = this.getElementAt(index - 1);
node.next = previous.next;
previous.next = node;
}
this.count++;
return true;
}
return false;
}
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
this.head = current.next;
} else {
const previous = this.getElementAt(index - 1);
current = previous.next;
previous.next = current.next;
}
this.count--;
return current.element;
}
return undefined;
}
remove(element) {
const index = this.indexOf(element);
return this.removeAt(index);
}
indexOf(element) {
let current = this.head;
for (let i = 0; i < this.size() && current != null; i++) {
if (this.equalsFn(element, current.element)) {
return i;
}
current = current.next;
}
return -1;
}
isEmpty() {
return this.size() === 0;
}
size() {
return this.count;
}
getHead() {
return this.head;
}
clear() {
this.head = undefined;
this.count = 0;
}
toString() {
if (this.head == null) {
return '';
}
let objString = `${this.head.element}`;
let current = this.head.next;
for (let i = 1; i < this.size() && current != null; i++) {
objString = `${objString},${current.element}`;
current = current.next;
}
return objString;
}
}
链表方法
push(el): 向链表尾部添加元素
insert(el, position): 向链表特定位置添加元素
getElementAt(index): 返回链表特定位置元素,如果不存在,返回undefined
remove: 返回链表移除一个元素
indexOf: 返回元素在链表索引,不存在元素返回-1
removeAt(position): 从链表特定位置删除元素
isEmpty: 链表是否为空
size: 链表的元素个数
toString: 转换输出字符串
3.2、双链表
DoublyNode extends Node {
constructor(element, next, prev) {
super(element, next);
this.prev = prev;
}
}
DoublyLinkedList extends LinkedList {
constructor(equalsFn = defaultEquals) {
super(equalsFn);
this.tail = undefined;
}
push(element) {
const node = new DoublyNode(element);
if (this.head == null) {
this.head = node;
this.tail = node; // NEW
} else {
// attach to the tail node // NEW
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
}
this.count++;
}
insert(element, index) {
if (index >= 0 && index <= this.count) {
const node = new DoublyNode(element);
let current = this.head;
if (index === 0) {
if (this.head == null) { // NEW
this.head = node;
this.tail = node; // NEW
} else {
node.next = this.head;
this.head.prev = node; // NEW
this.head = node;
}
} else if (index === this.count) { // last item NEW
current = this.tail;
current.next = node;
node.prev = current;
this.tail = node;
} else {
const previous = this.getElementAt(index - 1);
current = previous.next;
node.next = current;
previous.next = node;
current.prev = node; // NEW
node.prev = previous; // NEW
}
this.count++;
return true;
}
return false;
}
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
this.head = this.head.next;
// if there is only one item, then we update tail as well //NEW
if (this.count === 1) {
// {2}
this.tail = undefined;
} else {
this.head.prev = undefined;
}
} else if (index === this.count - 1) {
// last item //NEW
current = this.tail;
this.tail = current.prev;
this.tail.next = undefined;
} else {
current = this.getElementAt(index);
const previous = current.prev;
// link previous with current's next - skip it to remove
previous.next = current.next;
current.next.prev = previous; // NEW
}
this.count--;
return current.element;
}
return undefined;
}
indexOf(element) {
let current = this.head;
let index = 0;
while (current != null) {
if (this.equalsFn(element, current.element)) {
return index;
}
index++;
current = current.next;
}
return -1;
}
getHead() {
return this.head;
}
getTail() {
return this.tail;
}
clear() {
super.clear();
this.tail = undefined;
}
toString() {
if (this.head == null) {
return '';
}
let objString = `${this.head.element}`;
let current = this.head.next;
while (current != null) {
objString = `${objString},${current.element}`;
current = current.next;
}
return objString;
}
inverseToString() {
if (this.tail == null) {
return '';
}
let objString = `${this.tail.element}`;
let previous = this.tail.prev;
while (previous != null) {
objString = `${objString},${previous.element}`;
previous = previous.prev;
}
return objString;
}
}
双链表方法
push(el): 向链表尾部添加元素
insert(el, position): 向链表特定位置添加元素
getElementAt(index): 返回链表特定位置元素,如果不存在,返回undefined
remove: 返回链表移除一个元素
indexOf: 返回元素在链表索引,不存在元素返回-1
removeAt(position): 从链表特定位置删除元素
isEmpty: 链表是否为空
size: 链表的元素个数
toString: 转换输出字符串
新增方法:
getHead: 返回双链表第一个元素节点
getTail: 返回双链表最后一个元素节点
inverseToString: 返回双链表倒叙字符串
3.3、循环链表
CircularLinkedList extends LinkedList {
constructor(equalsFn = defaultEquals) {
super(equalsFn);
}
push(element) {
const node = new Node(element);
let current;
if (this.head == null) {
this.head = node;
} else {
current = this.getElementAt(this.size() - 1);
current.next = node;
}
// set node.next to head - to have circular list
node.next = this.head;
this.count++;
}
insert(element, index) {
if (index >= 0 && index <= this.count) {
const node = new Node(element);
let current = this.head;
if (index === 0) {
if (this.head == null) {
// if no node in list
this.head = node;
node.next = this.head;
} else {
node.next = current;
current = this.getElementAt(this.size());
// update last element
this.head = node;
current.next = this.head;
}
} else {
const previous = this.getElementAt(index - 1);
node.next = previous.next;
previous.next = node;
}
this.count++;
return true;
}
return false;
}
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
if (this.size() === 1) {
this.head = undefined;
} else {
const removed = this.head;
current = this.getElementAt(this.size() - 1);
this.head = this.head.next;
current.next = this.head;
current = removed;
}
} else {
// no need to update last element for circular list
const previous = this.getElementAt(index - 1);
current = previous.next;
previous.next = current.next;
}
this.count--;
return current.element;
}
return undefined;
}
}
循环链表方法
push(el): 向链表尾部添加元素
insert(el, position): 向链表特定位置添加元素
removeAt(position): 从链表特定位置删除元素
3.4、有序链表
const Compare = {
LESS_THAN: -1,
BIGGER_THAN: 1,
EQUALS: 0
};
function defaultCompare(a, b) {
if (a === b) {
return Compare.EQUALS;
}
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
SortedLinkedList extends LinkedList {
constructor(equalsFn = defaultEquals, compareFn = defaultCompare) {
super(equalsFn);
this.equalsFn = equalsFn;
this.compareFn = compareFn;
}
push(element) {
if (this.isEmpty()) {
super.push(element);
} else {
const index = this.getIndexNextSortedElement(element);
super.insert(element, index);
}
}
insert(element, index = 0) {
if (this.isEmpty()) {
return super.insert(element, index === 0 ? index : 0);
}
const pos = this.getIndexNextSortedElement(element);
return super.insert(element, pos);
}
getIndexNextSortedElement(element) {
let current = this.head;
let i = 0;
for (; i < this.size() && current; i++) {
const comp = this.compareFn(element, current.element);
if (comp === Compare.LESS_THAN) {
return i;
}
current = current.next;
}
return i;
}
}
有序链表方法
push(el): 向链表尾部添加元素
insert(el, position): 向链表特定位置添加元素
getIndexNextSortedElement(el): 返回元素在有序链表的位置,实际位置index+1;如果不存在,返回最后一个元素位置
4、集合
场景:无序唯一
class Set {
constructor() {
this.items = {};
}
add(element) {
if (!this.has(element)) {
this.items[element] = element;
return true;
}
return false;
}
delete(element) {
if (this.has(element)) {
delete this.items[element];
return true;
}
return false;
}
has(element) {
return Object.prototype.hasOwnProperty.call(this.items, element);
}
values() {
return Object.values(this.items);
}
union(otherSet) {
const unionSet = new Set();
this.values().forEach(value => unionSet.add(value));
otherSet.values().forEach(value => unionSet.add(value));
return unionSet;
}
intersection(otherSet) {
const intersectionSet = new Set();
const values = this.values();
const otherValues = otherSet.values();
let biggerSet = values;
let smallerSet = otherValues;
if (otherValues.length - values.length > 0) {
biggerSet = otherValues;
smallerSet = values;
}
smallerSet.forEach(value => {
if (biggerSet.includes(value)) {
intersectionSet.add(value);
}
});
return intersectionSet;
}
difference(otherSet) {
const differenceSet = new Set();
this.values().forEach(value => {
if (!otherSet.has(value)) {
differenceSet.add(value);
}
});
return differenceSet;
}
isSubsetOf(otherSet) {
if (this.size() > otherSet.size()) {
return false;
}
let isSubset = true;
this.values().every(value => {
if (!otherSet.has(value)) {
isSubset = false;
return false;
}
return true;
});
return isSubset;
}
isEmpty() {
return this.size() === 0;
}
size() {
return Object.keys(this.items).length;
}
clear() {
this.items = {};
}
toString() {
if (this.isEmpty()) {
return '';
}
const values = this.values();
let objString = `${values[0]}`;
for (let i = 1; i < values.length; i++) {
objString = `${objString},${values[i].toString()}`;
}
return objString;
}
}
集合的方法
add(el): 向集合添加一个新元素
delete(el): 从集合移除一个元素
has(el): 集合是否包含元素
clear: 清空集合
size: 集合元素个数
values: 返回包含集合所有值的数组
isEmpty: 集合是否为空
toString: 转换集合值为字符串
union(set): 并集
intersection(set): 交集
difference(set): 差集
isSubsetOf(set): 子集
扩展运算符求交集、并集、补集
并集:new Set([...setA, ...setB])
交集:new Set([...setA].filter(x => setB.has(x)))
补集:new Set([...setA].filter(x => !setBhas(x)))
5、字典和散列表
5.1、字典
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
function defaultToString(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}
class Dictionary {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key);
this.table[tableKey] = new ValuePair(key, value);
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.toStrFn(key)];
return valuePair == null ? undefined : valuePair.value;
}
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
remove(key) {
if (this.hasKey(key)) {
delete this.table[this.toStrFn(key)];
return true;
}
return false;
}
values() {
return this.keyValues().map(valuePair => valuePair.value);
}
keys() {
return this.keyValues().map(valuePair => valuePair.key);
}
keyValues() {
return Object.values(this.table);
}
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i++) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}
isEmpty() {
return this.size() === 0;
}
size() {
return Object.keys(this.table).length;
}
clear() {
this.table = {};
}
toString() {
if (this.isEmpty()) {
return '';
}
const valuePairs = this.keyValues();
let objString = `${valuePairs[0].toString()}`;
for (let i = 1; i < valuePairs.length; i++) {
objString = `${objString},${valuePairs[i].toString()}`;
}
return objString;
}
}
字典方法
set(key, value): 向字典添加新元素,如果key存在,value会覆盖当前已经存在的值
get(key): 获取当前key的value
remove(key): 使用key来删除对应字典内的键值对
hasKey(key): 是否在字典中存在当前key对应的键值对
clear: 清空字典
size: 返回字典的元素个数
isEmpty: 字典是否为空
toString: 转换字典键值对为字符串
keys: 字典所有键名,输出为数组
values: 字典所有键值对的值,输出为数组
keyValues: 字典所有的[键, 值]对
forEach(cb): 迭代字典所有元素,cb是回调函数,有两个参数:key和value。在回调函数返回false中止循环
5.2、散列表
场景:数据库建索引;javascript内部使用散列表来表示每个对象
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
function defaultToString(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}
class HashTable {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// 创建散列表值
djb2HashCode(key) {
const tableKey = this.toStrFn(key);
let hash = 5381;
for (let i = 0; i < tableKey.length; i++) {
hash = (hash * 33) + tableKey.charCodeAt(i);
}
return hash % 1013;
}
hashCode(key) {
return this.djb2HashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
this.table[position] = new ValuePair(key, value);
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.hashCode(key)];
return valuePair == null ? undefined : valuePair.value;
}
remove(key) {
const hash = this.hashCode(key);
const valuePair = this.table[hash];
if (valuePair != null) {
delete this.table[hash];
return true;
}
return false;
}
getTable() {
return this.table;
}
isEmpty() {
return this.size() === 0;
}
size() {
return Object.keys(this.table).length;
}
clear() {
this.table = {};
}
toString() {
if (this.isEmpty()) {
return '';
}
const keys = Object.keys(this.table);
let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`;
for (let i = 1; i < keys.length; i++) {
objString = `${objString},{${keys[i]} => ${this.table[keys[i]].toString()}}`;
}
return objString;
}
}
散列表方法
put(key, value): 向散列表增加元素
remove(key): 键值在散列表中移除值
get(key): 键值检索特定的值
loseloseHashCode(key): 散列函数
hashCode(key): 返回键名对应的键值对数据
getTable: 返回散列表
isEmpty: 散列表是否为空
size: 散列表元素个数
clear: 清空散列表
toString: 转换键值对为字符串
5.3、分离链接散列表
一般散列表对于重复的生成散列值会覆盖数据,也就是返回的是相同散列值最后添加那个元素。分离链接在每个散列值位置创建一个链表来储存元素,不会覆盖
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
function defaultToString(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}
class HashTableSeparateChaining {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// 创建散列表值
djb2HashCode(key) {
const tableKey = this.toStrFn(key);
let hash = 5381;
for (let i = 0; i < tableKey.length; i++) {
hash = (hash * 33) + tableKey.charCodeAt(i);
}
return hash % 1013;
}
hashCode(key) {
return this.djb2HashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) {
// 单向链表
this.table[position] = new LinkedList();
}
this.table[position].push(new ValuePair(key, value));
return true;
}
return false;
}
get(key) {
const position = this.hashCode(key);
const linkedList = this.table[position];
if (linkedList != null && !linkedList.isEmpty()) {
let current = linkedList.getHead();
while (current != null) {
if (current.element.key === key) {
return current.element.value;
}
current = current.next;
}
}
return undefined;
}
remove(key) {
const position = this.hashCode(key);
const linkedList = this.table[position];
if (linkedList != null && !linkedList.isEmpty()) {
let current = linkedList.getHead();
while (current != null) {
if (current.element.key === key) {
linkedList.remove(current.element);
if (linkedList.isEmpty()) {
delete this.table[position];
}
return true;
}
current = current.next;
}
}
return false;
}
isEmpty() {
return this.size() === 0;
}
size() {
let count = 0;
Object.values(this.table).forEach(linkedList => {
count += linkedList.size();
});
return count;
}
clear() {
this.table = {};
}
getTable() {
return this.table;
}
toString() {
if (this.isEmpty()) {
return '';
}
const keys = Object.keys(this.table);
let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`;
for (let i = 1; i < keys.length; i++) {
objString = `${objString},{${keys[i]} => ${this.table[
keys[i]
].toString()}}`;
}
return objString;
}
}
分离链接散列表方法
方法使用同一般散列表方法,重写了put、get、remove方法
5.4、线性探测
也是为了处理一般散列表覆盖值的问题,线性是因为不创建额外的数据结构,直接储存在表中
class HashTableLinearProbing {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// 创建散列表值
djb2HashCode(key) {
const tableKey = this.toStrFn(key);
let hash = 5381;
for (let i = 0; i < tableKey.length; i++) {
hash = (hash * 33) + tableKey.charCodeAt(i);
}
return hash % 1013;
}
hashCode(key) {
return this.djb2HashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) {
this.table[position] = new ValuePair(key, value);
} else {
let index = position + 1;
while (this.table[index] != null) {
index++;
}
this.table[index] = new ValuePair(key, value);
}
return true;
}
return false;
}
get(key) {
const position = this.hashCode(key);
if (this.table[position] != null) {
if (this.table[position].key === key) {
return this.table[position].value;
}
let index = position + 1;
while (this.table[index] != null && this.table[index].key !== key) {
index++;
}
if (this.table[index] != null && this.table[index].key === key) {
return this.table[position].value;
}
}
return undefined;
}
remove(key) {
const position = this.hashCode(key);
if (this.table[position] != null) {
if (this.table[position].key === key) {
delete this.table[position];
this.verifyRemoveSideEffect(key, position);
return true;
}
let index = position + 1;
while (this.table[index] != null && this.table[index].key !== key) {
index++;
}
if (this.table[index] != null && this.table[index].key === key) {
delete this.table[index];
this.verifyRemoveSideEffect(key, index);
return true;
}
}
return false;
}
verifyRemoveSideEffect(key, removedPosition) {
const hash = this.hashCode(key);
let index = removedPosition + 1;
while (this.table[index] != null) {
const posHash = this.hashCode(this.table[index].key);
if (posHash <= hash || posHash <= removedPosition) {
this.table[removedPosition] = this.table[index];
delete this.table[index];
removedPosition = index;
}
index++;
}
}
isEmpty() {
return this.size() === 0;
}
size() {
return Object.keys(this.table).length;
}
clear() {
this.table = {};
}
getTable() {
return this.table;
}
toString() {
if (this.isEmpty()) {
return '';
}
const keys = Object.keys(this.table);
let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`;
for (let i = 1; i < keys.length; i++) {
objString = `${objString},{${keys[i]} => ${this.table[
keys[i]
].toString()}}`;
}
return objString;
}
}
线性探测散列表方法
使用方法同一般散列表方法
5.5、ES6实现的Map类与字典对比
原生Map使用方式:
const map = new Map();
map.set('whh', 'whh@163.com');
map.set('lsd', 'lsd@163.com');
map.set('lbb', 'lbb@163.com');
console.log(map.has('whh')); // true
console.log(map.size); // 3
console.log(map.keys()); // {"whh", "lsd", "lbb"}
console.log(map.values()); // {"whh@163.com", "lsd@163.com", "lbb@163.com"}
console.log(map.get('lsd')); // "lsd@163.com"
Map类的values和keys方法返回的是Iterator,不是键值对构成的数组。size是属性,不是方法,删除元素用delete(key)
5.6、ES6中的WeakMap和WeakSet
这两个类是Map和Set的弱化版本:
1、这两个类没有entries、keys和values等方法;
2、只能用对象作为键;
3、创建这两个类主要是为了性能,没有强引用键,使得js的垃圾回收可以直接清除入口;
4、没有entries、keys和values等方法,实例不知道原始类内部情况,可以用来封装类的私有属性
6、递归
一种特殊的操作树和图结构的方法,递归迭代
递归引起的栈溢出问题
递归引起的重复计算问题
7、树
非顺序数据结构,父级节点下可以有多个或者零个子节点
7.1、二叉树
父级节点下最多两个子节点
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
function defaultCompare(a, b) {
if (a === b) {
return Compare.EQUALS;
}
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
const Compare = {
LESS_THAN: -1,
BIGGER_THAN: 1,
EQUALS: 0
};
class BinarySearchTree {
constructor(compareFn = defaultCompare) {
this.compareFn = compareFn;
this.root = undefined;
}
insert(key) {
// special case: first key
if (this.root == null) {
this.root = new Node(key);
} else {
this.insertNode(this.root, key);
}
}
insertNode(node, key) {
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
if (node.left == null) {
node.left = new Node(key);
} else {
this.insertNode(node.left, key);
}
} else if (node.right == null) {
node.right = new Node(key);
} else {
this.insertNode(node.right, key);
}
}
getRoot() {
return this.root;
}
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node == null) {
return false;
}
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
return this.searchNode(node.left, key);
} else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
return this.searchNode(node.right, key);
}
return true;
}
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node != null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node != null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node != null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current != null && current.right != null) {
current = current.right;
}
return current;
}
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node == null) {
return undefined;
}
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
node.left = this.removeNode(node.left, key);
return node;
} else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
node.right = this.removeNode(node.right, key);
return node;
}
// key is equal to node.item
// handle 3 special conditions
// 1 - a leaf node
// 2 - a node with only 1 child
// 3 - a node with 2 children
// case 1
if (node.left == null && node.right == null) {
node = undefined;
return node;
}
// case 2
if (node.left == null) {
node = node.right;
return node;
} else if (node.right == null) {
node = node.left;
return node;
}
// case 3
const aux = this.minNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
二叉树方法
insert(key):插入新的键
getRoot:返回根节点
search(key):查找键,返回true和false
inOrderTraverse:中序遍历节点
perOrderTraverse:先序遍历节点
postOrderTraverse:后序遍历节点
min:树中最小的键
max:树中最大的键
remove(key):删除键
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。