Arrays 通过内存优化搜索

Arrays 通过内存优化搜索,arrays,performance,loops,optimization,linked-list,Arrays,Performance,Loops,Optimization,Linked List,我有4096个项目的多个实例。我需要搜索和找到一个项目的基础上再发生,我想优化它。由于并非所有4096项都可以使用,我认为,加快速度的一种方法是使用链表而不是数组。每当我必须搜索一个项目时,一旦我找到它,我就会把它放在列表的最前面,这样下次它出现时,我就只需要做最小的搜索(循环)工作。这听起来对吗 EDIT1 我不认为二叉搜索树的想法是我真正可以使用的,因为我已经对数据进行了排序,比如数组,即前一个节点后面的每个节点都较大,这不符合目的,不是吗 我试图解决缓存的问题,并提出了如下建议: pend

我有4096个项目的多个实例。我需要搜索和找到一个项目的基础上再发生,我想优化它。由于并非所有4096项都可以使用,我认为,加快速度的一种方法是使用链表而不是数组。每当我必须搜索一个项目时,一旦我找到它,我就会把它放在列表的最前面,这样下次它出现时,我就只需要做最小的搜索(循环)工作。这听起来对吗

EDIT1 我不认为二叉搜索树的想法是我真正可以使用的,因为我已经对数据进行了排序,比如数组,即前一个节点后面的每个节点都较大,这不符合目的,不是吗

我试图解决缓存的问题,并提出了如下建议:

pending edit
但我得到的结果表明,它不像我希望的那样工作:


有什么建议可以改进吗?

最坏的情况是,除非像Brett建议的那样对数组或列表进行排序,否则您的搜索仍然是O(N)。因此,使用排序列表会增加插入的复杂性(按顺序插入),但搜索速度会快得多。你的建议几乎就像一个“缓存”。如果我们不知道在短期内搜索一个找到的项目的频率,我们很难说它有多有用。显然,缓存有好处,这就是为什么我们在内存中有整个L1、L2、L3体系结构。但是它是否会对你有用还不确定。

最坏的情况是,除非你像Brett建议的那样对数组或列表进行排序,否则你的搜索仍然是O(N)。因此,使用排序列表会增加插入的复杂性(按顺序插入),但搜索速度会快得多。你的建议几乎就像一个“缓存”。如果我们不知道在短期内搜索一个找到的项目的频率,我们很难说它有多有用。显然,缓存有好处,这就是为什么我们在内存中有整个L1、L2、L3体系结构。但是它是否对你有效还不确定。

回复Edit1: 我认为,如果您的数据元素不大,比如说,只有几个字节,甚至几十个字节,那么4096个字节可以装入内存。在这种情况下,您需要的是一个哈希表。在C++中,你使用<代码> unordeDeMAP < /C>。例如,如果键类型为
int
,则可以定义
unorderedmap
,并在
O(1)
中获取元素

如果你能很好地设计你的散列,最快的搜索可能是
O(1)
,最坏的情况可能是
O(n)
。如果这些项目较大且无法装入内存,则可以使用所谓的最近使用最少的缓存
算法
来节省内存

LRU缓存的示例代码

template <typename K>
class Key_Age{
list<K> key_list;
unordered_map<K, typename list<K> :: iterator> key_pos;
public:
void access(K key){
    key_list.erase(key_pos[key]);
    insert_new(key);
}

void insert_new(K key){
    key_list.push_back(key);
    key_pos[key] = --key_list.end();
}

K pop_oldest(){
    K t = key_list.front();
    key_list.pop_front();
    return t;
}
};

