map和set题目练习

一、习题一:随机链表的复制

1.1题目详情

1.2思路

在没有学习map和set之前,解决这道题最大的问题就在于无法建立原链表与拷贝链表的映射关系,只能通过在原链表每个节点后面新建一个新的链表来进行节点间的对应,而学习了map之后,就可以选用map<Node*,Node*>来建立两个链表之间的映射关系了

在map中,我们对[]进行了重载,可以实现没有对应key的节点时插入节点并对该处节点的value进行操作,有key节点的时候直接对key对应的value进行操作

综上,我们可以选择如下实现思路:

①复制一个新的链表

②创建一个map对象,利用它来创建原链表与新链表之间的映射关系

③更新随机因子random

1.3代码实现

①基本版

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*,Node*> m1;
        
        //复制链表
        Node* cur=head;
        Node* copy_n=nullptr;
        Node* c_cur=copy_n;
        while(cur)
        {
            if(copy_n==nullptr)
            {
                c_cur=new Node(cur->val);
                copy_n=c_cur;
                
                cur=cur->next;
            }
            else
            {
                c_cur->next=new Node(cur->val);
                c_cur=c_cur->next;
                 cur=cur->next;
            }
        }
        
        //遍历,在map中建立一一映射关系
        cur=head;
        c_cur=copy_n;

        while(cur)
        {
            m1[cur]=c_cur;//确定不会在map里有
            c_cur=c_cur->next;
            cur=cur->next;
        }
        //再次遍历,对随机值random进行更新
        cur=head;
        c_cur=copy_n;

        while(cur)
        {
            if(cur->random==nullptr)
            c_cur->random=nullptr;
            else
            {
                c_cur->random=(m1.find(cur->random))->second;
            }
            c_cur=c_cur->next;
            cur=cur->next;
        }

        return copy_n;



    }
};

②改善版:映射关系的建立可以和链表的复制一同进行

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*,Node*> m1;
        
        //复制链表,其中copy_n起头节点作用,c_cur起尾节点作用
        Node* cur=head;
        Node* copy_n=nullptr;
        Node* c_cur=nullptr;
        while(cur)
        {
            //头尾节点指向一起
            if(copy_n==nullptr)
            {
                copy_n=new Node(cur->val);
                c_cur=copy_n;
            }
            //尾节点往后走
            else
            {
                c_cur->next=new Node(cur->val);
                c_cur=c_cur->next;
            }
            //为原链表与循环链表建立联系(嵌套在循环里)
            m1[cur]=c_cur;

             cur=cur->next;
        }
        

        //循环设置复制链表random
        cur=head;
        c_cur=copy_n;

        while(cur)
        {
            if(cur->random==nullptr)
                c_cur->random=nullptr;
            else
                //c_cur->random=(m1.find(cur->random))->second;
                //m1[cur->random]本身就返回second的值,且一定存在cur->random
                c_cur->random=m1[cur->random];

            c_cur=c_cur->next;
            cur=cur->next;
        }
        return copy_n;
    }
};

二、习题二:判断是否循环链表

2.1题目详情

2.2思路

未学习map和set之前,我们使用了快慢指针的方式来解决这道题,但快慢指针的应用意味着我们需要进行循环必定相遇的求证,证明过程并不简单;但现在有了map和set,我们就可以选择直接插入节点到set中,因为set的insert会返回一个可以证明插入是否有效的

pair<iterator,bool>

利用这个bool我们可以轻易得出答案

2.3代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *tmp=head;
        set<ListNode*> sl;
        while(tmp)
        {
            if(!(sl.insert(tmp).second))
            return true;
            tmp=tmp->next;
        }
        return false;
    }
};

2.补:关于异地容灾备份

我们手机上的云存储其实对应的是一个数据同时存储在手机和服务器上,当在手机上删除了备份过的内容,服务器与手机进行同步服务的时候会自动把删除了的内容复原回去;

但是数据实在服务器上存储,服务器也是电脑,也有可能损坏,所以为了保证数据的安全性,有了数据容灾备份,采用一原本,多副本的方式彼此间进行同步

三、前k个高频单词

3.1题目详情

3.2思路

之前题目一的练习我们已经知道了map中重载[]的用法,在这里我们可以利用重载后的[]快速得到单词与单词出现次数的映射关系

有了映射关系以后,我们只需要设法通过次数对pair进行排序即可得到前k个需要的单词,可以在仿函数中书写逻辑,再调用系统函数sort

因为返回值是一个vector<string>类型的值,所以我们需要把map中的单词拷贝到vector中,此时我们距离成功只差一步,那就是按照字典序排序

那么我们就这么思考怎么排吗?要知道map中我们其实已经是按照key的字典序排列的,可不可以不消耗更多时间呢?

问题可以从sort上解决,因为sort的底层是快排与堆排结合,既然有快排,那么排序完以后就会变得不稳定,所以

①可以采用稳定排序stable_sort

②可以在仿函数中进行比较逻辑的特殊设定,让出现次数相同时按照字典序排序,以此来保证稳定

综上,我们可以得出思路:

①创建map对象,for循环建立映射关系

②补充仿函数逻辑,将其传入sort,并用sort进行次数的比较的出前k个单词

③把前k个单词放到一个新的vector中

3.2补:pair的比较大小

pair之间其实也支持比较大小,比如“<”符号,会先看first小不小,如果first小的话那这个pair就小;如果first大于等于的话会看second小不小,seconde小的话也会小

例如:

pair<int,int> p1=make_pair(5,7);

pair<int,int> p2=make_pair(8,6);

p1<p2;//p1的first比p2小,所以应该返回true

p2<p1;//p2的first比p1大,所以要看p2的second,而p2的second比p1小,所以也返回true

通过这个例子不难看出,除非在特殊情况,否则最好不要去用pair本身自带的比较逻辑,因为与我们日常思维方式不符合

3.3代码实现

①使用stable_sort稳定排序

struct Compare
{
    bool operator()(const pair<string,int>& p1,const pair<string,int>& p2)
    {
        //排降序
        return p1.second>p2.second;
    }
};

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        //创建map,建立单词出现顺序和次数的联系
        map<string,int> m;
        for(const auto& e:words)
        {
            m[e]++;
        }

        //排序,提取出排名前几的单词
        vector<pair<string,int>> v_p;
        for(const auto& e:m)
        {
            v_p.push_back(e);
        }
        //使用相对位置不改变的sort来确保字典序排序
        stable_sort(v_p.begin(),v_p.end(),Compare());

        vector<string> s;
        for(size_t i=0;i<k;i++)
        {
            s.push_back(v_p[i].first);
        }
        return s;


    }
};

②修改仿函数逻辑保持稳定

struct Compare
{
    bool operator()(const pair<string,int>& p1,const pair<string,int>& p2)
    {
        //排降序
        return p1.second>p2.second||(p1.second==p2.second&&p1.first<p2.first);
    }
};

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        //创建map,建立单词出现顺序和次数的联系
        map<string,int> m;
        for(const auto& e:words)
        {
            m[e]++;
        }

        //排序,提取出排名前几的单词
        vector<pair<string,int>> v_p(m.begin(),m.end());
        
        //使用相对位置不改变的sort来确保字典序排序
        sort(v_p.begin(),v_p.end(),Compare());

        vector<string> s;
        for(size_t i=0;i<k;i++)
        {
            s.push_back(v_p[i].first);
        }
        return s;


    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值