leetcode解题思路分析(九十八)846 - 852 题

  1. 一手顺子
    爱丽丝有一手(hand)由整数数组给定的牌。 现在她想把牌重新排列成组,使得每个组的大小都是 W,且由 W 张连续的牌组成。如果她可以完成分组就返回 true,否则返回 false。

记录每个牌是否用过,排序后依次找即可

class Solution {
    
    
public:
    bool isNStraightHand(vector<int>& hand, int groupSize) {
    
    
        int len = hand.size();
        // 两种特例
        if(len % groupSize != 0) return false; // 不能整除 直接返回false
        if(groupSize == 1) return true; // groupSize为1 直接返回true

        // 排序
        sort(hand.begin(),hand.end());

        // 记录数组元素是否被用过
        vector<int> used(len,false);
        // i为第一个指针 遍历hand数组 
        for(int i=0;i<len;i++){
    
    
            // 元素已被使用则跳过
            if(used[i]) continue;
            // 剩下的牌数量不足以构成一个新的W
            if(i>len-groupSize) return false;
            // 取use[i]为这一手牌中的第一张牌 并记录该牌已被使用
            int cur = hand[i];            
            used[i] = true;

            int tar = cur + 1; // tar为下一张需要找到的牌
            int end = cur + groupSize - 1;// end为这一手牌中的最后一张牌
            for(int j=i+1;j<len;j++){
    
    
                if(used[j]) continue; // 若已被用过 则跳过
                if(hand[j]>tar) return false;// hand[j]>tar 说明缺少需要的牌
                else if(hand[j]==tar){
    
    
                    // hand[j]==tar 说明已找到下一张需要的牌
                    used[j] = true;
 
                    if(hand[j]==end)break; // 已找到end这张牌 跳出循环 重新开始找新的一组牌
                    // 还未到end 继续找下一个tar
                    else tar++;
                }
            }
        }
        return true;
    }
};

  1. 访问所有节点的最短路径
    存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号。给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边

采用BFS+状态压缩得解

class Solution {
    
    
public:
    int shortestPathLength(vector<vector<int>>& graph) {
    
    
        int n = graph.size();

        // 1.初始化队列及标记数组,存入起点
        queue< tuple<int, int, int> > q; // 三个属性分别为 idx, mask, dist
        vector<vector<bool>> vis(n, vector<bool>(1 << n)); // 节点编号及当前状态
        for(int i = 0; i < n; i++) {
    
    
            q.push({
    
    i, 1 << i, 0}); // 存入起点,起始距离0,标记
            vis[i][1 << i] = true;
        }

        // 开始搜索
        while(!q.empty()) {
    
    
            auto [cur, mask, dist] = q.front(); // 弹出队头元素
            q.pop();

            // 找到答案,返回结果
            if(mask == (1 << n) - 1) return dist;

            // 扩展
            for(int x : graph[cur]) {
    
    
                int nextmask = mask | (1 << x);
                if(!vis[x][nextmask]) {
    
    
                    q.push({
    
    x, nextmask, dist + 1});
                    vis[x][nextmask] = true;
                }
            }
        }
        return 0;
    }
};

  1. 字母移位
    有一个由小写字母组成的字符串 S,和一个整数数组 shifts。我们将字母表中的下一个字母称为原字母的 移位(由于字母表是环绕的, ‘z’ 将会变成 ‘a’)。例如·,shift(‘a’) = ‘b’, shift(‘t’) = ‘u’,, 以及 shift(‘z’) = ‘a’。对于每个 shifts[i] = x , 我们会将 S 中的前 i+1 个字母移位 x 次。返回将所有这些移位都应用到 S 后最终得到的字符串。

前缀和的使用

class Solution {
    
    
public:
    string shiftingLetters(string s, vector<int>& shifts) {
    
    
        vector<int>presum;
        long long sumall=shifts.back();
        presum.push_back(sumall);
        reverse(shifts.begin(),shifts.end());
        for(int i=1;i<shifts.size();i++){
    
    
            sumall%=26;
            sumall+=shifts[i];
            sumall%=26;
            presum.push_back(sumall);
        }
        reverse(presum.begin(),presum.end());
        //cout<<presum[2]<<endl;
        for(int i=0;i<s.size();i++){
    
    
            int inter=presum[i]%26;
            int index=s[i]-'a';
            s[i]=(inter+index)%26+'a';
        }
        return s;
    }
};
  1. 到最近的人的最大距离
    给你一个数组 seats 表示一排座位,其中 seats[i] = 1 代表有人坐在第 i 个座位上,seats[i] = 0 代表座位 i 上是空的(下标从 0 开始)。至少有一个空座位,且至少有一人已经坐在座位上。亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座位上。返回他到离他最近的人的最大距离。

双指针滑动解题

