优秀算法设计与例题(第二部分)

一.链表

1.两数相加

2. 两数相加 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        ListNode *cur1=l1,*cur2=l2;
        ListNode* phead = new ListNode(0);   //头结点
        ListNode* tail=phead;   //尾指针
        int sum=0,data=0;   //记录进位和个位
        while(cur1 || cur2 || sum)
        {
            data=0;
            if(cur1)
            {
                data += cur1->val;
                cur1=cur1->next;
            }
            if(cur2)
            {
                data += cur2->val;
                cur2=cur2->next;
            }
            data += sum;
            sum=0;
            if(data>=10)
            {
                sum++;
                data -= 10;
            }
            tail->next = new ListNode(data);
            tail=tail->next;
        }
        tail=phead->next;
        delete phead;
        return tail;
    }
};

2.两两交换链表的结点

24. 两两交换链表中的节点 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* swapPairs(ListNode* head) 
    {
        if(head==nullptr || head->next==nullptr) return head;
 
        ListNode *phead = new ListNode(0);  //创建一个头结点
        phead->next = head;  //再把之前的头结点串起来

        ListNode *prev=phead,*cur=head,*next=head->next,*nnext=next->next;
        while(cur && next)
        {
            //1.交换结点
            prev->next=next;
            cur->next=nnext;
            next->next=cur;

            //2.修改指针 注意不能用“cur=cur->next”这样的修改了,因为next指向已经改过了
            prev=cur;   //注意顺序变了,不是p,c,n,nn了,而是p,n,c,nn了
            cur=nnext;
            if(cur) next=cur->next;
            if(next) nnext=next->next;
        }
        head=phead->next;
        delete phead;
        return head;
    }
};

3.重排链表

143. 重排链表 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    void head_insert(ListNode *&phead,int x)
    {
        ListNode *newnode=new ListNode(x);
        newnode->next=phead->next;
        phead->next=newnode;
    }
    void reverse(ListNode *&phead)
    {
        ListNode *p=phead;
        ListNode *newhead=new ListNode(0);
        while(p)
        {
            head_insert(newhead,p->val);
            p=p->next;
        }
        phead=newhead->next;
    }

    void reorderList(ListNode* head) 
    {
        if(head==nullptr) return;
        if(head->next==nullptr) return;
        //1.找到中点
        ListNode *slow=head,*fast=head;
        while(fast && fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }

        //2.翻转后半部分,并且让指向slow的前一个指针指向空(断开)
        ListNode *p0=head;
        while(p0->next != slow && p0->next) p0=p0->next;
        p0->next=nullptr;
        reverse(slow);

        //3.拼接
        ListNode *p1=head,*p2=slow;
        while(p1)
        {
            ListNode *p3=p1->next;
            ListNode *p4=p2->next;
            p1->next=p2;
            if(p3) p2->next=p3;

            p1=p3;
            p2=p4;
        }
    }
};

 4.合并K个升序链表

23. 合并 K 个升序链表 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    struct cmp
    {
        bool operator()(const ListNode* l1,const ListNode* l2)
        {
            return l1->val > l2->val;
        }
    };
    
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        //1.先创建一个小根堆
        priority_queue<ListNode*,vector<ListNode*>,cmp> heap;

        //2.让所有的头结点进入小根堆
        for(auto l:lists)
            if(l!=nullptr) heap.push(l);

        //3.合并k个有序链表
        ListNode* phead = new ListNode(0);
        ListNode* p0=phead;
        while(!heap.empty())
        {
            ListNode* t=heap.top();
            heap.pop();
            p0->next=t;
            p0=t;
            if(t->next != nullptr) heap.push(t->next);
        }
        p0=phead->next;
        delete phead;
        return p0;
    }
};

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* mergeTowList(ListNode* l1,ListNode* l2)
    {
        if(l1==nullptr) return l2;
        if(l2==nullptr) return l1;

        //合并链表
        ListNode head;   //这样直接在栈上开空间,调用完之后可以不用delete了
        ListNode *cur1=l1,*cur2=l2,*prev=&head;
        head.next=nullptr;

        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                prev->next=cur1;
                prev=prev->next;
                cur1=cur1->next;
            }
            else
            {
                prev->next=cur2;
                prev=prev->next;
                cur2=cur2->next;
            }
        }

        if(cur1) prev->next=cur1;
        if(cur2) prev->next=cur2;

        return head.next;
    }

    ListNode* merge(vector<ListNode*>& lists,int left,int right)
    {
        //递归结束条件
        if(left > right) return nullptr;
        if(left == right) return lists[left];

        //1.平分数组
        int mid=(right-left)/2+left;

        //2.递归处理左右区间
        ListNode* l1=merge(lists,left,mid);
        ListNode* l2=merge(lists,mid+1,right);

        //3.合并两个有序链表
        return mergeTowList(l1,l2);
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        return merge(lists,0,lists.size()-1);
    }
};

 5.K个一组翻转链表

