3.16

博弈,并查集,哈希表,双向链表

除数博弈

thinking

我们不妨选取几个例子来观察这个博弈的问题:(为方便,我们将站在Alice的角度进行选取数字)

  • n=1,false
  • n=2,选取1,true
  • n=3,选1,参考2,false
  • n=4,选1,2,参考2或者3的选择,我们选1,true
  • n=5,选取1,参考4的情况,false

我们不妨大胆地猜测,认为偶数必胜,奇数必输,证明如下:

我们知道对于任何的n,1均为可选,同时,我们不选择x为1的时候,这一次操作同样可以用k次减一来进行模拟。因此,我们可以直接认为每一次操作减一,易得a拿到2时,必胜。如果a碰到偶数,那么b一定是奇数,再一次后,a仍然是偶数,直到2为止a获胜。同理,a是奇数,则一定会失败。

solution

class Solution {
public:
    bool divisorGame(int n) {
        return (n&1)==0;
    }
};

获取生成数组中的最大值

thinking

简单模拟

solution

class Solution {
public:
    int a[102];
    int getMaximumGenerated(int n) {
        if(n==0) return 0;
        int ans=1;
        memset(a,0,sizeof(a));
        a[1]=1;
        for(int i=2;i<=n;++i) {
            if((i&1)==1) {
                a[i]=a[(i-1)/2]+a[(i-1)/2+1];
            }else {
                a[i]=a[i/2];
            }
            ans=max(ans,a[i]);
        }
        return ans;
    }
};

传递信息

thinking

看一眼数据量,直接爆搜有没有

solution

class Solution {
public:
    int ans=0;
    void dfs(int k,int pos,vector<vector<int>> &e,int n) {
        if(k==0) {
            if(pos==n-1) ++ans;
            return;
        }
        int len=e[pos].size();
        for(int i=0;i<len;++i) {
            --k;
            dfs(k,e[pos][i],e,n);
            ++k;
        }
    }
    int numWays(int n, vector<vector<int>>& relation, int k) {
        vector<vector<int>> e(n);
        for(auto &r:relation) e[r[0]].push_back(r[1]);
        dfs(k,0,e,n);
        return ans;
    }
};

冗余连接

thinking

并查集经典题,判断删除一个边后,整个图是否连通,并输出最后面的边

solution

class UnionFind {
    public:
        vector<int> parent;//父辈节点
        vector<int> size;//当前的层数
        int n;
        int setCount;//当前的连通分量
    public:
        UnionFind(int _n):n(_n),setCount(_n),parent(_n),size(_n,1) {
            iota(parent.begin(),parent.end(),0);
        }

        int findset(int x) {return parent[x]==x?x:parent[x]=findset(parent[x]);}

        bool unite(int x,int y) {
            x=findset(x);y=findset(y);
            if(x==y) return false;
            if(size[x]<size[y]) swap(x,y);
            parent[y]=x;
            size[x]+=size[y];
            --setCount;
            return true;
        }

        bool connected(int x,int y) {return findset(y)==findset(x);}
};
class Solution {
public:
    vector<int> findRedundantConnection(vector<vector<int>>& e) {
        int n=e.size();
        vector<int> ans(2);
        for(int i=0;i<n;++i) {
            UnionFind uf(n+1);
            for(int j=0;j<n;++j) {
                if(i==j) continue;
                uf.unite(e[j][0],e[j][1]);
            }
            if(uf.setCount==2) ans=e[i];
        }
        return ans;
    }
};

全 O(1) 的数据结构

thinking

考察数据结构的运用,可以先做下面的题对这一题进行铺垫,有一点点的小印象。

对于本题,我们采用双向链表+哈希表,维护双向链表正序,维护字符串的次数,使用哈希表存下节点的地址,方便查找。具体如下:

solution

struct Link {//双向链表
    string key;
    int cnt;
    Link* pre;
    Link* next;
    Link(string s,int c):key(s),cnt(c),pre(nullptr),next(nullptr) {}
};
const int inf=0x3f3f3f3f;
class AllOne {
public:
    //link 呈现单调递减
    unordered_map<string,Link*>hash;
    Link* head;
    Link* tail;
    AllOne() {
        //哑节点
        head=new Link("",inf);
        tail=new Link("",-1);
        head->next=tail;
        tail->pre=head;
    }
    