class Solution {
    
    
public:
    int maxDistToClosest(vector<int>& seats) {
    
    
        int pre = -1;   //上一个已坐位置,初始值为-1
        int maxdis = 0; //两个已坐位置间的最大长度
        int n = seats.size();
        for (int i = 0; i < n; i++) {
    
    
            if (seats[i] == 1) {
    
        //已坐座位
                //第一个已坐座位
                if (pre == -1) {
    
    
                    maxdis = i * 2;
                }
                else if (i - pre > 1) {
    
    
                    maxdis = max(maxdis, i - pre - 1);
                }
                pre = i;    //更新pre
            }
            
            //非已坐座位且是最后一个座位
            else if (i == n - 1) {
    
    
                maxdis = max(maxdis, 2 * (i - pre));
            }
        }
        return maxdis - maxdis / 2; //离他最近的人的最大距离为maxdis - maxdis / 2
    }
};


  1. 矩形面积2
    我们给出了一个(轴对齐的)矩形列表 rectangles 。 对于 rectangle[i] = [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐标,(x2,y2)是该矩形右上角的坐标。找出平面中所有矩形叠加覆盖后的总面积。 由于答案可能太大,请返回它对 10 ^ 9 + 7 取模的结果。

线性扫描

class Solution 
{
    
    
private:
    const static int MOD = 1e9 + 7;
    const static int OPEN = 0;
    const static int CLOSE = 1;

    // 计算宽度:其实就是不断累加更大的x的差值即可
    int QueryWidth(multiset<pair<int,int>>& activate)
    {
    
    
        int res = 0;
        int maxX = -1;
        for (auto [x1, x2] : activate)
        {
    
    
            maxX = max(maxX, x1);
            // 如果x变大了,则计算差值累加更大的宽度
            res += max(0, x2 - maxX);
            // 不断更新最大x
            maxX = max(maxX, x2);
        }
        return res;
    }

public:
    int rectangleArea(vector<vector<int>>& rectangles) 
    {
    
    
        vector<vector<int>> rec;
        for (auto v: rectangles)
        {
    
    
            rec.push_back({
    
    v[1], OPEN, v[0], v[2]});
            rec.push_back({
    
    v[3], CLOSE, v[0], v[2]});
        }
        // 排序从下到上来扫描
        sort(rec.begin(), rec.end());
        
        // 存储面积和
        int res = 0;
        // 初始化第一个y的位置
        int lastY = rec[0][0];
        // 当前需要计算的面积横坐标 [x1,x2]
        // 扫描过程中 对于每次OPEN则插入,CLOSE则删除
        multiset<pair<int,int>> activate;

        for (const vector<int> r : rec)
        {
    
    
            int y = r[0];
            int state = r[1];
            int x1 = r[2];
            int x2 = r[3];

            // 累加面积
            res = (res + (long long)QueryWidth(activate)*(y-lastY)) % MOD;
            // 更新上一个y坐标
            lastY = y;
            // 对于每次OPEN则插入,CLOSE则删除
            if (state == OPEN)
            {
    
    
                activate.insert({
    
    x1, x2});
            }
            else
            {
    
    
                activate.erase(activate.find(pair<int,int>{
    
    x1, x2}) );
            }
        }

        return res;
    }
};


  1. 喧闹和富有
    在一组 N 个人(编号为 0, 1, 2, …, N-1)中,每个人都有不同数目的钱,以及不同程度的安静(quietness)。为了方便起见,我们将编号为 x 的人简称为 "person x "。如果能够肯定 person x 比 person y 更有钱的话,我们会说 richer[i] = [x, y] 。注意 richer 可能只是有效观察的一个子集。另外,如果 person x 的安静程度为 q ,我们会说 quiet[x] = q 。现在,返回答案 answer ,其中 answer[x] = y 的前提是,在所有拥有的钱不少于 person x 的人中,person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。

根据有钱程度进行排序,然后再比较安静值得出最终解。

class Solution {
    
    
public:
    vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
    
    
    	int n = quiet.size();
    	vector<vector<int>> g(n);//有向图,富的指向穷的
    	vector<int> indegree(n, 0);//入度
    	for(auto& r : richer)
    	{
    
    
    		g[r[0]].push_back(r[1]);
    		indegree[r[1]]++;
    	}
    	queue<int> q;//点的id
    	vector<int> ans(n, -1);
    	for(int i = 0; i< n; i++)
    		ans[i] = i;//初始化最安静的是自己
    	for(int i = 0; i < n; i++)
    	{
    
    
    		if(indegree[i] == 0)
    		{
    
    
    			q.push(i);
    			//最富裕的人,入度为0
    		}
    	}
    	while(!q.empty())
    	{
    
    
    		int id = q.front();//人的id
    		int q_val = quiet[ans[id]];
    		//到他这为止,最安静的人的安静值
            q.pop();
    		for(auto nt : g[id])//跟他连接的人(比他穷)
    		{
    
    
    			if(q_val < quiet[ans[nt]])
    			//比 nt 更安静的人是 ans[nt], 其安静值没有q_val小
    				ans[nt] = ans[id];
    			if(--indegree[nt] == 0)
    				q.push(nt);
    		}
    	}
    	return ans;
    }
};

  1. 山脉数组的峰顶索引
    符合下列属性的数组 arr 称为 山脉数组 :
    arr.length >= 3
    存在 i(0 < i < arr.length - 1)使得:
    arr[0] < arr[1] < … arr[i-1] < arr[i]
    arr[i] > arr[i+1] > … > arr[arr.length - 1]
    给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i 。

二分查找

class Solution {
    
    
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
    
    
        int size = arr.size();
        int left = 0, right = size, mid = 0, ret = 0;

        while (left < right)
        {
    
    
            mid = (left + right) / 2;

            if (arr[mid] > arr[mid + 1])
            {
    
    
                if (arr[mid] > arr[mid - 1])
                {
    
    
                    ret = mid;
                    break;
                }

                right = mid;
                continue;
            }
            else
            {
    
    
                left = mid;
            }
        }

        return ret;
    }
};

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/121214293