25. K 个一组翻转链表 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
        //1.求出需要多少组
        int len=0;
        ListNode* cur=head;
        while(cur != nullptr)
        {
            len++;
            cur=cur->next;
        }
        int num=len/k;    

        //2.重复num次长度为k的逆序
        ListNode* phead = new ListNode(0);
        ListNode* prev = phead;
        cur=head;   //更新cur
        for(int i=0;i<num;i++)  //num组
        { 
            ListNode* tmp=cur;   //先记录最先进去的那个位置
            for(int j=0;j<k;j++)  //K个
            {
                ListNode* next=cur->next;
                cur->next=prev->next;
                prev->next=cur;
                cur=next;
            }
            prev=tmp;   
        }
        //3.连接上剩下的不需要逆置的
        prev->next=cur;
        
        prev=phead->next;
        delete phead;
        return prev;
    }
};

二.哈希表

1.两数之和

1. 两数之和 - 力扣(LeetCode)

 

//暴力枚举
class Solution 
{
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        for(int i=0;i<nums.size();i++)
        {
            for(int j=i-1;j>=0;j--)
            {
                if(nums[i]+nums[j]==target) return {i,j};
            }
        }
        return {-1,-1};
    }
};
class Solution 
{
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    { 
        unordered_map<int,int> hash;   // <nums[i],i>
        for(int i=0;i<nums.size();i++)
        {
            int x=target-nums[i];
            if(hash.count(x)) return {hash[x],i};
            hash[nums[i]]=i;
        }
        return {0,0};
    }
};

2.判断是否互为字符重排

面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode)

class Solution 
{
public:
    bool CheckPermutation(string s1, string s2)
    {
        int hash[26]={0};
        if(s1.size() != s2.size()) return false;
        //1.统计
        for(int i=0;i<s1.size();i++)
        {
            hash[s1[i]-'a']++;
            hash[s2[i]-'a']--;
        }
        //2.比较
        for(int i=0;i<26;i++)
        {
            if(hash[i] != 0) return false;
        }
        return true;
    }
};

 3.存在重复元素

217. 存在重复元素 - 力扣(LeetCode)

class Solution 
{
public:
    bool containsDuplicate(vector<int>& nums) 
    {
        unordered_map<int,int> hash;
        for(int i=0;i<nums.size();i++)
        {
            if(hash.count(nums[i])==1) return true;
            hash[nums[i]]++;
        }
        return false;
    }
};

 4.存在重复元素Ⅱ

219. 存在重复元素 II - 力扣(LeetCode)

class Solution 
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    {
        unordered_map<int,int> hash;
        int n=nums.size();
        if(n<=1) return false;

        for(int i=0;i<n;i++)
        {
            if(hash.count(nums[i]) && abs(hash[nums[i]]-i)<=k) return true;  //先判断有没有,再丢进去
            hash[nums[i]]=i;    //没有再丢进去
        }
        return false;
    }
};

 5.字母异位词分组

49. 字母异位词分组 - 力扣(LeetCode)

 

class Solution 
{
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) 
    {
        vector<vector<string>> ans;
        unordered_map<string,vector<string>> hash;
        //1.先将所有异位词分组
        for(int i=0;i<strs.size();i++)
        {
            string s(strs[i]);
            sort(strs[i].begin(),strs[i].end());
            hash[strs[i]].push_back(s);
        }
        //2.将不同的组分开输出
        for(auto x:hash)
        {
            ans.push_back(x.second);   //hash表中,第一个元素就是x.first,第二个就是x.second
        }
        return ans;
    }
};

三.字符串 

1.最长公共前缀

14. 最长公共前缀 - 力扣(LeetCode)

 

class Solution 
{
public:
    string findCommon(string& s1,string& s2)
    {
        int i=0;
        while(i<min(s1.size(),s2.size()) && s1[i]==s2[i]) i++;
        return s1.substr(0,i);   //切割字符串函数;
    }

    string longestCommonPrefix(vector<string>& strs) 
    {
        //两两比较
        string ret=strs[0];
        for(int i=0;i<strs.size();i++)
        {
            ret=findCommon(ret,strs[i]);
        }
        return ret;
    }
};

class Solution 
{
public:
    string longestCommonPrefix(vector<string>& strs) 
    {
        string ans;
        int k=strs.size();
        int sum=0;
        //一起比较
        for(int i=0;i<=200;i++)
        {
            sum=0;
            for(int j=0;j<k;j++)
            {
                if(i>=strs[j].size()) return ans;
                if(strs[j][i]==strs[0][i]) sum++;
            }
            if(sum==k) ans += strs[0][i];
            if(sum<k)  return ans;
        }
        return ans;
    }
};

2.最长回文字串

5. 最长回文子串 - 力扣(LeetCode)

