622. 设计循环队列
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
- MyCircularQueue(k): 构造器,设置队列长度为 k 。
- Front: 从队首获取元素。如果队列为空,返回 -1 。
- Rear: 获取队尾元素。如果队列为空,返回 -1 。
- enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
- deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
- isEmpty(): 检查循环队列是否为空。
- isFull(): 检查循环队列是否已满。
type MyCircularQueue struct {
data []int
size int
headIdx int
count int
}
func Constructor(k int) MyCircularQueue {
q := MyCircularQueue{
data: make([]int, k),
size: k,
headIdx: 0,
count: 0,
}
return q
}
func (q *MyCircularQueue) EnQueue(value int) bool {
if q.count == q.size {
return false
}
insertIdx := (q.headIdx + q.count) % q.size
q.data[insertIdx] = value
q.count++
return true
}
func (q *MyCircularQueue) DeQueue() bool {
if q.count == 0 { return false }
q.headIdx = (q.headIdx + 1) % q.size
q.count--
return true
}
func (q *MyCircularQueue) Front() int {
if q.count == 0 { return -1 }
return q.data[q.headIdx]
}
func (q *MyCircularQueue) Rear() int {
if q.count == 0 { return -1 }
return q.data[(q.headIdx + q.count - 1) % q.size]
}
func (q *MyCircularQueue) IsEmpty() bool {
return q.count == 0
}
func (q *MyCircularQueue) IsFull() bool {
return q.count == q.size
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* obj := Constructor(k);
* param_1 := obj.EnQueue(value);
* param_2 := obj.DeQueue();
* param_3 := obj.Front();
* param_4 := obj.Rear();
* param_5 := obj.IsEmpty();
* param_6 := obj.IsFull();
*/
705. 设计哈希集合
不使用任何内建的哈希表库设计一个哈希集合(HashSet)。
实现 MyHashSet 类:
- void add(key) 向哈希集合中插入值 key 。
- bool contains(key) 返回哈希集合中是否存在这个值 key 。
- void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
type MyHashSet struct {
data []int
}
func Constructor() MyHashSet {
hs := MyHashSet{
data: make([]int, 40000),
}
return hs
}
func (s *MyHashSet) Add(key int) {
// 先分成32个桶,桶里用一个int的32位表示32个值int为4个字节,有32位可以用来表示状态
idx, offset := s.getLoc(key)
// 将0000 0000 0000 0000 0000 0000 0000 0001其中最后一位1移动到对应位置
s.data[idx] |= 1 << offset
}
func (s *MyHashSet) getLoc(key int) (int, int) {
bucketIdx := key / 32
offset := key % 32
return bucketIdx, offset
}
func (s *MyHashSet) Remove(key int) {
idx, offset := s.getLoc(key)
// 将最后一位1移动到目标位之后,~按位取反,再&上,从而将对应位置上的1清掉
v := ^(1 << offset)
s.data[idx] &= ^(1 << offset)
}
func (s *MyHashSet) Contains(key int) bool {
idx, offset := s.getLoc(key)
v := s.data[idx]
return (v >> offset) & 1 == 1
}
/**
* Your MyHashSet object will be instantiated and called as such:
* obj := Constructor();
* obj.Add(key);
* obj.Remove(key);
* param_3 := obj.Contains(key);
*/
706. 设计哈希映射
不使用任何内建的哈希表库设计一个哈希映射(HashMap)。
实现 MyHashMap 类:
- MyHashMap() 用空映射初始化对象
- void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的- 值 value 。
- int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
- void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。
type MyHashMap struct {
data []list.List
}
func Constructor() MyHashMap {
m := MyHashMap{
data: make([]list.List, 769),
}
return m
}
type node struct {
key, value int
}
func (m *MyHashMap) Put(key int, value int) {
h := m.hash(key)
for e := m.data[h].Front(); e != nil; e = e.Next() {
if et := e.Value.(node); et.key == key {
e.Value = node{key, value}
return
}
}
m.data[h].PushBack(node{key, value})
}
func (m *MyHashMap) hash(key int) int {
return key % 769
}
func (m *MyHashMap) Get(key int) int {
h := m.hash(key)
for e := m.data[h].Front(); e != nil; e = e.Next() {
if et := e.Value.(node); et.key == key {
return et.value
}
}
return -1
}
func (m *MyHashMap) Remove(key int) {
h := m.hash(key)
for e := m.data[h].Front(); e != nil; e = e.Next() {
if e.Value.(node).key == key {
m.data[h].Remove(e)
}
}
}
/**
* Your MyHashMap object will be instantiated and called as such:
* obj := Constructor();
* obj.Put(key,value);
* param_2 := obj.Get(key);
* obj.Remove(key);
*/
641. 设计循环双端队列
设计实现双端队列。
实现 MyCircularDeque 类:
- MyCircularDeque(int k) :构造函数,双端队列最大为 k 。
- boolean insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true ,否则返回 false 。
- boolean insertLast() :将一个元素添加到双端队列尾部。如果操作成功返回 true ,否则返回 false 。
- boolean deleteFront() :从双端队列头部删除一个元素。 如果操作成功返回 true ,否则返回 false 。
- boolean deleteLast() :从双端队列尾部删除一个元素。如果操作成功返回 true ,否则返回 false 。
- int getFront() ):从双端队列头部获得一个元素。如果双端队列为空,返回 -1 。
- int getRear() :获得双端队列的最后一个元素。 如果双端队列为空,返回 -1 。
- boolean isEmpty() :若双端队列为空,则返回 true ,否则返回 false 。
- boolean isFull() :若双端队列满了,则返回 true ,否则返回 false 。
type MyCircularDeque struct {
head, tail *Node
cap, size int
}
type Node struct {
prev, next *Node
val int
}
func Constructor(k int) MyCircularDeque {
head, tail := new(Node), new(Node)
head.next = tail
tail.prev = head
q := MyCircularDeque{
cap: k,
head: head,
tail: tail,
}
return q
}
func (q *MyCircularDeque) InsertFront(value int) bool {
if q.IsFull() {
return false
}
q.size++
node := &Node{val: value}
head := q.head
next := head.next
head.next, node.prev = node, head
node.next, next.prev = next, node
return true
}
func (q *MyCircularDeque) InsertLast(value int) bool {
if q.IsFull() {
return false
}
q.size++
node := &Node{val: value}
tail := q.tail
prev := tail.prev
prev.next, node.prev = node, prev
tail.prev, node.next = node, tail
return true
}
func (q *MyCircularDeque) DeleteFront() bool {
if q.IsEmpty() {
return false
}
q.size--
head := q.head
head.next = head.next.next
head.next.prev = head
return true
}
func (q *MyCircularDeque) DeleteLast() bool {
if q.IsEmpty() {
return false
}
q.size--
tail := q.tail
tail.prev = tail.prev.prev
tail.prev.next = tail
return true
}
func (q *MyCircularDeque) GetFront() int {
if q.IsEmpty() {
return -1
}
return q.head.next.val
}
func (q *MyCircularDeque) GetRear() int {
if q.IsEmpty() {
return -1
}
return q.tail.prev.val
}
func (q *MyCircularDeque) IsEmpty() bool {
return q.head.next == q.tail
}
func (q *MyCircularDeque) IsFull() bool {
return q.size == q.cap
}
/**
* Your MyCircularDeque object will be instantiated and called as such:
* obj := Constructor(k);
* param_1 := obj.InsertFront(value);
* param_2 := obj.InsertLast(value);
* param_3 := obj.DeleteFront();
* param_4 := obj.DeleteLast();
* param_5 := obj.GetFront();
* param_6 := obj.GetRear();
* param_7 := obj.IsEmpty();
* param_8 := obj.IsFull();
*/
1206. 设计跳表
不使用任何库函数,设计一个 跳表 。
跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。
了解更多 : https://en.wikipedia.org/wiki/Skip_list
在本题中,你的设计应该要包含这些函数:
- bool search(int target) : 返回target是否存在于跳表中。
- void add(int num): 插入一个元素到跳表。
- bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回false. 如果存在多个 num ,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。
type Skiplist struct {
head *Node
prevNodes []*Node // 存放插入过程中需要用到的临时节点
maxLevel int
}
type Node struct {
val int
next, down *Node
}
func Constructor() Skiplist {
head := &Node{val: -1}
sl := Skiplist{
head: head,
prevNodes: make([]*Node, 64),
maxLevel: 16,
}
return sl
}
func (sl *Skiplist) Search(target int) bool {
curr := sl.head
for curr != nil {
// 在同一层上找
for curr.next != nil && curr.next.val < target {
curr = curr.next
}
if curr.next != nil && curr.next.val == target {
return true
}
curr = curr.down
}
return false
}
func (sl *Skiplist) Add(num int) {
level := -1
curr := sl.head
for curr != nil {
for curr.next != nil && curr.next.val < num {
curr = curr.next
}
level++
sl.prevNodes[level] = curr
curr = curr.down
}
// 从最底层level往上,对于待插入的节点,决定是否插入在某个level上
insertUp := true
var downNode *Node
for insertUp && level >= 0 {
prevNode := sl.prevNodes[level]
level--
prevNode.next = &Node{val: num, next: prevNode.next, down: downNode }
downNode = prevNode.next
// 随机决定是否在上一层插入node
insertUp = rand.Intn(2) == 0
}
if insertUp {
newhead := &Node{val: num, next: nil, down: downNode }
sl.head = &Node{val: -1, next: newhead, down: sl.head}
}
}
func (sl *Skiplist) Erase(num int) bool {
exist := false
curr := sl.head
for curr != nil {
for curr.next != nil && curr.next.val < num {
curr = curr.next
}
if curr.next != nil && curr.next.val == num {
exist = true
curr.next = curr.next.next
}
curr = curr.down
}
return exist
}
/**
* Your Skiplist object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Search(target);
* obj.Add(num);
* param_3 := obj.Erase(num);
*/