class LRU_Cache{
int capacity;
Key_Age<int> key_age;
unordered_map<int, int> lru_cache;

public:
LRU_Cache(int capacity): capacity(capacity) {
}

int get(int key) {
    if (lru_cache.find(key) != lru_cache.end()) {
        key_age.access(key);
        return lru_cache[key];
    }
    return -1;
}

void set(int key, int value) {
    if (lru_cache.count(key) < 1) {
        if (lru_cache.size() == capacity) {
            int oldest_key = key_age.pop_oldest();
            lru_cache.erase(oldest_key);
        }
        key_age.insert_new(key);
        lru_cache[key] = value;
        return;
    }

    key_age.access(key);
    lru_cache[key] = value;
}
模板
班级钥匙年龄{
列表键列表;
无序的地图键位置;
公众:
无效访问(K键){
键列表。擦除(键位置[键]);
插入新的(键);
}
无效插入新(K键){
按键列表。向后推(按键);
key_pos[key]=--key_list.end();
}
K pop_最古老的(){
K t=键列表。前();
key_list.pop_front();
返回t;
}
};
类LRU_缓存{
国际能力;
关键年龄关键年龄;
无序地图lru缓存;
公众:
LRU_缓存(内部容量):容量(容量){
}
int get(int键){
if(lru\u cache.find(key)!=lru\u cache.end()){
钥匙的使用年龄(钥匙);
返回lru_缓存[键];
}
返回-1;
}
无效集(int键,int值){
如果(lru_缓存计数(键)<1){
if(lru_cache.size()=容量){
int oldest_key=key_age.pop_oldest();
lru_缓存。擦除(最旧的_键);
}
钥匙年龄。插入新的(钥匙);
lru_缓存[键]=值;
返回;
}
钥匙的使用年龄(钥匙);
lru_缓存[键]=值;
}
})

回复Edit1: 我认为,如果您的数据元素不大,比如说,只有几个字节,甚至几十个字节,那么4096个字节可以装入内存。在这种情况下,您需要的是一个哈希表。在C++中,你使用<代码> unordeDeMAP < /C>。例如,如果键类型为
int
,则可以定义
unorderedmap
,并在
O(1)
中获取元素

如果你能很好地设计你的散列,最快的搜索可能是
O(1)
,最坏的情况可能是
O(n)
。如果这些项目较大且无法装入内存,则可以使用所谓的最近使用最少的缓存
算法
来节省内存

LRU缓存的示例代码

template <typename K>
class Key_Age{
list<K> key_list;
unordered_map<K, typename list<K> :: iterator> key_pos;
public:
void access(K key){
    key_list.erase(key_pos[key]);
    insert_new(key);
}

void insert_new(K key){
    key_list.push_back(key);
    key_pos[key] = --key_list.end();
}

K pop_oldest(){
    K t = key_list.front();
    key_list.pop_front();
    return t;
}
};

class LRU_Cache{
int capacity;
Key_Age<int> key_age;
unordered_map<int, int> lru_cache;

public:
LRU_Cache(int capacity): capacity(capacity) {
}

int get(int key) {
    if (lru_cache.find(key) != lru_cache.end()) {
        key_age.access(key);
        return lru_cache[key];
    }
    return -1;
}

void set(int key, int value) {
    if (lru_cache.count(key) < 1) {
        if (lru_cache.size() == capacity) {
            int oldest_key = key_age.pop_oldest();
            lru_cache.erase(oldest_key);
        }
        key_age.insert_new(key);
        lru_cache[key] = value;
        return;
    }

    key_age.access(key);
    lru_cache[key] = value;
}
模板
班级钥匙年龄{
列表键列表;
无序的地图键位置;
公众:
无效访问(K键){
键列表。擦除(键位置[键]);
插入新的(键);
}
无效插入新(K键){
按键列表。向后推(按键);
key_pos[key]=--key_list.end();
}
K pop_最古老的(){
K t=键列表。前();
key_list.pop_front();
返回t;
}
};
类LRU_缓存{
国际能力;
关键年龄关键年龄;
无序地图lru缓存;
公众:
LRU_缓存(内部容量):容量(容量){
}
int get(int键){
if(lru\u cache.find(key)!=lru\u cache.end()){
钥匙的使用年龄(钥匙);
返回lru_缓存[键];
}
返回-1;
}
无效集(int键,int值){
如果(lru_缓存计数(键)<1){
if(lru_cache.size()=容量){
int oldest_key=key_age.pop_oldest();
lru_缓存。擦除(最旧的_键);
}
钥匙年龄。插入新的(钥匙);
lru_缓存[键]=值;
返回;
}
钥匙的使用年龄(钥匙);
lru_缓存[键]=值;
}

})

说到绩效,只有一条重要规则:衡量它

例如,在您的例子中,您可能有两个不同的考虑因素,一个是理论运行时分析,另一个是机器的实际运行情况。这两项都严重依赖于4096件物品的特性。如果您的数据已排序,您可以进行O(logn)搜索,如果未排序,则为最坏情况O(n)等

关于链表的概念,由于数据不再存储在一起(空间loca),您可能会有更多的硬件缓存未命中