class Solution 
{
public:
    string longestPalindrome(string s) 
    {
        //中心扩展算法
        int begin=0,len=0,n=s.size();
        for(int i=0;i<n;i++)  //依次枚举所有的中点
        {
            //奇数长度的扩展
            int left=i,right=i;
            while(left>=0 && right<n && s[left]==s[right])
            {
                left--;
                right++;
            }
            if(right-left-1 > len)  //[left,begin,end,right] len=right-left+1-2
            {
                begin=left+1;
                len=right-left-1;
            }
            
            //偶数长度的扩展
            left=i,right=i+1;
             while(left>=0 && right<n && s[left]==s[right])
            {
                left--;
                right++;
            }
            if(right-left-1 > len)  //[left,begin,end,right] len=right-left+1-2
            {
                begin=left+1;
                len=right-left-1;
            }
        }
        return s.substr(begin,len);
    }
};

3.二进制求和

67. 二进制求和 - 力扣(LeetCode)

class Solution 
{
public:
    string addBinary(string a, string b) 
    {
        string ans;
        int cur1=a.size()-1,cur2=b.size()-1,t=0;
        while(cur1>=0 || cur2>=0 || t)
        {
            if(cur1>=0) t += a[cur1--]-'0';  //注意a中的0是字符‘0’
            if(cur2>=0) t += b[cur2--]-'0';

            ans.push_back(t%2+'0');
            t=t/2;
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

4.字符串相乘

43. 字符串相乘 - 力扣(LeetCode)

class Solution 
{
public:
    string multiply(string num1, string num2) 
    {
        //1.无进位相乘后相加
        int m=num1.size(),n=num2.size();
        reverse(num1.begin(),num1.end());
        reverse(num2.begin(),num2.end());
        vector<int> tmp(m+n);   //用来存放无进位相加的结果

        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                tmp[i+j] += (num2[i]-'0')*(num1[j]-'0');

        //2.处理进位
        int cur=0,t=0;
        string ans;
        while(cur<m+n-1 || t)
        {
            if(cur<m+n-1) t += tmp[cur++];
            ans += (t%10+'0');
            t=t/10;
        }
         
        //3.处理前导0
        while(ans.size()>1 && ans.back()=='0') ans.pop_back();
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

四.栈

1.删除字符串中的所有相邻重复项

1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)

class Solution 
{
public:
    string removeDuplicates(string s) 
    {
        string ans;     //用字符串模拟栈
        for(int i=0;i<s.size();i++)
        {
            if(ans.size() && ans.back()==s[i]) ans.pop_back();   //用ans.back()时要注意ans不能为空
            else  ans.push_back(s[i]);
        }
        return ans;
    }
};

 2.比较含退格的字符串

844. 比较含退格的字符串 - 力扣(LeetCode)

class Solution 
{
public:
    string final_string(string s)
    {
        string ans;
        for(int i=0;i<s.size();i++)
        {
            if(ans.size() && s[i]=='#') ans.pop_back();
            else if(s[i]!='#') ans.push_back(s[i]);
        }
        return ans;
    }
    bool backspaceCompare(string s, string t) 
    {
        return final_string(s).compare(final_string(t)) == 0;
    }
};

 3.基本计算器Ⅱ

227. 基本计算器 II - 力扣(LeetCode)

class Solution 
{
public:
    int calculate(string s) 
    {
        vector<int> st;   //用数组来模拟栈
        int i=0,n=s.size();
        char op='+';
        while(i<n)
        {
            if(s[i]==' ') i++;
            else if(s[i]>='0' && s[i]<='9')
            {
                //先把这个数字全拿出来
                int tmp=0;
                while(i<n && s[i]>='0' && s[i]<='9') tmp=tmp*10+(s[i++]-'0');
                
                //看符号分情况讨论
                if(op=='+') st.push_back(tmp);
                else if(op=='-') st.push_back(-tmp);
                else if(op=='*') st.back() *= tmp;
                else if(op=='/') st.back() /= tmp;

                //上面的while已经跳到相应的位置了,不需要i++了
            }
            else 
            {
                op = s[i];
                i++;
            }
        }

        //处理栈
        int ans=0;
        for(auto x:st) ans += x;
        return ans;
    }
};

4.字符串解码

394. 字符串解码 - 力扣(LeetCode)

class Solution 
{
public:
    string decodeString(string s) 
    {
        stack<string> str;
        stack<int> st;
        str.push("");

        int i=0,n=s.size();
        while(i<n)
        {
            if(s[i]>='0' && s[i]<='9')
            {
                int tmp=0;
                while(i<n && s[i]>='0' && s[i]<='9') tmp=tmp*10+(s[i++]-'0');  //提取数字
                st.push(tmp);
            }
            else if(s[i]=='[')
            {
                i++;       //注意是提取'['后面的字符
                string s0;
                while(i<n && s[i]>='a' && s[i]<='z') s0 += s[i++];
                str.push(s0);
            }
            else if(s[i]==']')
            {
                string ss;
                for(int j=0;j<st.top();j++) ss += str.top();
                str.pop();
                st.pop();
                str.top() += ss;
                i++;
            }
            else 
            {
                string s1;
                while(i<n && s[i]>='a' && s[i]<='z') s1 += s[i++];
                str.top() += s1;
            }            
        }
        return str.top();
    }
};

 5.验证栈序列

946. 验证栈序列 - 力扣(LeetCode)

class Solution 
{
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) 
    {
        stack<int> st;
        for(int i=0,j=0;i<pushed.size();i++)
        {
            st.push(pushed[i]);  //进栈
            while(st.size() && st.top()==popped[j])  //一直出栈
            {
                st.pop();
                j++;
            }
        }
        return st.size()==0;
    }
};

五.队列+宽搜(BFS)

1.N叉树的层序遍历

429. N 叉树的层序遍历 - 力扣(LeetCode)

/*
// Definition for a Node.
class Node 
{
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution 
{
public:
    vector<vector<int>> levelOrder(Node* root) 
    {   
        vector<vector<int>> ans;
        queue<Node*>  q;   //层序遍历
        int sum=0;

        if(root == nullptr) return ans;
        else q.push(root);

        while(q.size())
        {
            vector<int> tmp;
            sum=q.size();
            Node* node;
            for(int i=0;i<sum;i++) 
            {
                node=q.front();
                q.pop();
                tmp.push_back(node->val);
                for(int j=0;j<node->children.size();j++) //加入孩子结点
                {
                    q.push(node->children[j]);
                }
            }
            ans.push_back(tmp);
        }
        return ans;
    }
};

2.二叉树的锯齿形层序遍历

103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) 
    {
        vector<vector<int>> ans;
        queue<TreeNode*> q;
        int flag=1;

        if(root==nullptr) return ans;
        else q.push(root);

        while(q.size())
        {
            vector<int> tmp;
            int sz=q.size();
            for(int i=0;i<sz;i++)
            {
                TreeNode* node = q.front();
                q.pop();
                tmp.push_back(node->val);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            if(flag==-1) reverse(tmp.begin(),tmp.end());
            ans.push_back(tmp);
            flag *= -1;
        }
        return ans;
    }
};

3.二叉树的最大宽度

662. 二叉树最大宽度 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    int widthOfBinaryTree(TreeNode* root) 
    {
        vector<pair<TreeNode*,unsigned int>> q;   //用数组模拟队列
        q.push_back({root,1});
        unsigned int sum=0;

        while(q.size())
        {
            //1.先更新这一层的宽度
            auto& [x1,y1]=q[0];
            auto& [x2,y2]=q.back();
            sum=max(sum,y2-y1+1);

            //2.让下一层入队
            vector<pair<TreeNode*,unsigned int>> tmp;
            for(auto& [x,y]:q)
            {
                if(x->left) tmp.push_back({x->left,2*y});
                if(x->right) tmp.push_back({x->right,2*y+1});
            }
            q=tmp;  //把之前的q覆盖掉,完成进队操作
        }
        return sum;
    }
};

4.在每个树行中找最大值

515. 在每个树行中找最大值 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    vector<int> largestValues(TreeNode* root) 
    {
        vector<int> ans;
        queue<TreeNode*> q;

        if(root==nullptr) return ans;
        else q.push(root);

        while(q.size())
        {
            int sz=q.size(); 
            int m=INT_MIN;
            for(int i=0;i<sz;i++)
            {
                TreeNode* node = q.front();
                m=max(m,node->val);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
                q.pop();
            }
            ans.push_back(m);
        }
        return ans;
    }
};

 六.优先级队列(堆)——最好会手写堆

1.最后一块石头的重量

1046. 最后一块石头的重量 - 力扣(LeetCode)

class Solution 
{
public:
    int lastStoneWeight(vector<int>& stones) 
    {
        priority_queue<int> heap;  //创建一个大根堆(默认是大根堆)
        for(auto x:stones) heap.push(x);

        while(heap.size()>1)
        {
            int a=heap.top();
            heap.pop();
            int b=heap.top();
            heap.pop();
            
            if(a>b) heap.push(a-b);
        }
        if(heap.size()==1)return heap.top();
        else return 0;
    }
};

2.数据流中的第K大的元素

703. 数据流中的第 K 大元素 - 力扣(LeetCode)

class KthLargest 
{
public:
    //创建一个大小为K的小根堆
    priority_queue<int,vector<int>,greater<int>> heap;  //小根堆用greater
    int _k;

    KthLargest(int k, vector<int>& nums) 
    {
        _k=k;
        for(auto x:nums)
        {
            heap.push(x);
            if(heap.size()>_k) heap.pop();
        }
    }
    
    int add(int val) 
    {
        heap.push(val);
        if(heap.size()>_k) heap.pop();
        return heap.top();
    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

3.前K个高频单词

692. 前K个高频单词 - 力扣(LeetCode)

class Solution 
{
public:
    typedef pair<string,int> pa;
    struct cmp
    {
        bool operator()(const pa& a,const pa& b)
        {
            if(a.second==b.second)  //频次相同时,字典按照大根堆方式排列(一般字典顺序,留下ASCII小的,先出去大的,所以大根堆)
            {
                return a.first < b.first;    //大堆比小,小堆比大
            }
            return a.second>b.second;   //频次不同,就按频次来,频次需要建立小根堆(小的出去,最后大的留下,所以小根堆)
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) 
    {
        //1.统计单词频次
        unordered_map<string,int> hash;
        for(auto s:words) hash[s]++;

        //2.创建一个大小为K的堆
        priority_queue<pa,vector<pa>,cmp> heap;  //cmp是比较函数

        //3.TOPK主逻辑
        for(auto x:hash)
        {
            heap.push(x);
            if(heap.size()>k) heap.pop();
        }

        //4.提取结果
        vector<string> ans(k);
        //heap的主逻辑(最外层,频次)是一个小堆
        for(int i=k-1;i>=0;i--)
        {
            ans[i]=heap.top().first;
            heap.pop();
        }
        return ans;
    }
};

4.数据流的中位数

295. 数据流的中位数 - 力扣(LeetCode)

class MedianFinder 
{
public:
    priority_queue<int,vector<int>,less<int>> left;   //左边是大根堆
    priority_queue<int,vector<int>,greater<int>> right;  //右边是小根堆

    MedianFinder() 
    {}
    
    void addNum(int num) 
    {
        //分类讨论即可
        if(left.size()==right.size())  //左右堆元素个数相同
        {
            if(left.empty()) left.push(num); //放左堆
            else if(left.size() && num<=left.top())  //注意用left.top()一定要判断是否为空
            {
                left.push(num);
            }
            else  //进右堆
            {
                right.push(num);
                left.push(right.top());
                right.pop();
            }
        }
        else     //左右堆元素个数不同,(m=n+1),左边多
        {
            if(num<=left.top())
            {
                left.push(num);
                right.push(left.top());
                left.pop();
            }
            else
            {
                right.push(num);
            }
        }
    }
    
    double findMedian() 
    {
        if(left.size()==right.size()) return (left.top()+right.top())/2.0;
        else return left.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

七.BFS之FloodFill算法(还是用队列,找性质相同的连同块,四个方向都走)

1.图像渲染

733. 图像渲染 - 力扣(LeetCode)

class Solution 
{
public:
    typedef pair<int,int> PII;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};  //dx,dy为坐标增量
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) 
    {
        int prev=image[sr][sc];  //先标记一下需要修改的像素值
        if(prev==color) return image;  //不用修改了

        int m=image.size(),n=image[0].size();   //求一下横纵坐标最大值n,m
        queue<PII> q;
        q.push({sr,sc});
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            image[a][b]=color;
            for(int i=0;i<4;i++)
            {
                int x=a+dx[i],y=b+dy[i];
                if(x>=0 && x<m && y>=0 && y<n && image[x][y]==prev)  //注意增量的坐标不能越界
                {
                    q.push({x,y});
                }
            }
        }
        return image;
    }
};

2.岛屿数量

200. 岛屿数量 - 力扣(LeetCode)

class Solution 
{
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    
public:
    void bfs(vector<vector<char>>& grid,int i,int j,vector<vector<bool>>& vis)
    {
        queue<pair<int,int>> q;
        q.push({i,j});
        vis[i][j]=true;
        int m = grid.size(), n = grid[0].size();

        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];
                int y=b+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y]=='1') 
                {
                    q.push({x,y});
                    vis[x][y]=true;   //别忘了把这个位置修改成true
                }
            }
        }
    }

    int numIslands(vector<vector<char>>& grid) 
    {
        int ans=0;
        int m=grid.size(),n=grid[0].size();
        vector<vector<bool>> vis(m,vector<bool>(n,false));
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1' && !vis[i][j])
                {
                    ans++;
                    bfs(grid,i,j,vis);    //把这块陆地全部标记成true
                }
            }
        }
        return ans;
    }
};

3.岛屿最大面积

695. 岛屿的最大面积 - 力扣(LeetCode)

class Solution 
{
public:
    int dx[4] = { 0,0,1,-1 };
    int dy[4] = { 1,-1,0,0 };
    int bfs(vector<vector<int>>& grid, int i, int j, vector<vector<bool>>& vis)
    {
        int sum = 0;
        int m = grid.size(), n = grid[0].size();
        queue<pair<int, int>> q;
        q.push({ i,j });
        vis[i][j] = true;   //别忘了把该点标记为true
        sum++;

        while (q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k];
                int y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y])
                {
                    q.push({ x,y });
                    vis[x][y] = true;
                    sum++;
                }
            }
        }
        return sum;
    }

    int maxAreaOfIsland(vector<vector<int>>& grid)
    {
        int m = grid.size(), n = grid[0].size();
        int ans = 0;
        vector<vector<bool>> vis(m, vector<bool>(n, false));

        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (!vis[i][j] && grid[i][j] == 1)
                {
                    ans = max(ans, bfs(grid, i, j, vis));
                }
            }
        }
        return ans;
    }
};

4.被围绕的区域

130. 被围绕的区域 - 力扣(LeetCode)

class Solution
{
public:
    int dx[4] = { 0,0,1,-1 };
    int dy[4] = { 1,-1,0,0 };
    bool vis[201][201] = { false };
    void bfs0(vector<vector<char>>& board, int i, int j, int m, int n)  //仅修改vis
    {
        queue<pair<int, int>> q;
        q.push({ i,j });
        vis[i][j] = true;

        while (q.size())
        {
            int a = q.front().first;
            int b = q.front().second;
            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k];
                int y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O' && !vis[x][y])
                {
                    q.push({ x,y });
                    vis[x][y] = true;
                }
            }
        }
    }

    void bfs1(vector<vector<char>>& board, int i, int j, int m, int n) //涂色
    {
        queue<pair<int, int>> q;
        q.push({ i,j });
        board[i][j] = 'X';
        vis[i][j] = true;

        while (q.size())
        {
            int a = q.front().first;
            int b = q.front().second;

            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k];
                int y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O' && !vis[x][y])
                {
                    q.push({ x,y });
                    board[x][y] = 'X';
                    vis[x][y] = true;
                }
            }
        }
    }

    void solve(vector<vector<char>>& board)
    {
        //1.先把四条边上不符合条件的遍历掉
        int m = board.size(), n = board[0].size();
        for (int i = 0; i < m; i++)
        {
            if (board[i][0] == 'O') 
                bfs0(board, i, 0, m, n);
            if (board[i][n - 1] == 'O') 
                bfs0(board, i, n - 1, m, n);
        }
        for (int j = 0; j < n; j++)
        {
            if (board[0][j] == 'O') 
                bfs0(board, 0, j, m, n);
            if (board[m - 1][j] == 'O') 
                bfs0(board, m - 1, j, m, n);
        }
        for (int i = 1; i < m - 1; i++)
        {
            for (int j = 1; j < n - 1; j++)
            {
                if (board[i][j] == 'O' && !vis[i][j]) bfs1(board, i, j, m, n);
            }
        }
    }
};

八.BFS解决最短路(边权相同,四个方向选一个走)

1. 迷宫中离入口最近的出口

1926. 迷宫中离入口最近的出口 - 力扣(LeetCode)

 

class Solution 
{
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};

    int bfs(vector<vector<char>>& maze,int i,int j)
    {
        int m=maze.size(),n=maze[0].size();
        bool vis[m][n];
        memset(vis,false,sizeof(vis));
        
        queue<pair<int,int>> q;
        q.push({i,j});
        vis[i][j]=true;
        int step=0;

        while(q.size())
        {
            step++;       //在向四面走之前步数++
            int sz=q.size(); 
            for(int k=0;k<sz;k++)  //队列一次出sz个元素
            {
                auto [a,b]=q.front();
                q.pop();
                for(int h=0;h<4;h++)
                {
                    int x=a+dx[h],y=b+dy[h];
                    if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && maze[x][y]=='.')
                    {
                        //判断是否为出口
                        if(x==0 || x==m-1 || y==0 || y==n-1) return step;
                        q.push({x,y});
                        vis[x][y]=true;
                    }
                }
            }
        }
        return -1;
    }

    int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) 
    {
        return bfs(maze,entrance[0],entrance[1]);
    }
};

2.最小基因变化

433. 最小基因变化 - 力扣(LeetCode)

class Solution 
{
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) 
    {
        unordered_set<string> vis;   //用来标记当前基因序列是否出现过
        unordered_set<string> hash(bank.begin(),bank.end());   //存储基因库里的字符串
        string change="ACGT";   //一共有ACGT四种变化

        if(startGene == endGene) return 0;
        if(hash.count(endGene)==0) return -1;

        queue<string> q;
        q.push(startGene);
        vis.insert(startGene);

        int step=0;
        while(q.size())
        {
            step++;
            int sz=q.size();
            while(sz--)  //每走一步都要出sz个
            {
                string s=q.front();
                q.pop();
                for(int i=0;i<8;i++)
                {
                    string tmp=s;
                    for(int j=0;j<4;j++)
                    {
                        tmp[i]=change[j];
                        if(vis.count(tmp)==0 && hash.count(tmp))  //没出现过并且在库中
                        {
                            if(tmp==endGene) return step;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
            
        }
        return -1;
    }
};

3.单词接龙

127. 单词接龙 - 力扣(LeetCode)

class Solution 
{
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) 
    {
        unordered_set<string> vis;
        unordered_set<string> dic(wordList.begin(),wordList.end());   //把字典放到哈希表里

        if(beginWord==endWord) return 1;
        if(dic.count(endWord)==0) return 0;

        queue<string> q;
        q.push(beginWord);
        vis.insert(beginWord);
        int ans=1;

        while(q.size())
        {
            int sz=q.size();
            ans++;
            while(sz--)
            {
                string s=q.front();
                q.pop();
                for(int i=0;i<s.size();i++)
                {
                    string tmp=s;
                    for(char x='a';x<='z';x++)
                    {
                        tmp[i]=x;
                        if(vis.count(tmp)==0 && dic.count(tmp))
                        {
                            if(tmp==endWord) return ans;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;        
    }
};

 4.为高尔夫比赛砍树

675. 为高尔夫比赛砍树 - 力扣(LeetCode)

 

class Solution 
{
    int m,n;
public:
    bool vis[51][51];
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    
    int bfs(vector<vector<int>>& forest,int bx,int by,int ex,int ey)
    {
        if(bx==ex && by==ey) return 0;

        queue<pair<int,int>> q;
        memset(vis,0,sizeof(vis));
        q.push({bx,by});
        vis[bx][by]=true;

        int step=0;
        while(q.size())
        {
            step++;
            int sz=q.size();
            while(sz--)
            {
                auto [a,b]=q.front();
                q.pop();
                for(int i=0;i<4;i++)
                {
                    int x=a+dx[i],y=b+dy[i];
                    if(x==ex && y==ey) return step;
                    if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && forest[x][y])
                    {
                        q.push({x,y});
                        vis[x][y]=true;
                    }
                }
            }
        }
        return -1;
    }

    int cutOffTree(vector<vector<int>>& forest) 
    {
        m=forest.size(),n=forest[0].size();
        //1.准备工作:找出砍树的顺序
        vector<pair<int,int>> trees;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(forest[i][j]>1) trees.push_back({i,j});  //有需要砍树的地块加进去

        sort(trees.begin(),trees.end(),[&](const pair<int,int>& p1,const pair<int,int>& p2)
        {
            return forest[p1.first][p1.second] < forest[p2.first][p2.second];   //按照树木高度从低到高排序
        });

        //2.按照顺序砍树
        int begin_x=0,begin_y=0;
        int ret=0;
        for(auto& [a,b]:trees)  //分解若干个找迷宫出口
        {
            int step=bfs(forest,begin_x,begin_y,a,b);
            if(step==-1) return -1;    //找不到通往下一棵树的路线
            ret += step;
            begin_x=a,begin_y=b;   //更新(迷宫)起点位置
        }
        return ret;
    }
};

九.BFS解决多源最短路(大多正难则反)

1.01矩阵

542. 01 矩阵 - 力扣(LeetCode)

class Solution 
{
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};

    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) 
    {
        int m=mat.size(),n=mat[0].size();
        vector<vector<int>> dist(m,vector<int>(n,-1));
        queue<pair<int,int>> q;

        //1.把所有源点加入到队列中(构成超级源点)
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(mat[i][j]==0)
                {
                    dist[i][j]=0;
                    q.push({i,j});
                }

        //2.一层一层的往外扩展
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k],y=b+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && dist[x][y]==-1)
                {
                    dist[x][y]=dist[a][b]+1;   //这个点比ab多1的距离
                    q.push({x,y});
                }
            }
        }
        return dist;
    }
};

 2.飞地的数量

1020. 飞地的数量 - 力扣(LeetCode)

class Solution 
{
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    int numEnclaves(vector<vector<int>>& grid) 
    {
        int m=grid.size(),n=grid[0].size();
        vector<vector<bool>> vis(m,vector<bool>(n,false));
        queue<pair<int,int>> q;

        //1.把边上的1加入队列,作为超级源点,找里面的1
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(i==0 || i==m-1 || j==0 || j==n-1)
                {
                    if(grid[i][j]==1)
                    {
                        q.push({i,j});
                        vis[i][j]=true;
                    }
                }

        //2.多源bfs
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k],y=b+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y]==1)
                {
                    vis[x][y]=true;
                    q.push({x,y});
                }
            }
        }

        //3.统计结果
        int ans=0;
        for(int i=1;i<m-1;i++)
            for(int j=1;j<n-1;j++)
                if(grid[i][j]==1 && !vis[i][j]) ans++;
        return ans;
    }
};

3.地图中的最高点

1765. 地图中的最高点 - 力扣(LeetCode)

class Solution 
{
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater) 
    {
        int m=isWater.size(),n=isWater[0].size();
        vector<vector<int>> hight(m,vector<int>(n,-1));  //hight初始化为-1可以少创建一个vis数组
        queue<pair<int,int>> q;

        //1.把所有源点加入队列,构成超级源点,进行多源bfs
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(isWater[i][j]==1)
                {
                    hight[i][j]=0;
                    q.push({i,j});
                }
        
        //2.进行多源bfs
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k],y=b+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && hight[x][y]==-1)  //注意是hight[x][y]==-1(没搜索过)
                {
                    hight[x][y]=hight[a][b]+1;
                    q.push({x,y});               
                }
            }
        }
        return hight;
    }
};

4.地图分析

1162. 地图分析 - 力扣(LeetCode)

class Solution 
{
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    int maxDistance(vector<vector<int>>& grid) 
    {
        int n=grid.size(),sum=0,len=0;
        vector<vector<int>> dist(n,vector<int>(n,-1));
        queue<pair<int,int>> q;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(grid[i][j]==1)
                {
                    dist[i][j]=0;
                    q.push({i,j});
                    sum++;
                }
        if(sum==0 || sum==n*n) return -1;

        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k],y=b+dy[k];
                if(x>=0 && x<n && y>=0 && y<n && dist[x][y]==-1)
                {
                    dist[x][y]=dist[a][b]+1;
                    q.push({x,y});
                    len=max(len,dist[x][y]);
                }
            }
        }
        return len;
    }
};

十.BFS解决拓扑排序

 1.课程表

207. 课程表 - 力扣(LeetCode)

class Solution 
{
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) 
    {
        //1.准备工作
        unordered_map<int,vector<int>> edges;   //邻接表存图
        vector<int> in(numCourses);   //标记每一个结点的入度

        //2.建图
        for(auto& e:prerequisites)
        {
            int a=e[0],b=e[1];   //b -> a 的一条边
            edges[b].push_back(a);
            in[a]++;
        }

        //3.拓扑排序
        queue<int> q;
        //(1)先把所有入度为0的点加入到队列中
        for(int i=0;i<numCourses;i++)
        {
            if(in[i]==0)
                q.push(i);
        }
        //(2)bfs
        while(q.size())
        {
            int t=q.front();
            q.pop();
            for(int k:edges[t])  //从该点的邻接矩阵中遍历
            {
                in[k]--;   //指向下一个点的入度-1
                if(in[k]==0) q.push(k);
            }
        }

        //4.判断是否有环
        for(int i=0;i<numCourses;i++)
        {
            if(in[i]) return false;
        }
        return true;
    }
};

2.课程表Ⅱ

210. 课程表 II - 力扣(LeetCode)

class Solution 
{
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) 
    {
        //1.准备工作
        unordered_map<int,vector<int>> edges;  //int对应的邻接矩阵vector<int>
        // vector<vector<int>> edges(numCourses);  //这是用邻接表存图
        vector<int> in(numCourses);
        vector<int> ans;
        //2.建图
        for(auto& e:prerequisites)
        {
            int a=e[0],b=e[1];
            edges[b].push_back(a);
            in[a]++;
        }

        //3.拓扑排序
        queue<int> q;
        //(1)先把所有入度为0的结点加入到队列中
        for(int i=0;i<numCourses;i++)
        {
            if(in[i]==0) q.push(i);
        }

        //(2)bfs
        while(q.size())
        {
            int t=q.front();
            ans.push_back(t);
            q.pop();
            for(int i:edges[t])  //从临边矩阵里遍历
            {
                in[i]--;
                if(in[i]==0) q.push(i);
            }
        }
        //4.判断是否有环
        if(ans.size()==numCourses) return ans;
        else return {};
    }
};

3.火星词典

LCR 114. 火星词典 - 力扣(LeetCode)

class Solution 
{
    unordered_map<char,unordered_set<char>> edges;   //用邻接矩阵来存储图,用unordered_set是为了防止冗余
    unordered_map<char,int> in;  //统计字符对应的入度,之前vector<int>可以理解为unordered_map<int,int>
    bool cheak;
public:
    void add(string& s1,string& s2)
    {
        int n=min(s1.size(),s2.size());
        int i=0;
        for( ;i<n;i++)
        {
            if(s1[i]!=s2[i])
            {
                char a=s1[i],b=s2[i];  //a -> b
                if(edges.count(a)==0 || edges[a].count(b)==0)
                {
                    edges[a].insert(b);
                    in[b]++;
                }
                break;
            }   
        }
        if(i==s2.size() && i<s1.size()) cheak=true;
    }

    string alienOrder(vector<string>& words) 
    {
        //1.建图+初始化入度哈希表
        for(auto& s:words)
        {
            for(auto ch:s)
            {
                in[ch]=0;  //把入度都标为0
            }
        }
        int n=words.size();
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                add(words[i],words[j]);
                if(cheak) return "";
            }
        }

        //2.拓扑排序
        queue<char> q;
        for(auto& [a,b]:in)
        {
            if(b==0) q.push(a);  //先把入度为0的结点加入队列
        }
        string ret;
        while(q.size())
        {
            char t=q.front();
            q.pop();
            ret += t;
            for(char ch:edges[t])
            {
                in[ch]--;
                if(in[ch]==0) q.push(ch);
            }
        }

        //3.判断
        for(auto& [a,b]:in)
        {
            if(b != 0) return ""; 
        }
        return ret;
    }  
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对玛导至昏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值