    void inc(string key) {
        if(!hash.count(key)) {
            //找不到这个节点
            Link* node=new Link(key,1);
            hash[key]=node;
            node->next=tail;
            node->pre=tail->pre;
            node->pre->next=node;
            node->next->pre=node;
        } else {
            //找到了这个节点
            Link* node=hash[key];
            node->cnt=(node->cnt)+1;
            while(node->cnt>node->pre->cnt) {
                swap(node->pre,node);
            }
        }
    }
    
    void dec(string key) {
        Link* node=hash[key];
        node->cnt=(node->cnt)-1;
        if(node->cnt==0) {
            //需要删除该节点
            node->next->pre=node->pre;
            node->pre->next=node->next;
            delete node;
            hash.erase(key);
        } else {
            //需要移动该节点
            while(node->cnt<node->next->cnt) {
                swap(node,node->next);
            }
        }
    }
    
    string getMaxKey() {
        if(head->next->cnt!=-1) return head->next->key; 
        return "";
    }
    
    string getMinKey() {
        if(tail->pre->cnt!=inf) return tail->pre->key;
        return "";
    }
    void swap(Link* left,Link* right) {//交换两个节点
        left->next=right->next;
        right->pre=left->pre;
        left->pre=right;
        right->pre->next=right;
        right->next=left;
        left->next->pre=left;
    }
};

LRU 缓存(medium)

thinking

我们采用哈希表,与双向链表进行操作。详细原理如下:

我们储存每一对键值对\((key,value)\),用双端链表进行维护查询数据的新旧,即是,我们将每一次刚访问过或者是刚插入的数据移动到链表的头部,当数据量超出容量的时候,我们只需要删除来年表最后一个节点即可。具体代码如下:

solution

struct DlinkedNode {
    int key,value;
    DlinkedNode* prev;
    DlinkedNode* next;
    DlinkedNode():key(0),value(0),prev(nullptr),next(nullptr) {}
    DlinkedNode(int _key,int _value):key(_key),value(_value),prev(nullptr),next(nullptr) {}
};//双向链表的结构体
//不过结构体里面也能定义函数???
class LRUCache {
private:
    unordered_map<int,DlinkedNode*>cache;
    DlinkedNode* head;
    DlinkedNode* tail;
    int size;
    int capacity;
public:
    LRUCache(int _capacity) {//初始化
        capacity=_capacity;
        size=0;
        head=new DlinkedNode();
        tail=new DlinkedNode();
        head->next=tail;
        tail->prev=head;//设置前后哑节点,方便后续操作
    }
    
    int get(int key) {
        if(!cache.count(key)) return -1;//查找key值
        DlinkedNode* node=cache[key];;
        moveToHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if(!cache.count(key)) {//查不到key值,则手工添加key值
            DlinkedNode* node=new DlinkedNode(key,value);
            cache[key]=node;
            addToHead(node);
            ++size;
            if(size>capacity) {//size++,判断一下,是否需要移除
                DlinkedNode *removed=removeTail();
                cache.erase(removed->key);
                delete removed;
                --size;
                }
            } else {//查到key值,更新value即可
                DlinkedNode* node=cache[key];
                node->value=value;
                moveToHead(node);
        }
    }
    void addToHead(DlinkedNode* node) {//将独立的node移动到头部
        node->prev=head;
        node->next=head->next;
        head->next->prev=node;
        head->next=node;
    }
    void removeNode(DlinkedNode* node) {//移除node节点
        node->prev->next=node->next;
        node->next->prev=node->prev;
    }
    void moveToHead(DlinkedNode* node) {//将节点从链表中移动到头部
        removeNode(node);
        addToHead(node);
    }
    DlinkedNode* removeTail() {//删除并返回节点
        DlinkedNode* node=tail->prev;
        removeNode(node);
        return node;
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
posted @ 2022-03-17 00:11  圣道  阅读(95)  评论(0)    收藏  举报