算法之hashMap、LRU、bitMap、生产者消费者

hashMap

//KVMap,首先,HashMap存储的是键值对,所以需要一个键值对类型。
//链表结构里数据的数据类型 键值对
type KV struct {
    Key string
    Value string
}

//LinkNode,键值对又是主要存储在链表里的,所以需要一个链表类。
//链表结构
type LinkNode struct {
    //节点数据
    Data KV
    //下一个节点
    NextNode *LinkNode
}

//创建只有头结点的链表
func CreateLink() *LinkNode {
   //头结点数据为空 是为了标识这个链表还没有存储键值对
    var linkNode = &LinkNode{KV{"",""}, nil}
    return linkNode
}

//当发生哈希碰撞时,键值对会存储在新建的链表节点上。这里需要一个添加节点的功能,我们这里采用尾插法添加节点。
//尾插法添加节点,返回链表总长度
func (link *LinkNode) AddNode(data KV) int {
    var count = 0
    //找到当前链表尾节点
    tail := link
    for {
        count += 1
        if tail.NextNode == nil {
            break
        }else {
            tail = tail.NextNode
        }
    }
    var newNode = &LinkNode{data, nil}
    tail.NextNode = newNode
    return count+1
}

//HashMap 接下来,就是猪脚HashMap登场了。
//HashMap木桶(数组)的个数
const BucketCount  = 16
type HashMap struct {
    //HashMap木桶
    Buckets [BucketCount]*LinkNode
}

//创建HashMap
func CreateHashMap() *HashMap {
    myMap := &HashMap{}
    //为每个元素添加一个链表对象
    for i := 0; i < BucketCount ; i++  {
        myMap.Buckets[i] = CreateLink()
    }

    return myMap
}

//我们需要一个哈希散列算法,将key转化为一个0-BucketCount的整数,作为存放它的数组的下标。这里这个散列算法,应尽可能随机地使新增的键值对均匀地分布在每个数组下。
//一般像go的map和Java的HashMap都会有一个复杂的散列算法来达到这个目的,我们这里只是为了讲HashMap原理,暂且就用一个简单的方法来求出下标。
//自定义一个简单的散列算法,它可以将不同长度的key散列成0-BucketCount的整数
func HashCode(key string) int {
    var sum = 0
    for i := 0; i < len(key); i++ {
        sum += int(key[i])
    }
    return (sum % BucketCount)
}

//往HashMap里添加键值对
//添加键值对
func (myMap *HashMap)AddKeyValue(key string, value string)  {

    //1.将key散列成0-BucketCount的整数作为Map的数组下标
    var mapIndex = HashCode(key)

    //2.获取对应数组头结点
    var link = myMap.Buckets[mapIndex]

    //3.在此链表添加结点
    if link.Data.Key == "" && link.NextNode == nil {
        //如果当前链表只有一个节点,说明之前未有值插入  修改第一个节点的值 即未发生哈希碰撞
        link.Data.Key = key
        link.Data.Value = value

        fmt.Printf("node key:%v add to buckets %d first node\n", key, mapIndex)
    }else {

        //发生哈希碰撞
        index := link.AddNode(KV{key, value})
        fmt.Printf("node key:%v add to buckets %d %dth node\n", key, mapIndex, index)
    }
}

//根据键从HashMap里取出对应的值,按键取值
func (myMap *HashMap)GetValueForKey(key string) string {
    //1.将key散列成0-BucketCount的整数作为Map的数组下标
    var mapIndex = HashCode(key)
    //2.获取对应数组头结点
    var link = myMap.Buckets[mapIndex]
    var value string
    //遍历找到key对应的节点
    head := link
    for {
        if head.Data.Key == key {
            value = head.Data.Value
            break
        }else  {
            head = head.NextNode
        }
    }
    return value
}

LRU算法

LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的。如果要自己实现一个LRU算法,可以用哈希表加双向链表实现:
在这里插入图片描述
设计思路是:使用哈希表存储 key,值为链表中的节点,节点中存储值,双向链表来记录节点的顺序,头部为最近访问节点。

LRU算法中有两种基本操作:

  1. get(key):查询key对应的节点,如果key存在,将节点移动至链表头部。
  2. set(key, value): 设置key对应的节点的值。如果key不存在,则新建节点,置于链表开头。如果链表长度超标,则将处于尾部的最后一个节点去掉。如果节点存在,更新节点的值,同时将节点置于链表头部。
    lru缓存**

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]

  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

示例:输入:

