We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
想要熟练掌握位运算,记住下面几个公式即可:
n & (n - 1)
n & -n
n >> 1
n & 1 == 1 | 0
n & ~n = 0
思路:
回溯,时间复杂度 O(n!),空间复杂度 O(n)
位运算,时间复杂度 O(n!),空间复杂度 O(n),由于位运算更接近计算机底层,所以运行速度会更快,位运算也是N-Queens问题的终极解决方案,算法的核心要点如下:
availPos = (~(cols | pie | na)) & ((1 << n) - 1)
p = availPos & -availPos
availPos &= (availPos - 1)
dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1)
class Solution { private int count = 0; public int totalNQueens(int n) { dfs(n, 0, 0, 0, 0); return count; } private void dfs(int n, int row, int cols, int pie, int na) { if (row == n) { count++; return; } int availPos = (~(cols | pie | na)) & ((1 << n) - 1); while (availPos != 0) { int p = availPos & -availPos; availPos &= (availPos - 1); dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1); } } }
class Solution: def totalNQueens(self, n: int) -> int: self.count = 0 self.dfs(n, 0, 0, 0, 0) return self.count def dfs(self, n, row, cols, pie, na): if row == n: self.count += 1 return availPos = (~(cols | pie | na)) & ((1 << n) - 1) while availPos: p = availPos & -availPos availPos &= (availPos - 1) self.dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1)
布隆过滤器由一个很长的二进制向量和一系列随机映射函数组成。用于检索一个元素是否在一个集合中。
布隆过滤器是一个快速判断元素是否存在集合的算法,特点是:
最近最少使用缓存替换策略,是一种 缓存替换策略,其他缓存策略还有 FIFO、LFU、RR 等等。
LRU:Latest Recently Used 最近最少使用。
接下来让我们自己造轮子,硬核实现一个 LRU Cache,它应该支持以下两个操作:
get(key)
put(key, value)
还有两个需求:
增删 O(1),我们第一个想到要用 LinkedList,访问 O(1),我们第一个想到要用 HashTable,那么怎么把两者结合起来呢?让我们从思考这两个问题开始:
先回答第一个问题,因为我们需要删除数据,链表删除节点需要找到当前节点的前驱,单链表想要找到节点前驱需要从头开始遍历,存在 O(n) 的时间复杂度;而双向链表本身就保存了节点的前驱,删除时间复杂度就是 O(1),所以我们使用双向链表。
接下来回答第二个问题,由于缓存空间有限,当缓存存满时,根据 LRU 策略,我们需要移除缓存里保存最久的未被访问节点,比如上图中的 A 和 B,与此同时,我们还需要移除 HashMap 中的该节点(已不在缓存中,下次无法查到),如果节点只存 value,那么 HashMap 就无法移除该节点(哈希表删除节点需要 key, map.remove(key)),所以链表节点需要同时存 key 和 value,就像下图这样。
map.remove(key)
解答了这两个问题,我们就可以开始动手了,先实现一个同时保存 key 和 value 的双向链表。
class Node { public int key, val; public Node next, prev; public Node(int k, int v) { this.key = k; this.val = v; } } // 双向链表 class DoubleLinkedList { private Node head, tail; // 头尾虚节点 private int size; // 链表元素数 public DoubleLinkedList() { head = new Node(0, 0); tail = new Node(0, 0); head.next = tail; tail.prev = head; size = 0; } // 在链表头部添加节点 public void addFirst(Node node) { node.next = head.next; node.prev = head; head.next.prev = node; head.next = node; size++; } // 删除链表中的节点(该节点一定存在) public void remove(Node node) { node.prev.next = node.next; node.next.prev = node.prev; size--; } // 删除链表中最后一个节点,并返回该节点 public Node removeLast() { if (tail.prev == head) return null; Node last = tail.prev; remove(last); return last; } // 返回链表长度 public int size() { return size; } }
接下来就是正式实现 LRU Cache 了,核心逻辑写在注释上了。
class LRUCache { private HashMap<Integer, Node> map; private DoubleLinkedList cache; private int cap; // 最大容量 public LRUCache(int capacity) { this.cap = capacity; map = new HashMap<>(); cache = new DoubleLinkedList(); } // 访问节点 public int get(int key) { if (!map.containsKey(key)) { return -1; } int val = map.get(key).val; // 使用put方法把最近访问的节点提前 put(key, val); return val; } public void put(int key, int value) { // 生成新的节点 Node node = new Node(key, value); if (map.containsKey(key)) { // 删除旧的节点,新的插到头部 cache.remove(map.get(key)); cache.addFirst(node); // 更新 map 中对应的数据 map.put(key, node); } else { // 如果缓存满了 if (cap == cache.size()) { // 删除链表最后一个数据 Node last = cache.removeLast(); // 哈希表也要删 map.remove(last.key); } // 新节点添加到头部 cache.addFirst(node); map.put(key, node); } } }
最后多说一句,如果我们把这两部分合起来,其实就得到了 LeetCode 146 的最佳题解,我想这也是面试题中对 LRU Cache 实现的最高要求了。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
位运算
核心要领
想要熟练掌握位运算,记住下面几个公式即可:
n & (n - 1)
n & -n
n >> 1
n & 1 == 1 | 0
n & ~n = 0
经典例题
52. N-Queens II
思路:
回溯,时间复杂度 O(n!),空间复杂度 O(n)
位运算,时间复杂度 O(n!),空间复杂度 O(n),由于位运算更接近计算机底层,所以运行速度会更快,位运算也是N-Queens问题的终极解决方案,算法的核心要点如下:
availPos = (~(cols | pie | na)) & ((1 << n) - 1)
考虑整行、两条对角线,得到当前能放皇后的位置,用最低的 n 位表示p = availPos & -availPos
取得 availPos 最低位的1,用来放皇后availPos &= (availPos - 1)
清除最低位的 1,表示皇后已放dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1)
对 cols, pie, na 可放皇后的位置做相应的更新,下探下一层 ( Drill Down )Java 实现
Python 实现
布隆过滤器
本质
布隆过滤器由一个很长的二进制向量和一系列随机映射函数组成。用于检索一个元素是否在一个集合中。
要点
布隆过滤器是一个快速判断元素是否存在集合的算法,特点是:
LRU Cache
最近最少使用缓存替换策略,是一种 缓存替换策略,其他缓存策略还有 FIFO、LFU、RR 等等。
LRU:Latest Recently Used 最近最少使用。
硬核实现 LRU Cache
接下来让我们自己造轮子,硬核实现一个 LRU Cache,它应该支持以下两个操作:
get(key)
- 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。put(key, value)
- 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。还有两个需求:
增删 O(1),我们第一个想到要用 LinkedList,访问 O(1),我们第一个想到要用 HashTable,那么怎么把两者结合起来呢?让我们从思考这两个问题开始:
先回答第一个问题,因为我们需要删除数据,链表删除节点需要找到当前节点的前驱,单链表想要找到节点前驱需要从头开始遍历,存在 O(n) 的时间复杂度;而双向链表本身就保存了节点的前驱,删除时间复杂度就是 O(1),所以我们使用双向链表。
接下来回答第二个问题,由于缓存空间有限,当缓存存满时,根据 LRU 策略,我们需要移除缓存里保存最久的未被访问节点,比如上图中的 A 和 B,与此同时,我们还需要移除 HashMap 中的该节点(已不在缓存中,下次无法查到),如果节点只存 value,那么 HashMap 就无法移除该节点(哈希表删除节点需要 key,
map.remove(key)
),所以链表节点需要同时存 key 和 value,就像下图这样。解答了这两个问题,我们就可以开始动手了,先实现一个同时保存 key 和 value 的双向链表。
接下来就是正式实现 LRU Cache 了,核心逻辑写在注释上了。
最后多说一句,如果我们把这两部分合起来,其实就得到了 LeetCode 146 的最佳题解,我想这也是面试题中对 LRU Cache 实现的最高要求了。
参考链接
The text was updated successfully, but these errors were encountered: