复杂数据结构
实现一个并查集
并查集的实现比较简单,不过就是使用递归,涉及到路径压缩和权重对比等操作,比较简单
class BingchaSet {
constructor(n) {
this.rank = [];
this.father = [];
for (let i = 0; i < n; i++) {
this.rank[i] = 1;
this.father[i] = i;
}
}
find(i) {
if (this.father[i] === i) {
return i;
}
const parent = this.find(this.father[i]);
this.father[i] = parent;
return parent;
}
union(i, j) {
const parentI = this.find(i);
const parentJ = this.find(j);
if (this.rank[parentI] <= this.rank[parentJ]) {
this.father[parentI] = parentJ;
} else {
this.father[parentJ] = parentI;
}
if (this.rank[parentI] === this.rank[parentJ] && parentI != parentI) {
this.rank[parentJ]++;
}
}
}
export default BingchaSet;
684. 冗余连接
题目描述
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。
例子1
输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
1
/ \
2 - 3
例子2
输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
| |
4 - 3
注意:
1 输入的二维数组大小在 3 到 1000。
2 二维数组中的整数在1到N之间,其中N是输入数组的大小。
。
思考
1 这里很明显使用并查集来解决,因为前面我们已经介绍了什么是并查集。
题目本身并不难,就是判断是否有环,可以使用并查集的find方法,如果发现两个节点的祖先都是同一个,那就是我们需要找的。
参考实现2
实现1
/**
* @param {number[][]} edges
* @return {number[]}
*/
import BingchaSet from "../bingchaSet/index";
// Runtime: 368 ms, faster than 5.94% of JavaScript online submissions for Redundant Connection.
// Memory Usage: 48.9 MB, less than 5.45% of JavaScript online submissions for Redundant Connection.
export default (edges) => {
const len = edges.length;
const bingset = new BingchaSet(len);
for (let [u, v] of edges) {
const x = bingset.find(u - 1);
const y = bingset.find(v - 1);
if (x !== y) {
bingset.merge(u - 1, v - 1);
} else {
return [u, v];
}
}
};
146. LRU 缓存机制
题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
例子1
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
注意:
1 1 <= capacity <= 3000
2 0 <= key <= 3000
。
3 0 <= value <= 10^4
4 最多调用 3 * 10^4 次 get 和 put
思考
1 这里最难的就是如何找出最近最少使用的key值?
看了题解了解到,Map.keys()是按照set的顺序倒序输出的,这样就可以利用这一特性来进行解决寻找最近最少使用的key值
参考实现1
实现1
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.map = new Map();
this.max = capacity;
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
console.log(this.map, this.keyMap);
if (this.map.has(key)) {
const val = this.map.get(key);
this.map.delete(key);
this.map.set(key, val);
return val;
} else {
return -1;
}
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
// Runtime: 196 ms, faster than 64.75% of JavaScript online submissions for LRU Cache.
// Memory Usage: 51.3 MB, less than 59.00% of JavaScript online submissions for LRU Cache.
LRUCache.prototype.put = function (key, value) {
console.log(this.map.size, this.max);
if (this.map.has(key)) {
this.map.delete(key);
}
this.map.set(key, value);
if (this.map.size > this.max) {
const firstKey = this.map.keys().next().value;
this.cache.delete(firstKey);
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
716. 最大栈
题目描述
设计一个支持 push,pop,top,peekMax 和 popMax 的 stack
1 peekMax 获取栈中的最大元素
2 popMax 获取栈中的最大元素并且删除
例子1
MaxStack stack = new MaxStack();
stack.push(5);
stack.push(1);
stack.push(5);
stack.top(); -> 5
stack.popMax(); -> 5
stack.top(); -> 1
stack.peekMax(); -> 5
stack.pop(); -> 1
stack.top(); -> 5
思考
1 使用两个栈,一个存储正常的push进来的数字,一个maxStack用来存储每次push进来的最大元素
参考实现1
实现1
class MaxStack {
constructor() {
this.normalStack = [];
this.maxStack = [];
}
//O(1);
push(val) {
this.normalStack.push(val);
const len = this.maxStack.length;
if (len === 0 || (len > 0 && val >= this.maxStack[len - 1])) {
this.maxStack.push(val);
} else {
this.maxStack.push(this.maxStack[len - 1]);
}
}
//O(1);
pop() {
this.maxStack.pop();
return this.normalStack.pop();
}
//O(1);
// 获取当前最顶上的元素,但是不删除
top() {
if (this.normalStack.length > 0) {
return this.normalStack[this.normalStack.length - 1];
} else {
return -1;
}
}
//O(1);
// 获取最大的stack中max元素
peekMax() {
if (this.maxStack.length > 0) {
return this.maxStack[this.maxStack.length - 1];
} else {
return -1;
}
}
//O(n);
// 删除最大的元素
popMax() {
const res = this.peekMax();
const temp = [];
while (this.top() !== res) {
const normalEle = this.normalStack.pop();
temp.shift(normalEle);
this.maxStack.pop();
}
this.pop();
for (let val of temp) {
this.push(val);
}
return res;
}
}
export default MaxStack;
380. Insert Delete GetRandom O(1)
题目描述
设计一个插入、删除和随机取值均为 O(1) 时间复杂度的数据结构
例子1
输入:
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出:
[null, true, false, true, 2, true, false, 2]
思考
1 使用一个map就可以了
参考实现1
实现1
/**
* Initialize your data structure here.
*/
var RandomizedSet = function () {
this.map = new Map();
};
/**
* Inserts a value to the set. Returns true if the set did not already contain the specified element.
* @param {number} val
* @return {boolean}
*/
RandomizedSet.prototype.insert = function (val) {
if (!this.map.has(val)) {
this.map.set(val, val);
return true;
} else {
return false;
}
};
/**
* Removes a value from the set. Returns true if the set contained the specified element.
* @param {number} val
* @return {boolean}
*/
RandomizedSet.prototype.remove = function (val) {
if (this.map.has(val)) {
this.map.delete(val);
return true;
} else {
return false;
}
};
/**
* Get a random element from the set.
* @return {number}
*/
// Runtime: 228 ms, faster than 24.73% of JavaScript online submissions for Insert Delete GetRandom O(1).
// Memory Usage: 49.3 MB, less than 15.96% of JavaScript online submissions for Insert Delete GetRandom O(1).
RandomizedSet.prototype.getRandom = function () {
const random = Math.floor(this.map.size * Math.random());
let count = 0;
for (let [key, val] of this.map) {
if (count === random) {
return val;
} else {
++count;
}
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* var obj = new RandomizedSet()
* var param_1 = obj.insert(val)
* var param_2 = obj.remove(val)
* var param_3 = obj.getRandom()
*/
432. 全 O(1) 的数据结构
题目描述
1 Inc(key) - 插入一个新的值为 1 的 key。或者使一个存在的 key 增加一,保证 key 不为空字符串。
2 Dec(key) - 如果这个 key 的值是 1,那么把他从数据结构中移除掉。否则使一个存在的 key 值减一。如果这个 key 不存在,这个函数不做任何事情。key 保证不为空字符串。
3 GetMaxKey() - 返回 key 中值最大的任意一个。如果没有元素存在,返回一个空字符串"" 。
4 GetMinKey() - 返回 key 中值最小的任意一个。如果没有元素存在,返回一个空字符串""。
思考
1 使用两个map,一个保存key对用的次数,一个保存次数对应的key值对象,然后通过一个双向链表链接起来,这样就可以达到取最大的key和最小的key都是O(1)
如果需要O(1), map和双向链表都是一种选择,
参考实现1
实现1
/**
* Initialize your data structure here.
*/
class Bucket {
constructor(val, preBucket, nextBucket) {
this.count = val;
this.next = nextBucket || null;
this.pre = preBucket || null;
this.keySet = new Set();
}
}
var AllOne = function () {
// key2count map
this.key2countMap = new Map();
// sameCount map, 每个count对应是一个双向的Bucket链表
this.sameCountBucketMap = new Map();
// this.head 指向最小的val
this.head = new Bucket(Number.MIN_VALUE);
// this.tail 指向最大的val
this.tail = new Bucket(Number.MAX_VALUE);
this.head.next = this.tail;
this.tail.pre = this.head;
};
/**
* Inserts a new key <Key> with value 1. Or increments an existing key by 1.
* @param {string} key
* @return {void}
*/
AllOne.prototype.inc = function (key) {
// 如果存在,则把它加入到相同count的链表中去
if (this.key2countMap.has(key)) {
this.changeKey(key, 1);
} else {
this.key2countMap.set(key, 1);
if (this.head.next.count !== 1) {
this.addBucketAfter(new Bucket(1), this.head);
}
this.head.next.keySet.add(key);
this.sameCountBucketMap.set(1, this.head.next);
}
};
/**
* Decrements an existing key by 1. If Key's value is 1, remove it from the data structure.
* @param {string} key
* @return {void}
*/
AllOne.prototype.dec = function (key) {
if (this.key2countMap.has(key)) {
const count = this.key2countMap.get(key);
if (count === 1) {
this.key2countMap.delete(key);
this.removeKeyFromBucket(this.sameCountBucketMap.get(count), key);
} else {
this.changeKey(key, -1);
}
}
};
/**
* Returns one of the keys with maximal value.
* @return {string}
*/
AllOne.prototype.getMaxKey = function () {
return this.tail.pre === this.head ? "" : this.tail.pre.keySet[Symbol.iterator]().next().value;
};
/**
* Returns one of the keys with Minimal value.
* @return {string}
*/
AllOne.prototype.getMinKey = function () {
return this.head.next === this.tail ? "" : this.head.next.keySet[Symbol.iterator]().next().value;
};
// 把相同count的Bucket加入到sameCountBucketMap
AllOne.prototype.changeKey = function (key, offset) {
// 首先修改原来存在key值
const count = this.key2countMap.get(key);
this.key2countMap.set(key, count + offset);
const curBucket = this.sameCountBucketMap.get(count);
let newBucket;
if (this.sameCountBucketMap.has(count + offset)) {
newBucket = this.sameCountBucketMap.get(count + offset);
} else {
// add new Bucket
newBucket = new Bucket(count + offset);
this.sameCountBucketMap.set(count + offset, newBucket);
this.addBucketAfter(newBucket, offset === 1 ? curBucket : curBucket.pre);
}
newBucket.keySet.add(key);
this.removeKeyFromBucket(curBucket, key);
};
AllOne.prototype.removeKeyFromBucket = function (bucket, key) {
bucket.keySet.delete(key);
if (bucket.keySet.size === 0) {
this.removeBucketFromList(bucket);
this.sameCountBucketMap.delete(bucket.count);
}
};
AllOne.prototype.removeBucketFromList = function (bucket) {
bucket.pre.next = bucket.next;
bucket.next.pre = bucket.pre;
bucket.next = null;
bucket.pre = null;
};
AllOne.prototype.addBucketAfter = function (newBucket, preBucket) {
newBucket.pre = preBucket;
newBucket.next = preBucket.next;
preBucket.next.pre = newBucket;
preBucket.next = newBucket;
};
/**
* Your AllOne object will be instantiated and called as such:
* var obj = new AllOne()
* obj.inc(key)
* obj.dec(key)
* var param_3 = obj.getMaxKey()
* var param_4 = obj.getMinKey()
*/