[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3

返回值:

[1,-1]

说明:

第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录
#include <unordered_map>
 
struct DListNode{
    int key, val;
    DListNode* pre;
    DListNode* next;
    DListNode(int k, int v): key(k), val(v), pre(nullptr), next(nullptr){};
};
 
 
class Solution {
private:
    int size = 0;
    DListNode* head;
    DListNode* tail;
    unordered_map<int, DListNode*> mp;
 
public:
    /**
     * lru design
     * @param operators int整型vector<vector<>> the ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LRU(vector<vector<int> >& operators, int k) {
        // write code here
        if(k < 1) return {};
        this->size = k;
        this->head = new DListNode(0,0);
        this->tail = new DListNode(0,0);
        this->head->next = this->tail;
        this->tail->pre = this->head;
 
        if(operators.size() == 0) return {};
 
        vector<int> res;
 
        for(vector<int> op : operators){
            if(op[0] == 1) {
                set(op[1], op[2]);
            }else if(op[0] == 2){
                int value = get(op[1]);
                res.push_back(value);
            }
        }
        return res;
    }
 
    void set(int key, int val){
        if(mp.find(key) == mp.end()){ // hashmap 中没找到
            DListNode* node = new DListNode(key, val);
            mp[key] = node;
            if(this->size <= 0){
                removeLast();
            }
            else{
                this->size--;
            }
            insertFirst(node);
        }
        else{  // hashmap 中已经有了,也就是链表里也已经有了
            mp[key]->val = val;
            moveToHead(mp[key]);
        }
    }
 
 
    int get(int key){
        int ret = -1;
        if(mp.find(key) != mp.end()){
            ret = mp[key]->val;
            moveToHead(mp[key]);
        }
        return ret;
    }
 
    void moveToHead(DListNode* node){
        if(node->pre == this->head) return;
        node->pre->next = node->next;
        node->next->pre = node->pre;
        insertFirst(node);
    }
 
 
    void removeLast(){
        mp.erase(this->tail->pre->key);
        this->tail->pre->pre->next = this->tail; // remove the last node in dlist
        this->tail->pre = this->tail->pre->pre;
    }
 
    void insertFirst(DListNode* node){
        node->pre = this->head;
        node->next = this->head->next;
        this->head->next->pre = node;
        this->head->next = node;
    }
 
};

生产者消费者

type Product struct {
    name  int
    value int
}

func producer(wg *sync.WaitGroup, products chan<- Product, name int, stop *bool) {
    for !*stop {
        product := Product{name: name, value: rand.Int()}
        products <- product
        fmt.Printf("producer %v produce a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}

func consumer(wg *sync.WaitGroup, products <-chan Product, name int) {
    for product := range products {
        fmt.Printf("consumer %v consume a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}
//主线程
var wgp sync.WaitGroup
var wgc sync.WaitGroup
stop := false
products := make(chan Product, 10)

// 创建 5 个生产者和 5 个消费者
for i := 0; i < 5; i++ {
    go producer(&wgp, products, i, &stop)
    go consumer(&wgc, products, i)
    wgp.Add(1)
    wgc.Add(1)
}

time.Sleep(time.Duration(1) * time.Second)
stop = true     // 设置生产者终止信号
wgp.Wait()      // 等待生产者退出
close(products) // 关闭通道
wgc.Wait()      // 等待消费者退出

bitMap的简单实现

对于任意一个数x,x / 32对应着它在vector的第几个位置,x % 32对应它的比特位。
下面给出C++代码的实现。

class BitMap
{
public:
    BitMap(size_t num)
    {
        _v.resize((num >> 5) + 1); // 相当于num/32 + 1
    }

    void Set(size_t num) //set 1
    {
        size_t index = num >> 5; // 相当于num/32
        size_t pos = num % 32;
        _v[index] |= (1 << pos);
    }

    void ReSet(size_t num) //set 0
    {
        size_t index = num >> 5; // 相当于num/32
        size_t pos = num % 32;
        _v[index] &= ~(1 << pos);
    }

    bool get(size_t num)//check whether it exists
    {
        size_t index = num >> 5;
        size_t pos = num % 32;
        bool flag = false;
        if (_v[index] & (1 << pos))
            flag = true;
        return flag;

    }

private:
    vector<size_t> _v;
}; 

进程交替打印

让两个线程互相唤醒对方来交替打印数字

#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>

int g_num = 1;
pthread_mutex_t mutex;
pthread_cond_t cond1,cond2;

void* thread1(void* arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
        //如果需要交替打印一定范围(例如1-10)内的数字,那么可以加上下面两行代码
        //if(g_num > 10)
           //exit(1);
        printf("Thread1: %d \n",g_num);
        g_num ++;
        pthread_cond_signal(&cond2);
        pthread_cond_wait(&cond1,&mutex);		
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
	return NULL;
}

void* thread2(void* arg)
{
	while(1)
	{
        //这个sleep(1)加在前面是因为开启线程时有可能是线程2先打印,
        //导致变成thread2输出奇数,threa1输出偶数。为了避免这种情况,可以在延迟下线程2的打印。
        sleep(1);
		pthread_mutex_lock(&mutex);
        printf("Thread2: %d \n",g_num);
		g_num++;
        pthread_cond_signal(&cond1);
        pthread_cond_wait(&cond2,&mutex);
		pthread_mutex_unlock(&mutex);
		
	}
	return NULL;
}
int main()
{
	pthread_t p1,p2;
	
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond1,NULL);
	pthread_cond_init(&cond2,NULL);	
	
	pthread_create(&p1,NULL,thread1,NULL);
	pthread_create(&p2,NULL,thread2,NULL);
    pthread_join(p1,NULL);
	pthread_join(p2,NULL);
	
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond1);
	pthread_cond_destroy(&cond2);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值