【C++】list splice、LRU cache

31 篇文章 4 订阅
9 篇文章 1 订阅

  在做 LeetCode - 146. LRU Cache 的时候用到了 list 的 splice 功能,做一个记录。

一、list::splice

  先讲 splice 功能。splice 是剪接、胶接的意思,比如剪接电影、胶接磁带这样的,用在 list 上就是在一个 list 中剪接上另一个 list 的部分或全部内容。
英文介绍如下:

Transfer elements from list to list

Transfers elements from x into the container, inserting them at position.

This effectively inserts those elements into the container and removes them from x, altering the sizes of both containers.
The operation does not involve the construction or destruction of any element.
They are transferred, no matter whether x is an lvalue or an rvalue, or whether the value_type supports move-construction or not.

  就是说,这个功能是将一个 list Transfer 到另一个 list 中。这个功能在 C++98 和 C++11 中都是三种形式调用,不过 C++11 每一种都多了一个 右值引用(&&) 的参数传递方式,所以 C++11 中其实共有 6 种,不过还是分为 3 类:
  用两个 list 举例:list la{ 1,2,3 }, lb{ 4,5,6 };

1、entire list(插入一整个 list)
void splice (const_iterator _Where, list& _Right)
void splice (const_iterator _Where, list&& _Right)
2、single element(插入 list 中的一个元素)
void splice(const_iterator _Where, list& _Right, const_iterator _First)
void splice(const_iterator _Where, list&& _Right, const_iterator _First)
3、element range(插入 list 中的部分元素)
// void splice(const_iterator _Where, list& _Right, const_iterator _First, const_iterator _Last)
// void splice(const_iterator _Where, list&& _Right, const_iterator _First, const_iterator _Last)

  要注意两点:
1、三种 splice 方式都是在 position 位置插入,原 position 位置上的元素,在被插入元素的后边
2、三种方式的 splice 后,被插入的元素会从原 list 中被 remove 掉
3、三种 splice 方式都不会有任何元素的构造和析构
4、splice 会影响两个 list 的大小和内容
5、右值引用的参数传递和非右值引用的参数传递结果是一样的
6、C++ STL list 的 splice 时间复杂度为 O ( 1 ) O(1) O(1),因为这是特殊设计的,而 list 的 .size() 方法时间复杂度是 O ( N ) O(N) O(N),因为要遍历整个链表(这个问题在另一篇博客中记录,待填入网址)。

  还是用 list la{ 1,2,3 }, lb{ 4,5,6 }; 举例,三种方式如下。

1、entire list

  在一个 list 的 _Where 位置,插入另一个完整 list _Right

// void splice(const_iterator _Where, list& _Right)
	la.splice(la.begin(), lb);
// void splice(const_iterator _Where, list&& _Right)
	la.splice(la.begin(), std::move(lb));  // or la.splice(la.begin(), {4,5,6});

  两种操作结果相同:la.size() = 6, lb.size() = 0,la 内容为 4 5 6 1 2 3,lb 为空。
  所有右值引用传递参数的形式和上边的两种一样,后边就不写了。

2、single element

  在一个 list 的 _Where 位置,插入另一个 list _Right_First 元素(不一定是第一个,只是形参叫这个名字)。

// void splice(const_iterator _Where, list& _Right,	const_iterator _First)
	la.splice(la.begin(), lb, lb.begin());

  splice 结果:la.size() = 4, lb.size() = 2,la 内容为 4 1 2 3,lb 为 5 6

3、element range

  在一个 list 的 _Where 位置,插入另一个 list _Right[_First, _Last) 元素(和 C++ 规定一样,左闭右开区间)。

// void splice(const_iterator _Where, list& _Right, const_iterator _First, const_iterator _Last)
	auto ite = lb.begin();
	++ite;
	++ite;
	la.splice(la.begin(), lb, lb.begin(), ite);

  splice 结果:la.size() = 5, lb.size() = 1,la 内容为 4 5 1 2 3,lb 为 6

二、LRU cache - LeetCode - 146. LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache.
It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present.
When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.
Follow up: Could you do both operations in O(1) time complexity?

// Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1);		// returns 1
cache.put(3, 3);	// evicts key 2
cache.get(2);		// returns -1 (not found)
cache.put(4, 4);	// evicts key 1
cache.get(1);		// returns -1 (not found)
cache.get(3);		// returns 3
cache.get(4);		// returns 4

  代码使用 unordered_map<int, list<pair<int, int>>::iterator> mp; 来存储和查询 key,达到 O ( 1 ) O(1) O(1) 时间复杂度,然后用 list<pair<int, int>> cacheList; 来存储 key 和对应的 value,因为链表的调整是 O ( 1 ) O(1) O(1),所以 get 和 put 是 O ( 1 ) O(1) O(1) 时间,代码如下:

LRUCache(int capacity) :_cap(capacity) {}

int get(int key) {
    if(mp.find(key) == mp.end()) return -1;
    const int res = mp[key]->second;
	cacheList.splice(cacheList.begin(), cacheList, mp[key]);
	mp[key] = cacheList.begin();
    return res;
}

void put(int key, int value) {
    if(mp.find(key) != mp.end()) {
        mp[key]->second = value;
        cacheList.splice(cacheList.begin(), cacheList, mp[key]);
		mp[key] = cacheList.begin();
    }
    else {
        if (cacheList.size() >= _cap) {
			mp.erase(cacheList.back().first);
			cacheList.pop_back();
		}
		cacheList.emplace_front(key, value);
		mp[key] = cacheList.begin();
    }
}

private:
    int _cap;
    list<pair<int, int>> cacheList;
    unordered_map<int, list<pair<int, int>>::iterator> mp;
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值