想要和得到中间还有两个字——做到

文章目录

要是搁以前,暑假都是让我用来休闲,看看电影、电视剧、动漫啥的。但是有日,突然想到了一部电视剧,之前上英语课的时候听一老师提起过,士兵突击。当时问我们这部电视剧最好的英文译名应该是啥?老师说是go fighting! 反正闲着也是闲着,就放纵自己几天全身心投入到故事中。
士兵突击小时也看过不少遍,记着就是因为这部剧,导致了一大堆的军旅题材的电视剧出现。小时觉着军旅有种亲切感,消磨了社会上一切的繁杂,可以靠能力去为自己争取机会。
这次暑假看完却对许三多虽然在草原五班,却在坚持着修路这一举动动容。以致于后续剧情都难以给我更深的触动。
在晚上散步兼思考的时间段,冷静地思考了一下士兵突击这部剧。像是中国的阿甘,不过明显不同于阿甘,他所宣传的是中国的一种价值观,是一种主动式的成功。阿甘则更像是一种被动式的成功。
除此之外,这剧有不少句子可当做人生信条去遵守。挑拣几句吧。你可以混日子,小心别让日子把你给混了。
想要和得到中间还有两个字——做到。
我又忽地和2月前的我心灵相通,记起了我曾做的规划。我也许做规划,但是遵不遵守全看心情(笑)。
决定要拿起我的精神武器,向懒惰大军发起我的突击!

2022.7.17

2321.拼接数组的最大分数

https://leetcode.cn/problems/maximum-score-of-spliced-array/submissions/

class Solution {
public:
    int maximumsSplicedArray(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int temp = 0;
        int total = 0;
        for(int i : nums1)
            total += i;
        int result = total;
        for(int i = 0; i < n;++i){
            temp += nums2[i] - nums1[i];
            if(temp < 0)
                temp = 0;
            else
                result = max(result,temp+total);
        }
        total = 0;
        for(int i : nums2)
            total += i;
        temp = 0;
        for(int i = 0; i < n;++i){
            temp += nums1[i] - nums2[i];
            if(temp < 0)
                temp = 0;
            else
                result = max(result,temp+total);
        }
        return result;
    }
};

此题从本质上来说,是对连续最长子段和的变形。我在做的过程中也意识到了,可惜实在是经验不足。意识到了,但是没有看破他的本质。

他的本质是求解两个连续最长子段和中最长的那个。需要借助于差分。

做的时候,我太纠结于两个数组了。之后虽然想到了把这些东西放到一个数组里面,但是就是想不到怎么放。放的规则就是利用差分。真的看起来很是简洁。

565.数组嵌套

https://leetcode.cn/problems/array-nesting/

class Solution {
public:
    int arrayNesting(vector<int>& nums) {
        int max = 0,count = 0,n=nums.size(),x,temp;
       // vector<int> visit(n);
        for(int i = 0; i < n;++i){
            if(nums[i] != -1){
                count = 1;
               // visit[i] = 1;
                x = nums[i];
                nums[i] = -1;
                while(nums[x] != -1){
                    ++count;
                //    visit[x] = 1;
                    temp = x;
                    x = nums[x];
                    nums[temp] = -1;
                }
                if(max < count)
                    max = count;
            }
        }
        return max;
    }
};

水题,圈排序。

2022.7.18

749.隔离病毒

https://leetcode.cn/problems/contain-virus/

/*
此算法的描述如下
首先进行将要使用的元素初始化
然后进行迭代
	进行遍历 如果没有访问过 且感染了 那么就进行dfs 将能访问到的所有点放入其中 dfs是向着四个方向进行遍历 将访问到的顶点 添加其中 将数量同样添加其中
	遍历完后 进行判断 还能否找到未被隔离的区域
	如果没有了 那么退出
	如果还存在 则找到感染数最大的那块区域 进行隔离
	然后将剩余的区域进行扩张
最后返回总数
*/
class Solution {
public:
    const int dir[4][2] = {{0,-1},{0,1},{1,0},{-1,0}};
    //向各个方向进行探索
    const vector<int> dirs = {-1, 0, 1, 0, -1};
    //隔离某一编号的防火墙数量
    vector<int> c;
    //具体区域 直接采取一个整数去表达
    vector<vector<int>> areas;
    //边界的点
    vector<unordered_set<int>> boundaries;
    //具体地图
    vector<vector<int>> infected;
    //是否访问过
    vector<vector<bool>> vis;
    //地图大小 m为行 n为列
    int m;
    int n;

    int containVirus(vector<vector<int>>& isInfected) {
        infected = isInfected;
        m = infected.size();
        n = infected[0].size();
        vis.assign(m, vector<bool>(n));
        int ans = 0;
        while (1)
        {
            for (int i = 0; i < m; ++i) 
                for (int j = 0; j < n; ++j) 
                    vis[i][j] = false;
            c.clear();
            areas.clear();
            boundaries.clear();
            for (int i = 0; i < m; ++i)
            {
                for (int j = 0; j < n; ++j)
                {
                    if (infected[i][j] == 1 && !vis[i][j])//找到了一块区域
                    {
                        c.push_back(0);
                        areas.push_back({});
                        boundaries.push_back({});
                        dfs(i, j);
                    }
                }
            }
            if (areas.empty())//如果已经找不到还未隔离的区域 那么退出
                break;
            int idx = getMax();//获得当前区域中最大的那块
            ans += c[idx];//将防火墙数量加入进去
            for (int t = 0; t < areas.size(); ++t)
            {
                if (t == idx)
                {
                    for (int v : areas[t])
                    {
                        int i = v / n, j = v % n;
                        infected[i][j] = -1;
                    }
                }
                else
                {
                    for (int v : areas[t])
                    {
                        int i = v / n, j = v % n;
                        for (int k = 0; k < 4; ++k)
                        {
                            int x = i + dirs[k], y = j + dirs[k + 1];
                            if (x >= 0 && x < m && y >= 0 && y < n && infected[x][y] == 0)                              infected[x][y] = 1;
                        }
                    }
                }
            }
        }
        return ans;
    }

    int getMax() {
        int idx = 0;
        int mx = boundaries[0].size();
        for (int i = 1; i < boundaries.size(); ++i)//找到最大感染数量的那个 并进行隔离
        {
            int t = boundaries[i].size();
            if (mx < t)
            {
                mx = t;
                idx = i;
            }
        }
        return idx;
    }
    void dfs(int i, int j) {
        vis[i][j] = true;
        areas.back().push_back(i * n + j);
        for (int k = 0; k < 4; ++k)
        {
            int x = i + dirs[k], y = j + dirs[k + 1];
            if (x >= 0 && x < m && y >= 0 && y < n)
            {
                if (infected[x][y] == 1 && !vis[x][y]) dfs(x, y);
                else if (infected[x][y] == 0)
                {
                    c.back() += 1;
                    boundaries.back().insert(x * n + y);
                }
            }
        }
    }
};
class Solution {
public:
	int dir[4][2] = {{0,-1},{0,1},{1,0},{-1,0}};
	vector<int> c;//防火墙数量 算防火墙数量的时候关键在于重复
    vector<vector<int> > areas;//感染区域的点坐标
    vector<set<int> > bounders;//边界上的点 采取set 或者unorder_set 即可 关键在于不重复的点
	vector<vector<bool> > visit;//标记数组
    int m,n;//行数 与列数
    int containVirus(vector<vector<int>>& isInfected) {
        m = isInfected.size();
        n = isInfected[0].size();
	   visit.assign(m, vector<bool>(n));
        int count = 0;
        while(1){
            //清空数据
            for(int i = 0; i != m;++i) for(int j = 0; j != n;++j) visit[i][j] = false;
            areas.clear();
            c.clear();
            bounders.clear();
            //寻找还可感染的区域
            for(int i = 0; i != m;++i){
                for(int j = 0; j != n;++j){
                    //如果是感染区域 并且没有访问过
                    if(isInfected[i][j] == 1 && !visit[i][j]){
                       //添加到结果集合中去
                       areas.push_back({});
                       c.push_back(0);
                       bounders.push_back({});
                        dfs(i,j,isInfected);//寻找这个点集周围的感染点
                    }
                }
            }
            if(c.empty())//如果 已经找不到了
                break;
            int max_id = get_max();
            int ii,jj;//ii 为行 jj 为列
            count += c[max_id];//添加防火墙封锁这块区域
            cout << max_id <<' '<<count<<endl;
            //对于所有的感染区域
            for(int i = 0; i != c.size();++i){
                //如果是已经封锁过的区域 标记无法再次访问 可采用-max_id的形式
                if(i == max_id){
                    //对于已经封锁过的id 将其中所有的感染点标记为 已访问过
                    for(int v:areas[max_id]){
                        ii = v/n,jj = v%n;
                        isInfected[ii][jj] = -1;
                    }
                }else{//对于其他的 需要进行再度扩张
                    for(int v:areas[i]){
                        for(int k = 0;k != 4;++k){
                            ii = v/n + dir[k][0];
                            jj = v%n + dir[k][1];
                            if(ii >= 0 && ii < m && jj >= 0 && jj < n && isInfected[ii][jj] == 0)
                            	isInfected[ii][jj] = 1;
                        }
                    }
                }
            }
        }
        return count;
    }
	int get_max(){//找到将要感染区域最大的那块 是感染区域 不是防火墙数量 一定要注意 这两个有区别
    	int max = bounders[0].size(),max_id = 0;
        for(int i = 1; i != bounders.size();++i){
            if(max < bounders[i].size()){
                max = bounders[i].size();
                max_id = i;
            }
        }
        return max_id;
	}
    void dfs(int i, int j,vector<vector<int> >& isInfected){
        visit[i][j] = true;//标记访问
        areas.back().push_back(i*n+j);
        int ii,jj;
        //向各个方向探索
        for(int k = 0; k != 4;++k){
            ii = i + dir[k][0];
            jj = j + dir[k][1];
            //在合法情况下
            if(ii >= 0 && ii < m && jj >= 0 && jj < n){
                //如果是未感染点 标记可感染 将该点加入到当前区域可感染点的集合中去
            	if(isInfected[ii][jj] == 0){
                    ++c.back();//防火墙数量增加
          			bounders.back().insert(ii*n+jj);//边界数量增加
                }else if(isInfected[ii][jj] == 1 && !visit[ii][jj]){//是感染点 加入到当前区域集合中 dfs
                    dfs(ii,jj,isInfected);
                }
            }
        }
    }
};

1.两数相和

https://leetcode.cn/problems/two-sum/

class Solution {
public:
struct node {
    int num;
    int id;
};
bool static cmp (const struct node & x,const struct node & y){
    return x.num < y.num;
}
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<struct node> num(nums.size());
        for(int i = 0; i != nums.size();++i){
            num[i].num = nums[i];
            num[i].id = i;
        }
    sort(num.begin(),num.end(),cmp);
    auto i = num.begin(),j = num.end()-1;
    while(i < j){
        if(i->num + j->num > target)
            --j;
        else if(i->num + j->num < target)
            ++i;
        else
            break;
    }
    cout << i->num << ' '<<j->num<<endl;
    return vector{i->id,j->id};
    }
};

2022.7.19

731.我的日程安排表

https://leetcode.cn/problems/my-calendar-ii/

/*
题目要求不能三重预定
所以每次调用函数时
先判断和二重预定中是否重复 有重复就会出现三重预定
没有重复在判断和一重预定是否有重复
有则将其视为二重预定 添加至二重预定集合里面

另外一种解决方案如下
采用差分数组
起始点 对应值 +1 
终点 对应值 -1
然后遍历从起点到终点的区域
将所有值相加
如果大于2 则说明 出现了三重预定 将起点和终点值复原

线段树
*/
class MyCalendarTwo {
vector<pair<int,int>> one;//一重预定
vector<pair<int,int>> two;//二重预定
public:
    MyCalendarTwo() {
    }
    //注意不能使用map 可能会出现第一个关键字重复 最终导致出现问题
    bool book(int start, int end) {
        for(const auto &[x,y]:two){
            if(start<y&&end>x) return false;//与二重预定有重合,直接返回false;
        }
        for(const auto &[x,y]:one){
            if(start<y&&end>x){
                two.push_back({max(x,start),min(y,end)});//与一重预定有重合,将重合部分放入二重预定里面
            }
        }
        one.push_back({start,end});
        return true;
    }
};

/**
 * Your MyCalendarTwo object will be instantiated and called as such:
 * MyCalendarTwo* obj = new MyCalendarTwo();
 * bool param_1 = obj->book(start,end);
 */

2.两数相加

https://leetcode.cn/problems/add-two-numbers/

/**
 * 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* result = new ListNode();
        ListNode* p1 = l1,*p2 = l2,*p = result;
        int a1,a2,add = 0,re;
        while(p1 != nullptr || p2 != nullptr){
            if(p1 == nullptr)
                a1 = 0;
            else{
                a1 = p1->val;
                p1 = p1->next;
            }
            if(p2 == nullptr)
                a2 = 0;
            else{
                a2 = p2->val;
                p2 = p2->next;
            }
            re = a1 + a2 + add;
            add = re/10;
            p->next = new ListNode(re%10);
            p = p->next;
        }
        if(add != 0)
            p->next = new ListNode(add);
        return result->next; 
    }
};

2022.7.20

1260.二维数组迁移

https://leetcode.cn/problems/shift-2d-grid/

/*
此题有三种解法
一种是将二维变成一维 然后随便用什么模拟一下就行了 
一种是直接模拟 直接模拟分为一步一步来 和 一口气解决
一步一步来 复杂度为o(n*m*k)
还有一种就是像下面这种代码一样 斗转星移 颠倒乾坤 注:该代码稍作修改即可用于一维数组的迁移
时间复杂度为o(n*m);
*/
class Solution {
public:
    int n, m;

    void reverse(vector<vector<int>>& grid, int start, int end) {
        for (int i = start, j = end - 1; i < j; i ++, j -- )
            swap(grid[i / m][i % m], grid[j / m][j % m]);
    }

    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        n = grid.size(), m = grid[0].size();
        k %= n * m;
        reverse(grid, 0, n * m);
        reverse(grid, 0, k);
        reverse(grid, k, n * m);
        return grid;
    }
};

3.无重复字符的最长子串

https://leetcode.cn/problems/longest-substring-without-repeating-characters/

/*基本上是我想的思路 可惜我只想到用Map进行模拟了 可是map默认值为0 会与数组的下标冲突
很苦恼 没解决 就之前寻找算了
这是别人写的代码 思路是一致的
此题就是滑动窗口*/
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int maxx=0,start = -1;
        int pos[128];
     //   for(int j=0;j<128;j++) pos[j] = -1;
        memset(pos,-1,sizeof(pos));
        for(int i=0;i<s.size();i++){
            start = max(pos[(int)s[i]], start);
            maxx = max(maxx, i-start);
            pos[(int)s[i]] = i;
        }
        return maxx;
    }
};
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int maxLength = 0,length = 0;
        for(int i = 0; i < s.length();++i){
            int j;
            for(j = i-1; j >=i-length ;--j){
                if(s[j] == s[i])
                    break;
            }
           // cout <<s[i]<<' '<< i << ' '<<j<<' '<<length<<' '<<maxLength<< endl;
            if(j != -1){
                maxLength = max(maxLength,length);
                length = i - j;
            }else{
                ++length;
            }
        } 
       // cout << length << ' '<<maxLength<<endl;
        maxLength = max(maxLength,length);
        return maxLength;
    }
};

2022.7.21

4.寻找两个正序数组的中位数

https://leetcode.cn/problems/median-of-two-sorted-arrays/

/*深受震撼的一题 剩下两种方法甚至还不是简单的改进 不得不佩服*/
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {//为了防止越界
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int m = nums1.size();
        int n = nums2.size();
        int left = 0, right = m;//用于在第二个数组中寻找对应的位置
        int median1 = 0, median2 = 0;
//数组奇数与偶数分别记录
        while (left <= right) {//在第一个数组中枚举i 通过计算得到j的位置
            int i = (left + right) / 2;
            int j = (m + n + 1) / 2 - i;//利用的是中位数的定义 是两个数组中第 (m+n+1)/2大的那个 或者说是第(m+n+1)/2小的那个 将中位数变成了第k小的数 很神奇
            //i和j代表的含义是第一个数组从0到i-1 第二个数组从0到j-1 划分到一块 剩下的元素再划分为一块 两块元素和为m+n 而中间的数即为中位数 因为是按照大小排序的 虽然前面的元素之间的相关次序未知 但是在交界处的已知 
			//简化之后的判断
            int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]);
            int nums_i = (i == m ? INT_MAX : nums1[i]);
            int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]);
            int nums_j = (j == n ? INT_MAX : nums2[j]);
//此时由于i是随机选取的 因此被其决定的j在第二个数组中值可能过小 导致并不满足顺序的要求 因此就需要减小i的大小 借以增大j的大小 而数组又是有序的 因此j所对应的元素的大小也就增大 而这可以借助于二分查找实现 又由于之前交换了数组的大小 确保了 小数组在前面 因此j不会越界 所以必然能在 log(m)的复杂度下找到可以正确划分的元素i,但是可能又不止一个 于是会继续寻找 并且不断记录 最后一次找到的就是结果 而m = min(m,n) 所以这个算法的复杂度为奇迹般的log(min(m,n));(lll¬ω¬) 此题有两点最为要命 第一点是 将中位数转化为 第k小的数 第二点是 将集合划分作为关键 并且巧妙地结合了二分查找 避免无用枚举 (lll¬ω¬)
            if (nums_im1 <= nums_j) {
                median1 = max(nums_im1, nums_jm1);
                median2 = min(nums_i, nums_j);
                left = i + 1;
            } else {
                right = i - 1;
            }
        }
        return (m + n) % 2 ? median1 : (median1 + median2) / 2.0;//分奇偶
    }
};
//第二种稍微正常一点的思路 ┭┮﹏┭┮ 其实就是不断去掉无用的数字 整体上是查找第k小的数的翻版 可以去掉前面 n个数 也就变成了求解 第 k-n 小的数 当时我也想到了这块 但是确实没想到中位数怎么传递 因为需要不断的去掉一些无用数字 如果从两头去掉 很麻烦
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
            return median;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
         * 这里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
         * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
         * 这样 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
         * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
         */

        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        int kthElement = 0;

        while (true) {
            // 边界情况
            if (index1 == length1) {
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return Math.min(nums1[index1], nums2[index2]);
            }
            
            // 正常情况
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}
/*
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/

814.二叉树剪枝

https://leetcode.cn/problems/binary-tree-pruning/

/**
 * 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:
    TreeNode* pruneTree(TreeNode* root) {
        if(!root)//空指针
            return nullptr;
        root->left = pruneTree(root->left);
        root->right = pruneTree(root->right);
        if(root->val == 0 && !root->left && !root->right)//如果子不包含 且值为0 剪掉 必须放到这里
            return nullptr;
        return root;
    }
};

2022.7.22

757.设置交集大小至少为2

https://leetcode.cn/problems/set-intersection-size-at-least-two/

//本题关键点有三个 第一个是想到贪心 第二个是想到排序 第三个是想到怎么排序
/*
最难的是怎么排序 按照正常的习惯 是左递增 右递减 如此
此种方式未尝不可 但是总归有些复杂
最佳方式即右递增 左递减 这样最能服务于算法
*/
class Solution {
public:
    bool static cmp(vector<int> &a,vector<int> &b){
        if(a[1]!=b[1])
            return a[1] < b[1];
        return a[0] > b[0];
    }
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);//右边界递增 左边界递减
        int c = 2,l=intervals[0][1]-1,r=intervals[0][1];//集合大小最少为2 默认选择第一个的最后两个元素 这样对于所有右边界相同的 都可做到题目的要求
        vector<int> v;
        v.push_back(l);
        v.push_back(r);
        for(int i = 1; i < intervals.size();++i){
            if(r < intervals[i][0]){//如果是 [1,2] [4,5]之前选择的是1,2 此时需要选择4,5结果需要加2调换位置 需注意 选择的应该是右边界和前面的那个元素 [1,2] [8,10]即选择9,10而非 8,10。
                c+=2;
                r = intervals[i][1];
                l = intervals[i][1]-1;
                v.push_back(r);
                v.push_back(l);
            }else if(l < intervals[i][0] && intervals[i][0] <= r){//如果左边界在当前范围内
                if(r < intervals[i][1]){//如果没有真包含该区域 即[4,5] [5,7] 这说明已经到下一个范围了 因为排序是按递增的顺序进行的排序 对于下一个区间 我已经包含了r 这个元素 只需要再包含一个元素 就可以满足至少包含两个元素的条件了 又由于是按照右边界排序的 包含有边界必然是优解 故此时多包含了一个元素
                    l = r;
                    r = intervals[i][1];
                    v.push_back(r);
                }else if(r == intervals[i][1]){//如果是真包含 此时为了使用更少的元素就包含这个区间 最好的是直接包含这个区间结尾前面的那个元素 所以他需要加1
                    l = intervals[i][1]-1;
                    v.push_back(l);
                }
                ++c;
            }
        }
        return c;
        //return v.size(); v 即最后的结果集合 
    }
};
  1. 300.最长递增子序列
  2. 674.最长连续递增序列
  3. 718.最长重复子数组
  4. 1143.最长公共子序列
  5. 1035.不相交的线
  6. 53.最大子序和
  7. 392.判断子序列
  8. 115.不同的子序列
  9. 583.两个字符串的删除操作
  10. 72.编辑距离
  11. 为了绝杀编辑距离,我做了三步铺垫,你都知道么?
  12. 647.回文子串
  13. 516.最长回文子序列
  14. 动态规划总结篇

-----------

5.最长回文子串

https://leetcode.cn/problems/longest-palindromic-substring/

//动规版本 O(n2) O(n2)
class Solution {
public:
    string longestPalindrome(string s) {
        const int n = s.length();
        int dp[n+1][n+1];
        int left = 0,maxLength = 0;
        memset(dp,0,sizeof(dp));
        for(int i = n; i >= 0;--i){//i和j顺序不要乱
            for(int j = i; j < n;++j){
                if(s[i]==s[j]){
                    if(j-i <= 1){
                        dp[i][j] = 1;
                    }else if(dp[i+1][j-1])
                        dp[i][j] = 1;
                }
                if(dp[i][j]&&j-i+1>maxLength){
                    maxLength = j - i + 1;
                    left = i;
                }
            }
        }
        return s.substr(left,maxLength);
    }
};
//双指针版本 o(n2) o(1)
class Solution {
public:
    int left,maxLength;
    string longestPalindrome(string s) {
        for(int i = 0; i < s.length();++i){
            extend(i,i,s,s.length());
            extend(i,i+1,s,s.length());
        }
        return s.substr(left,maxLength);
    }
    void extend(int i,int j,const string & s,int n){
        while(i >= 0 && j < n && s[i] == s[j]){
            if(j-i+1>maxLength){
                maxLength =j-i+1;
                left = i;
            }
            ++j;
            --i;
        }
    }
};
//暴力版本 o(n3) o(1)
class Solution {
public:

    bool isPal(string s){
        string ss(s);
        reverse(s.begin(),s.end());
        return ss == s;
    }
    string longestPalindrome(string s) {
        int n = s.length();
        for(int i = n; i >= 1;--i){
            for(int j = 0; j + i <= n;++j){
                if(isPal(s.substr(j,i)))
                    return s.substr(j,i);
            }
        }
        return s;
    }
};

2022.7.23

6.Z字形变换

https://leetcode.cn/problems/zigzag-conversion/

/*这题告诫我 不要被题目带偏 要能认识到本质
认识到本质 再去做处理 一下两种方法皆是如此
题目中虽然说是Z字型变换 但是不妨将中间的折线变成直线 于是就变成了一种上上下下的循环了
之后分别计算每一层 就可以解决这个问题了*/
class Solution {
public:
    string convert(string s, int numRows) {
        if(s.length()<=1 || numRows==1) return s;
        vector<string> s0(numRows);
        int m=0;
        while(m<s.length()){
            for(int i=0;i<numRows;i++)
                if(m<s.length())
                    s0[i]+=s[m++];
            for(int i=numRows-2;i>0;i--)
                if(m<s.length())
                    s0[i]+=s[m++];
        }
        string res;
        for(int i=0;i<numRows;i++)
            res+=s0[i];
        return res;
    }
};
class Solution {
public:
    string convert(string s, int numRows) {
        string ss;
        if(s.empty()||numRows<1)
            return ss;
        if(numRows == 1)
            return s;
        vector<string> temp(numRows);
        int r,c;
        for(int i = 0; i < s.size();++i){
            r = i / (numRows-1);
            c = i % (numRows-1);
            if(r&1){
                temp[numRows-1-c].push_back(s[i]);
            }else{
                temp[c].push_back(s[i]);
            }
        }
        for(int i = 0; i < temp.size();++i){
            ss+=temp[i];
        }
        return ss;
    }
};

剑指offer2 115.重建序列

https://leetcode.cn/problems/ur2n8P/

class Solution {//拓扑排序而已 看破本质就好了
public:
    bool sequenceReconstruction(vector<int>& nums, vector<vector<int>>& sequences) {
        int n = nums.size(),maxn = 0;//需要找到的是最短的那个 也就是说 应该是刚好相等
        vector<int> v(n+1),visit(n+1);//一个用于标记前驱结点 一个用于找到末端结点
        for(auto & a:sequences){
            for(int i = a.size()-1;i>=1;--i){
                int r = a[i];
                while(r!=a[i-1]&&r!=0){//不是直接父节点 如1,2,3 1,2 2,3 1,3 如果直接覆盖的话就会错判
                    r = v[r];
                }
                if(r == 0)
                    v[a[i]] = a[i-1];//标记前驱结点
                visit[a[i-1]] = 1;//标记访问
                maxn = max(a[i],maxn);//寻找最大的数
                maxn = max(a[i-1],maxn);
            }
        }
        if(maxn > n)//如果n都不够大 那明显无法构成
            return false;
        // for(int x:v)
        //     cout << x <<' ';
        // cout << endl;
        int root;//寻找末端结点 末端结点没有人访问过
        for(root = 1; root <= n;++root){
            if(!visit[root])
                break;
        }
        if(root == n+1)//如果找遍了都找不到 那么返回false
            return false;
        int c = 0;//寻找这条线上点数量 如果是能够还原的 那么应该刚好是n个
        while(root!=0){
            root = v[root];
            ++c;
        }
        if(c!=n)
            return false;
        return true;
    }
};

300.最长递增子序列

https://leetcode.cn/problems/longest-continuous-increasing-subsequence/

/*利用容器的二分 4ms 需要注意 二分查找的不是刚好等于 而是第一个大于等于的数
就拿这种方法来说吧
下面自己写的和这个思路是一致的
lis表用于维护数据 其本身是有序的
遍历数组 将当前值插入到一个合适的位置
也就是刚好大于等于这个值的位置
如果找到的是末位数
那么说明这个数比之前的都大 因此序列增长1
如果找到了一个适合的位置 因此就可以取缔一个比他大的数 
这其实是一种贪心策略 只要最后的序列每个数尽可能小 能放下的数也就尽可能多
*/
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> lis;
        for(int num: nums) {
            auto it = lower_bound(lis.begin(), lis.end(), num);
            if(it == lis.end()) lis.push_back(num);
            else {
                *it = num;
            }
        }
        return lis.size();
    }
};
/*自己写的二分 4ms*/
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> tail(nums.size());
        int res = 0;
        for (int num : nums){
            int i = 0, j = res;
            while (i < j){
                int mid = i + (j - i) / 2;
                if (num <= tail[mid]){
                    j = mid;
                }else{
                    i = mid + 1;
                }   
            }
            tail[i] = num;
            if (res == j) res++;
        }
        return res;
    }
};
/*动规穷举 264ms
思路如下
dp i 的定义是 从0到i 以nums[i]结尾的子序列最长值
由于一个数字即可看作一个序列 因此初始化为1
如果在[0,i]范围内 有一j小于i 那么子序列的最长值可据此增长1 或者 不变 因此是取的最大值
这样的确是对的 dp j表示的是以num[j]结尾的序列最长值 子序列不一定连续
故而初始化时需要从小到大
*/
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size() <= 1)
            return 1;
        vector<int> dp(nums.size(),1);
        int result = 1;
        for(int i = 0; i < nums.size();++i){
            for(int j = 0;j < i;++j){
                if(nums[j] < nums[i]){
                    dp[i] = max(dp[i],dp[j]+1);
                }
            }
            result = max(result,dp[i]);
        }
        return result;
    }
};

2022.7.24

7.整数反转

https://leetcode.cn/problems/reverse-integer/

class Solution {
public:
    int reverse(int x) {
        if(x < 0){
            if(x == INT_MIN)//负数不能直接转为正数 因为最小的负数取反比最大的正数大1 会导致整数没有能转换的
                return 0;
            return -reverse(-x);
        }
        int result = 0;
        while(x){
            if(result > INT_MAX /10)//如果直接判断result是否大于了INT_MAX 那么可能在这之前result就越界了 会报错
                return 0;
            result = result * 10 + x%10;
            x /= 10;
        }
        return result;
    }
};

1184.公交站间的距离

https://leetcode.cn/problems/distance-between-bus-stops/

//一开始还以为是单源最短路 吓死我了
class Solution {
public:
    int distanceBetweenBusStops(vector<int>& distance, int start, int destination) {
        int asc = 0,desc = 0;
        int i = start,j = destination;
        int n = distance.size();
        while(i != destination){
            asc += distance[i];
            i = (i+1)%n;
        }
        while(j != start){
            desc += distance[j];
            j = (j+1)%n;
        }
        return min(asc,desc);
    }
};

8.字符串转换整数

https://leetcode.cn/problems/string-to-integer-atoi/

//关键点仍然在于INT_MAX INT_MIN 情况的判断 注意此时就需要去判断下一位数字是大于7还是小于了 因为字符串是可以做到原数字就超过INT_MAX 或者INT_MIN 的
int myAtoi(char * s){
        int result = 0;
        int flag = 1;
        while(*s == ' ')
            s++;
        if(*s == '-'){
            flag = -1;
            s++;
        }else if(*s == '+'){
            flag = 1;
            s++;
        }
        while(*s >= '0' && *s <= '9'){
            if(flag == 1){
                if(result > INT_MAX / 10 || (result == INT_MAX / 10 && *s > '7'))
                    return INT_MAX;
            }else if(flag == -1){
                if(result < INT_MIN / 10 || (result == INT_MIN / 10 && *s > '8'))
                    return INT_MIN;
            }
            result = result * 10 + flag * (*s-'0');
            s++;
        }
        return result;
}

2022.7.25

42.接雨水

https://leetcode.cn/problems/trapping-rain-water/submissions/

class Solution {
public:
    int trap(vector<int>& height) {
        //动态规划版本
        if(height.size() <= 2)//小于等于两块不可能接的到雨水
            return 0;
        int count = 0;
        //分别计算当前点左右的最大值 动态规划的核心思想是预先计算好一些信息 让这些信息不必要重复进行计算
        vector<int> left(height.size());
        vector<int> right(height.size());
        int leftMax = 0,rightMax = 0;
        for(int i = 0; i < height.size(); ++i){
            left[i] = max(leftMax,height[i]);
            leftMax = left[i];
        }
        for(int j = height.size()-1; j >= 0; --j){
            right[j] = max(rightMax,height[j]);
            rightMax = right[j];
        }
        //最后计算每一块能够接住的雨水
        for(int i = 0; i < height.size(); ++i){
            count += min(right[i],left[i]) - height[i];
        }
        return count;
    }
};
class Solution {
public:
    int trap(vector<int>& height) {
        //单调栈版本
        int count = 0;
        stack<int> s;//单调栈 存放数组下标 栈底到栈顶高度递减
        for(int i = 0; i < height.size(); ++i){
            if(s.empty()){//如果栈为空 就添加该元素
                s.push(i);
            }else{//栈不为空时 就需要去进行计算
                while(height[i] > height[s.top()]){//不满足的时候不断退栈计算
                    int top = s.top();s.pop();//计算时需要拿出两个元素进行比较 为了防止栈为空时取出元素 应该在弹出栈顶元素后再对栈是否为空进行判断 毕竟如果只有一个 是无法解到雨水的
                    if(s.empty()){
                        break;
                    }
                    //计算接的雨水量 注意top s.top() i 这三个是如何在计算式中运用的 采取的思想是逐步计算 直接算高度乘长度
                    //cout << i <<' '<<top<<' '<<s.top()<<' '<<(i-s.top()-1)* (min(height[i],height[s.top()]) - height[top])<<endl;
                    count += (i-s.top()-1)* (min(height[i],height[s.top()]) - height[top]);
                }
                s.push(i);
            }
        }
        return count;
    }
};
class Solution {
public:
    int trap(vector<int>& height) {
        //双指针大法
        int count = 0;
        int left = 0,right = height.size()-1;
        int leftMax = 0,rightMax = 0;
        while(left < right){
            leftMax = max(leftMax,height[left]);
            rightMax = max(rightMax,height[right]);
            if(rightMax > leftMax){
                count += leftMax - height[left];
                ++left;
            }else{
                count += rightMax - height[right];
                --right;
            }
        }
        return count;
    }
};

2022.7.26

1206.设计跳表

https://leetcode.cn/problems/design-skiplist/submissions/

/*就说一下对跳表的看法吧 跳表的本质是二分查找加上了链表 二分查找的核心不在于数组的随机访问 而在于通过 几个标志量快速区分值所在的区域 并依次缩小无意义的查找 因而跳表采取了分级的策略 除了层级为1的位置 存储了全部元素外 其余的层级都存储的是一些标志量 用于快速找到当前元素所在的位置 这些标志变量则采用随机化的方法获取 这些元素需要额外的指向下一个元素的指针 
因此
对于跳表的查找
先从最高层级开始找起
找到刚好小于等于它的位置 如果在当前层级无法找到 则跳转到下一层 在已经缩小了区域进行寻找
之后再一步一步进行寻找
直至找到 或者未找到
对于跳表的删除
可以先进行跳表的查找 但是需要额外记录所有指向 需要删除元素的指针 将这些指针都进行删除
防止出现自己删了 那些指针直接成为野指针的情况
对于跳表的插入
其实可以认为是找到一个好位置 进行插入
这个寻找的操作 和查找是类似的
为了防止有多个重复值的元素插入 寻找时应该是找严格大于它的第一个元素 再进行插入
此时就需要去判断他是否要作为标志元素 
如果是 则需要让其添加后续 并将其添加到之前层级的元素中去*/
#ifndef _SKIPLIST_H__
#define _SKIPLIST_H__
#include<time.h>
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define MAXLEVE 8 //跳表的最大层数
template<typename K,typename V>struct SkipNode //跳表的节点类型
{   
    K _key; 
 	V _value;  
    size_t _sz;//表示该节点的层数
    vector<SkipNode<K,V> *> _pleve;//存放每一层的指针   
    SkipNode(K key=K(),V value=V(),size_t sz=size_t()):_key(key),_value(value),_sz(sz)
    {   					
        _pleve.resize(0);        
        for(size_t i=0;i<sz;i++)
        {            
            _pleve.push_back(NULL);        
        }    
	}  
	~SkipNode(){ _key=-1;_value=-1; _sz=-1;_pleve.clear();}
};
template<typename K,typename V>class SkipList                       //跳表类
{
public:
    typedef SkipNode<K,V> Node;    SkipList();
    void Insert(K key,V value);
    bool Find(K key,V& value);    
    bool Erase(K key);    
    void Print();    
    int GetLeve();//返回跳表的最大层数    
    size_t Size();    
    ~SkipList();
    private:    
    int Random();//产生随机层数的函数
    protected:
    SkipList(SkipList<K,V> &);
    //防拷贝    
    SkipList<K,V>& operator=(SkipList<K,V>);    
    //防赋值
    private:    
    Node *_head;
    int _maxLeve;
    //记录跳表的最大层数    
    int _size;//记录跳表最底层元素的个数
};
//别人的C代码
#define MAX(a, b) ((a) > (b) ? (a) : (b))
const int MAX_LEVEL = 32;
const int P_FACTOR = RAND_MAX >> 2;

typedef struct SkiplistNode {
    int val;
    int maxLevel;
    struct SkiplistNode **forward;
} SkiplistNode;

typedef struct {
    SkiplistNode *head;
    int level;
} Skiplist;

SkiplistNode *skiplistNodeCreat(int val, int maxLevel) {
    SkiplistNode *obj = (SkiplistNode *)malloc(sizeof(SkiplistNode));
    obj->val = val;
    obj->maxLevel = maxLevel;
    obj->forward = (SkiplistNode **)malloc(sizeof(SkiplistNode *) * maxLevel);
    for (int i = 0; i < maxLevel; i++) {
        obj->forward[i] = NULL;
    }
    return obj;
}

void skiplistNodeFree(SkiplistNode* obj) {
    if (obj->forward) {
        free(obj->forward);
        obj->forward = NULL;
        obj->maxLevel = 0;
    }
    free(obj);
}

Skiplist* skiplistCreate() {
    Skiplist *obj = (Skiplist *)malloc(sizeof(Skiplist));
    obj->head = skiplistNodeCreat(-1, MAX_LEVEL);
    obj->level = 0;
    srand(time(NULL));
    return obj;
}

static inline int randomLevel() {
    int lv = 1;
    /* 随机生成 lv */
    while (rand() < P_FACTOR && lv < MAX_LEVEL) {
        lv++;
    }
    return lv;
}

bool skiplistSearch(Skiplist* obj, int target) {
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 target 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < target) {
            curr = curr->forward[i];
        }
    }
    curr = curr->forward[0];
    /* 检测当前元素的值是否等于 target */
    if (curr && curr->val == target) {
        return true;
    } 
    return false;
}

void skiplistAdd(Skiplist* obj, int num) {
    SkiplistNode *update[MAX_LEVEL];
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 num 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < num) {
            curr = curr->forward[i];
        }
        update[i] = curr;
    }
    int lv = randomLevel();
    if (lv > obj->level) {
        for (int i = obj->level; i < lv; i++) {
            update[i] = obj->head;
        }
        obj->level = lv;
    }
    SkiplistNode *newNode = skiplistNodeCreat(num, lv);
    for (int i = 0; i < lv; i++) {
        /* 对第 i 层的状态进行更新,将当前元素的 forward 指向新的节点 */
        newNode->forward[i] = update[i]->forward[i];
        update[i]->forward[i] = newNode;
    }
}

bool skiplistErase(Skiplist* obj, int num) {
    SkiplistNode *update[MAX_LEVEL];
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 num 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < num) {
            curr = curr->forward[i];
        }
        update[i] = curr;
    }
    curr = curr->forward[0];
    /* 如果值不存在则返回 false */
    if (!curr || curr->val != num) {
        return false;
    }
    for (int i = 0; i < obj->level; i++) {
        if (update[i]->forward[i] != curr) {
            break;
        } 
        /* 对第 i 层的状态进行更新,将 forward 指向被删除节点的下一跳 */
        update[i]->forward[i] = curr->forward[i];
    }
    skiplistNodeFree(curr);
    /* 更新当前的 level */
    while (obj->level > 1 && obj->head->forward[obj->level - 1] == NULL) {
        obj->level--;
    }
    return true;
}

void skiplistFree(Skiplist* obj) {
    for (SkiplistNode * curr = obj->head; curr; ) {
        SkiplistNode *prev = curr;
        curr = curr->forward[0];
        skiplistNodeFree(prev);
    }
    free(obj);
}
// 1206. 设计跳表
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define bool int
#define TRUE 1
#define FALSE 0

#define SKIPLIST_MAXLEVEL 32      //跳表最大层级  

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
//跳表单个结点数据结构
typedef struct SkiplistNode {
    int value;                      // 值
    struct SkiplistLevel {
        struct SkiplistNode *next;
    } level[];                      // 跳表每个节点有N层索引, 柔性数组
} SkiplistNode;
//跳表数据结构
typedef struct Skiplist {
    struct SkiplistNode *head;      // 表头节点
    int length;                     // 记录跳跃表长度
    int level;                      // 记录跳跃表当前层数
} Skiplist;


// 按照指定层级和value 创建一个跳表结点
SkiplistNode* skiplistNodeCreate(int level, int value)
{
    SkiplistNode *p = (SkiplistNode *)malloc(sizeof(*p) + sizeof(struct SkiplistLevel) * level);
    p->value = value;
    return p;
}

// 跳表的创建
Skiplist* skiplistCreate() {
    Skiplist *sl = (Skiplist *)malloc(sizeof(*sl));
    sl->length = 0;
    sl->level = 1;
    sl->head = skiplistNodeCreate(SKIPLIST_MAXLEVEL, INT_MIN);
    for (int i = 0; i < SKIPLIST_MAXLEVEL; ++i) {
        sl->head->level[i].next = NULL;
    }
    return sl;
}

// 跳表的查找, O(logN)
bool skiplistSearch(Skiplist* obj, int target) {
    SkiplistNode *p = obj->head;
    int levelIdx = obj->level - 1;
    
    for (int i = levelIdx; i >= 0; --i) {
        // 如果第i层节点值小于target, 沿当前层继续查找
        while (p->level[i].next && p->level[i].next->value < target) {
            p = p->level[i].next;
        }
        // 第i层未找到该节点, 或者节点值已大于target, 沿着下一层查找
        if (p->level[i].next == NULL || p->level[i].next->value > target) {
            continue;
        }
        return TRUE;
    }
    return FALSE;
}

// P(层数为n) = (1/2) ^ n
int GetSkipNodeRandomLevel()
{
    int level = 1;
    while (rand() & 0x1) {
        ++level;
    }
    return min(level,SKIPLIST_MAXLEVEL); 
}

// 跳表的插入 O(logN)
// 要点: 记忆待插入节点的所有前继节点的值,并更新最大层数
void skiplistAdd(Skiplist* obj, int num) {
    SkiplistNode *p = obj->head;
    int levelIdx = obj->level - 1;
    struct SkiplistNode *preNodes[SKIPLIST_MAXLEVEL]; // 记忆待插入节点的所有前继节点的值
    for (int i = obj->level; i < SKIPLIST_MAXLEVEL; ++i) {
        preNodes[i] = obj->head;
    }

    // 记忆待插入节点的所有前继节点的值,便于后续插入操作
    for (int i = levelIdx; i >= 0; --i) {
        // 如果第i层节点值小于target, 沿当前层继续查找插入的位置
        while( p->level[i].next && p->level[i].next->value < num) {
            p = p->level[i].next;
        }
        preNodes[i] = p;
    }

    int newLevel = GetSkipNodeRandomLevel();
    struct SkiplistNode *newNode = skiplistNodeCreate(newLevel, num);
    for (int i = 0; i < newLevel; ++i) {
        newNode->level[i].next = preNodes[i]->level[i].next;
        preNodes[i]->level[i].next = newNode;
    }
    obj->level = max(obj->level, newLevel);         // 注意更新跳跃表当前层数
    ++obj->length;
}

void skiplistNodeDelete(Skiplist *obj, SkiplistNode *cur, SkiplistNode **preNodes)
{
    for (int i = 0; i < obj->level; ++i) {
        if (preNodes[i]->level[i].next == cur) { // 只有相等,才能从当前层的链表中删除节点!
            preNodes[i]->level[i].next = cur->level[i].next;
        }
    }
    // 如果删除的节点是层数最大的,那么可能需要更新跳表长度
    for (int i = obj->level - 1; i >= 1; --i) {
        if (obj->head->level[i].next != NULL) {
            break;
        }
        --obj->level;
    }
    --obj->length;
    // 释放被删除节点空间
    free(cur);
}

// 跳跃表删除操作 O(logN)
// 要点: 找到待删除节点的前一个节点, 注意更新最大层数
bool skiplistErase(Skiplist* obj, int num) {
    SkiplistNode *p = obj->head;
    int levelIdx = obj->level - 1;
    struct SkiplistNode *preNodes[SKIPLIST_MAXLEVEL]; // 记忆待删除节点的所有前继节点的值
    for (int i = levelIdx; i >= 0; --i) {
        // 如果第i层节点值小于num, 沿当前层继续查找
        while (p->level[i].next && p->level[i].next->value < num) {
            p = p->level[i].next;
        }
        preNodes[i] = p;
    }

    p = p->level[0].next;
    if (p && p->value == num) {
        skiplistNodeDelete(obj, p, preNodes);
        return TRUE;
    }
    return FALSE;
}

void skiplistFree(Skiplist* obj) {
    SkiplistNode *cur = obj->head->level[0].next;
    SkiplistNode *d;
    while(cur) {
        d = cur;
        cur = cur->level[0].next;
        free(d);
    }
    free(obj->head);   
    free(obj);
}

/**
 * Your Skiplist struct will be instantiated and called as such:
 * Skiplist* obj = skiplistCreate();
 * bool param_1 = skiplistSearch(obj, target);
 
 * skiplistAdd(obj, num);
 
 * bool param_3 = skiplistErase(obj, num);
 
 * skiplistFree(obj);
*/
struct Node
{
     //向右向下足矣
    Node* right;
    Node* down;  
    int val;
    Node(Node *right, Node *down, int val) : right(right), down(down), val(val) {}
};

class Skiplist {
private:
    Node *head;
    // 预先分配后能到 104ms (94%)
    const static int MAX_LEVEL = 32;
    // 查找时临时使用的变量
    vector<Node*> pathList;
public:
    Skiplist() {
        head = new Node(NULL, NULL, -1);  //初始化头结点
        pathList.reserve(MAX_LEVEL);
    }
    
    bool search(int target)
    {
        Node *p = head;
        while(p)
        {
            // 左右寻找目标区间
            while (p->right && p->right->val < target) {
                p = p->right;
            }
            
            // 没找到目标值,则继续往下走
            if (!p->right || target < p->right->val) {
                p = p->down;
            } else {   
                //找到目标值,结束
                return true;
            }
        }
        return false;
    }
    
    void add(int num) {
        // cout << "add " << num << endl;
         //从上至下记录搜索路径
        pathList.clear();
        Node *p = head;
        // 从上到下去搜索 次小于num的 数字,等于就是另外一种实现里的 prevs
        while(p)
        {
            // 向右找到次小于num的p
            while (p->right && p->right->val < num)
            { 
                p = p->right;
            }
            pathList.push_back(p);
            p = p->down;
        }

        bool  insertUp = true;
        Node* downNode = NULL;
        // 从下至上搜索路径回溯,50%概率
        // 这里实现是会保证不会超过当前的层数的,然后靠头结点去额外加层, 即每次新增一层
        while (insertUp && pathList.size() > 0)
        {
            Node *insert = pathList.back();
            pathList.pop_back();
            // add新结点
            insert->right = new Node(insert->right,downNode,num); 
            // 把新结点赋值为downNode
            downNode = insert->right; 
            // 50%概率   
            insertUp = (rand()&1)==0;
            // cout << " while new node " << num << " insertUp " << insertUp << endl;
        }
        // 插入新的头结点,加层
        if(insertUp)
        {  
            // cout << " insertUp new node " << num << endl;
            head = new Node(new Node(NULL,downNode,num), head, -1);
        }
    }
    
    bool erase(int num) {
        Node *p = head;
        bool seen = false;
        while (p)
        {
            // 当前层向右找到次小的点
            while (p->right && p->right->val < num)
            {
                p = p->right;
            }
            // 无效点,或者 太大, 则到下一层去
            if (!p->right || p->right->val > num)
            {  
                p = p->down;
            }
            else
            {
                // 搜索到目标结点,进行删除操作,结果记录为true
                // 继续往下层搜索,删除一组目标结点
                seen = true;
                p->right = p->right->right;
                p = p->down;
            }
        }
        return seen;
    }
};
struct SkipNode {
    int m_value = 0;
    vector<SkipNode *> m_levelNextNode; // 节点在不同层的下一个节点

    SkipNode(int value, int size): m_value(value), m_levelNextNode(size, NULL) {}
};

class Skiplist {
public:
    Skiplist() {
        // 创建虚拟头结点
        m_skipHead = new SkipNode(0, m_maxLevel);
    }

    ~Skiplist() {
        delete(m_skipHead); m_skipHead = NULL;
    }
    
    void add(int num) {
        int newNodeLevel = randomLevel();
        m_level = max(m_level, newNodeLevel);

        // 这里新节点 levelNextNode数组个数为 newNodeLevel即可, 因为节点的层数不会变了, 数组没必要申请 m_maxLevel个
        SkipNode* newNode = new SkipNode(num, newNodeLevel);

        // 插入节点
        SkipNode* findNode = m_skipHead;    // 从虚拟节点开始查找(每层都有)
        for (int level=m_level-1; level>=0; --level) {
            findNode = findLevelClosest(findNode, level, num);

            // ** 只处理节点生成的level层, 的节点关系 **
            if (level < newNodeLevel) {
                newNode->m_levelNextNode[level] = findNode->m_levelNextNode[level];    // 添加当前节点的 下一个节点
                findNode->m_levelNextNode[level] = newNode;    // 前置节点的 下一个节点 指向 当前节点
            }
        }

        // 增加层数 初始节点都是虚拟头节点
        m_length++;
        return ;
    }
    
    bool search(int target) {
        SkipNode* findNode = m_skipHead;    // 从虚拟节点开始查找(每层都有, 第一个虚拟节点的值无意义)
        for (int level=m_level-1; level>=0; --level) {
            findNode = findLevelClosest(findNode, level, target);
            if (findNode->m_levelNextNode[level] && findNode->m_levelNextNode[level]->m_value == target) {
                return true;
            }
        }

        return false;
    }

    bool erase(int num) {
        SkipNode* deleteNode = NULL;
        SkipNode* findNode = m_skipHead;

        for (int level=m_level-1; level>=0; --level) {
            findNode = findLevelClosest(findNode, level, num);

            if (findNode->m_levelNextNode[level] && findNode->m_levelNextNode[level]->m_value == num) {
                deleteNode = findNode->m_levelNextNode[level];  // 记录删除节点(是findNode的下一个节点)
                findNode->m_levelNextNode[level] = findNode->m_levelNextNode[level]->m_levelNextNode[level];
            }
        }

        if (deleteNode) {
            m_length--;
            while (m_level > 1 && m_skipHead->m_levelNextNode[m_level-1] == NULL) {
                m_level--;
            }

            delete deleteNode; deleteNode = NULL;
            return true;
        }

        return false;
    }

    // 辅助函数
    inline SkipNode* findLevelClosest(SkipNode* node, int level, int value) {
        while (node->m_levelNextNode[level] && node->m_levelNextNode[level]->m_value < value){
            node = node->m_levelNextNode[level];
        }
        return node;
    }

    // 生成随机数 确定节点的level
    int randomLevel() {
        int level = 1;
        while ((random() % 100) < m_percent && level < m_maxLevel){
            level++ ;
        }
        return level;
    }

    void debug() {
        printf("skiplist debug, length:%d, level:%d\n", m_length, m_level);
        for (int level=m_level-1; level>=0; --level) {
            int size = 0;
            string levelDebug;

            SkipNode* levelNode = m_skipHead->m_levelNextNode[level];
            while (levelNode) {
                size++;
                levelDebug += std::to_string(levelNode->m_value) + " ";

                levelNode = levelNode->m_levelNextNode[level];
            }
            printf("level:%d size:%d\n%s\n", level, size, levelDebug.c_str());
        }
        printf("skiplist debug end\n\n");
        return ;
    }

private:
    SkipNode *m_skipHead = NULL;

    int m_length = 0;
    int m_level = 0;

    static const int m_percent = 25;  // 1/4概率
    static const int m_maxLevel = 64;	// 最多64层, 足以支持2^64个节点
};

2022.7.27

295.数据流的中位数

class MedianFinder {
public:
    multiset<int> s;
    multiset<int>::iterator left, right;
    MedianFinder() {

    }
    
    void addNum(int num) {
        const int n = s.size();

        s.insert(num);
        if(!n){//一个元素都没有的情况下 就选择新插入的元素
            left = right = s.begin();
        }else{
            if(n & 1){//原先是奇数 两个指针指向同一个元素
                if(num < *left){
                    --left;
                }else{
                    ++right;
                }
            }else{//原先是偶数 两个指针指向不同的两个元素
                if(num > *left && num < *right){//刚好插入到中间 左右都向中间收缩
                    ++left;
                    --right;
                }else if(num >= *right){//插入到比较大的右半部分 左指针右移
                    ++left;
                }else{//num <= * left 插入到左半部分 右指针左移 让left等于到right
                    --right;
                    left = right;//防止插入时 num 刚好等于 *left num插入到left的右边 所以需要将left直接移动到right处
                }
            }
        }
    }
    
    double findMedian() {
        return (*left+*right)/2.0;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */
class MedianFinder {
public:
    priority_queue<int> minQueue;//小顶堆
    priority_queue<int,vector<int>,greater<int> > maxQueue;//大顶堆
    MedianFinder() {

    }
    
    void addNum(int num) {
        if(minQueue.empty()){//小顶堆为空 则直接放小顶堆
            minQueue.push(num);
        }else{
            if(minQueue.size() == maxQueue.size()){//两个堆同样大小
                if(maxQueue.top() >= num){// 如果大顶堆所有元素都比num大 那么num就算插入大顶堆 也是堆顶元素 所以直接让小顶堆压入即可
                    minQueue.push(num);
                }else{//堆顶元素小于了 num 说明num不是最小的 则需要插入堆中 取出最小元素
                    maxQueue.push(num);
                    minQueue.push(maxQueue.top());
                    maxQueue.pop();                   
                }
            }else{//理由同上
                if(minQueue.top() <= num){
                    maxQueue.push(num);
                }else{
                    minQueue.push(num);
                    maxQueue.push(minQueue.top());
                    minQueue.pop();                    
                }
            }
        }
    }
    
    double findMedian() {//总和为奇数时 取出小顶堆 堆顶元素 偶数 取出两个并求均值
        return (minQueue.size()+maxQueue.size()) & 1 ? minQueue.top() : (minQueue.top()+maxQueue.top())/2.0;
    }
};

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

2022.7.28

1131.数组序号转换

//此题即为 套排序 ? 忘记具体名字了 有几个编程的注意事项 首先是空直接返回 其次是 用lambda表达式写比较函数 最为简单 最后是需要额外利用一个数组来存储arr的信息 用于最后的返回 不然会导致前面的判断相等出错
class Solution {
public:
    vector<int> arrayRankTransform(vector<int>& arr) {
        if(arr.empty())
            return arr;
        
        vector<int> v(arr.size());
        for(int i = 0; i < v.size(); ++i){
            v[i] = i;
        }
        sort(v.begin(),v.end(),[&](int x,int y) -> bool{return arr[x] < arr[y];});

        int k = 1;
        vector<int> a(arr);
        for(int i = 0; i < v.size(); ++i){
            if(i > 0 && arr[v[i]] != arr[v[i-1]]){
                ++k;
            }
            a[v[i]] = k;
        }
        return a;
    }
};
class Solution {
public:
    vector<int> arrayRankTransform(vector<int>& arr) {
        vector<int> b(arr);
        sort(b.begin(),b.end());//对数组进行排序
        b.erase(unique(b.begin(),b.end()), b.end());//去除掉b数组里面所有重复的元素
        for(auto& x:arr)//计算x所在的位置 并减去开头 得到最后的定位
            x = upper_bound(b.begin(),b.end(),x) - b.begin();
        return arr;
    }
};

2022.7.29

593.有效的正方形

https://leetcode.cn/problems/valid-square/submissions/

class Solution {
public:
    bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) {
        set<int> s;
        s.insert((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1]));
        s.insert((p1[0]-p3[0])*(p1[0]-p3[0])+(p1[1]-p3[1])*(p1[1]-p3[1]));
        s.insert((p1[0]-p4[0])*(p1[0]-p4[0])+(p1[1]-p4[1])*(p1[1]-p4[1]));
        s.insert((p3[0]-p2[0])*(p3[0]-p2[0])+(p3[1]-p2[1])*(p3[1]-p2[1]));
        s.insert((p4[0]-p2[0])*(p4[0]-p2[0])+(p4[1]-p2[1])*(p4[1]-p2[1]));
        s.insert((p3[0]-p4[0])*(p3[0]-p4[0])+(p3[1]-p4[1])*(p3[1]-p4[1]));
        if(s.size()!=2)//只有对角线长度 和边的长度
            return 0;
        if(*s.begin()==0)//四个点有重复的情况
            return 0;
        return 1;
    }
};
class Solution {
public:
    int distant(int x1,int y1,int x2,int y2){//计算两点之间的距离
        return (x2 - x1)*(x2 - x1) + (y2-y1)*(y2-y1);
    }
    bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) {
        vector<pair<int,int> > v{{p1[0],p1[1]},{p2[0],p2[1]},{p3[0],p3[1]},{p4[0],p4[1]}};//收录各点
        set<pair<int,int> > s;//用于去掉重复的点
        vector<int> dis(4);//计算各点之间的距离
        
        
        for(int i = 0; i < 4;++i){//去重
            if(s.count(v[i]))
                return false;
            s.insert(v[i]);
        }
        
        
        sort(v.begin(),v.end(),[&](pair<int,int> &x,pair<int,int> &y) -> 
            bool{
                if(x.first != y.first)
                    return x.first < y.first;
                return x.second > y.second;
                });
        swap(v[2],v[3]);//让点的顺序按照逆时针的方向转
        
        
        for(int i = 0; i < 4; ++i){//四边相等
            dis[i] =  distant(v[i].first,v[i].second,v[(i+1)%4].first,v[(i+1)%4].second);
        }
        for(int i = 0; i < 4; ++i){
            if(dis[i] != dis[(i+1)%4])
                return false;
        }
        
        
        if(dis[0] + dis[1] != distant(v[0].first,v[0].second,v[2].first,v[2].second))//有一角为90°
            return false;
        return true;//的平行四边形 是 正方形
    }
};

12.整数转罗马数字

class Solution {
public:
    string intToRoman(int num) {//从上位向下位计算
        //I,IV,V,IX,X,XL,L,XC,C,CD,D,CM,M
        string str[13] = {"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"};
        int strnum[13] = { 1,   4,    5,   9,    10,  40,   50,  90,  100,  400,  500, 900, 1000};

        string result = "";
        int count = 12;

        while( count >= 0){
            int p = num - strnum[count];
            if( p >= 0 ){
                num = p;
                result += str[count];
            }
            else{
                count--;
            }
        }

        return result;
    }
};
class Solution {
public:
    string intToRoman(int num) {//从下位向上位计算
        int c = 0;
        string result = "";
        string v[13] = {"I","X","C","M","IV","IX","XL","XC","CD","CM","V","L","D"};

        int x = 0;
        while(num){
            x = num%10;
            num /= 10;
            ++c;
            if(x >= 0 && x <= 3){
                for(int i = 0; i < x;++i){
                    result = v[c-1] + result;
                }
            }else if(x == 4){
                result = v[c*2+2] + result;
            }else if(x >= 5 && x <= 8){
                for(int i = 5; i < x; ++i){
                    result = v[c-1] + result;
                }
                result = v[9+c] + result;
            }else{
                result = v[c*2+3] + result;
            }
        }
        return result;
    }
};

13.罗马数字转整数

class Solution {
public:
    int romanToInt(string s) {
        map<char,int> m{{'I',1},{'V',5},{'X',10},{'L',50},{'C',100},{'D',500},{'M',1000}};
        int n = 0;
        for(int i = 0; i < s.length(); ++i){
            if(i + 1 < s.length() && m[s[i+1]] > m[s[i]]){
                n += m[s[i+1]] - m[s[i]];
                ++i;
            }else{
                n += m[s[i]];
            }
        }
        return n;
    }
};

16.最接近的三数之和

class Solution {
public://16-20ms
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int len = nums.size();
        //target小于可达到的最小值 返回最小值
        int minV = nums[0] + nums[1] + nums[2];
        if (minV > target) {
            return minV;
        } 
        //target大于可达到的最大值 返回最大值
        int maxV = nums[len - 1] + nums[len - 2] + nums[len - 3];
        if (maxV < target) {
            return maxV;
        }
        //在最大值和最小值 中选择离target近的那一个 target 处于最大值和最小值中间
        auto GenRslt = [target](int a, int b) -> int {
            return  (abs(a - target) < abs(b - target)) ? a : b;
        };
        int rslt = GenRslt(minV, maxV);

        for (int i = 0; i < len - 2; i++) {//防止越界
            if ((i > 0) && (nums[i] == nums[i - 1])) {//去掉一些已经判断过的元素
                continue;
            }
            if ((nums[i] + nums[i + 1] + nums[i + 2]) > target) {//target 小于了 可达到的最小值
                rslt = GenRslt(rslt, nums[i] + nums[i + 1] + nums[i + 2]);
                break;
            }
            if ((nums[i] + nums[len - 2] + nums[len - 1]) < target) {// 可能在 i+1,len-1 范围内 存在更接近target的值
                rslt = GenRslt(rslt, nums[i] + nums[len - 2] + nums[len - 1]);
                continue;
            }
            //在 当前范围内寻找 最接近target的值
            int j = i + 1;
            int k = len - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if ((nums[i] + nums[j] + nums[k]) == target) {//减少无用计算
                    return sum;
                }

                rslt = GenRslt(rslt, sum);
                if (sum < target) {
                    j++;
                } else {
                    k--;
                }
            }
        }

        return rslt;
    }
};
class Solution
{
public://16ms
    int threeSumClosest(vector<int> &nums, int target)
    {
        // 对nums进行排序,排序完成后内部数据从小到大排列
        sort(nums.begin(), nums.end());
        int result;
        int tmin = INT_MAX;
        for (int i = 0; i < nums.size() - 2; i++)
        {
            // 第一重循环每一个数只取相同的第一个
            // 后面的相同的数就不再寻找三数之和
            // 但是需要注意第一项之前没有数,需要做单独判断
            if (i > 0 && nums[i] == nums[i - 1])
            {
                continue;
            }
            // 我们需要在nums[i]之后寻找两数之和最接近target-nums[i]
            // 以i+1和nums.size()-1为起点
            int left = i + 1;
            int right = nums.size() - 1;
            while (left < right)
            {
                // 在一次一重循环中,三数之和的最小值会是
                // nums[i]和在其之后的连续两个数的和
                // 如果target比最小值还小,那么本次循环就不需要继续循环
                int min = nums[i] + nums[left] + nums[left + 1];
                if (target < min)
                {
                    if (tmin > abs(target - min))
                    {
                        tmin = abs(target - min);
                        result = min;
                    }
                    break;
                }
                // 在一次一重循环中,三数之和的最大值会是
                // nums[i]和在其之后的最后两个数的和
                // 如果target比最大值还大,那么本次循环就不需要继续循环
                int max = nums[i] + nums[right - 1] + nums[right];
                if (target > max)
                {
                    if (tmin > abs(target - max))
                    {
                        tmin = abs(target - max);
                        result = max;
                    }
                    break;
                }
                // 如果三数之和等于target,那么就找到了nums中最接近的三数之和
                // 如果三数之和大于target,nums[right]过大,将其下标左移可以使结果变小
                // 如果三数之和小于target,nums[left]过小,将其下标右移可以使结果变大
                int tmp = nums[i] + nums[left] + nums[right];
                if (tmp == target)
                {
                    return target;
                }
                else if (tmp > target)
                {
                    // 右指针移动过程中如果遇到重复,可以直接跳过,减少所需的循环判断次数
                    while (left < right && nums[right] == nums[right - 1])
                        right--;
                    right--;
                }
                else
                {
                    // 左指针移动过程中如果遇到重复,可以直接跳过,减少所需的循环判断次数
                    while (left < right && nums[left] == nums[left + 1])
                        left++;
                    left++;
                }
                // 判断本次循环的三数之和是否距离target更近
                if (tmin > abs(tmp - target))
                {
                    tmin = abs(tmp - target);
                    result = tmp;
                }
            }
        }
        return result;
    }
};

2022.7.30

952.按公因数计算最大组件大小

https://leetcode.cn/problems/largest-component-size-by-common-factor/

/*
此题若采用暴力枚举 复杂度为o(n^2 sqrt(m));m为数组中最大数的大小 因为每次都要计算gcd
且无优化办法 元素无法快速跳过 
若采用质数筛的方法 建立质数筛的时间为一常数 (较大常数因子)
中间遍历每个元素的时候 o(n sqrt(m))
最后遍历数组求最大值为o(n)
因此时间复杂度为o(n sqrt(m))
空间复杂度为o(n) 创建并查集数组

暴力枚举难以优化的原因就在于 每个元素并不是单纯凭借着大小 便可排除的
而是根据因子 因子联系着这些数 而所有的合数都是可以由质数相乘得到的
因此 很自然的想法
对于每个数 我们计算出所有不同的质数因子
对于这些质数因子 我们都把这个数字算上
最后再遍历数组 看看有多少种类的质数因子被计算了
*/
int prime[100001];//存储的是 下标所对应值 的 最小的质数因子

int init = []() {
    prime[1] = 1;
    for(int i = 2; i <= 100000; i++) {
        if(prime[i] != 0) {
            continue;
        }
        for(int j = i; j <= 100000; j += i) {
            prime[j] = i;
        }
    }
    return 0;
}();


class Solution {
public:
    int parents[100001];

    int getParent(int n) {
        while(n != parents[n]) {
            parents[n] = parents[parents[n]];
            n = parents[n];
        }
        return n;
    }

    void merge(int a, int b) {
        int pa = getParent(a);
        int pb = getParent(b);
        parents[pa] = pb;
    }

    int largestComponentSize(vector<int>& nums) {
        for(int i = 0; i <= 100000; i++) {
            parents[i] = i;//让他等于父亲结点
        }

        for(auto n : nums) {//对于nums中的每个数 拆解这些数 将他与当前这个质因数合并 但是可能 当前数n存在多个质因数 因此需要多次计算 直至确定其没有多个质因素为止 比如 2 3 6 
            for(int t = n; t != 1;) {
                int v = prime[t];
                merge(n, v);
                while(prime[t] == v) {//直到当前这个元素不包含 因子v时
                    t /= prime[t];
                }
            }
        }

        unordered_map<int, int> fre;//哈希表
        int ans = 0;
        for(auto n : nums) {
            int p = getParent(n);//找到每个元素的父节点
            ans = max(ans, ++fre[p]);//记录最大的数量
        }

        return ans;
    }
};

17.电话号码的字母组合

class Solution {
public:
    vector<string> ans;
    map<int,string> m{{2,"abc"},{3,"def"},{4,"ghi"},{5,"jkl"},{6,"mno"},{7,"pqrs"},{8,"tuv"},{9,"wxyz"}};

    void arrayList(int pos,string & digits,string & result){
        if(pos == digits.size()){
            ans.push_back(result);
        }
        for(char c: m[(int)(digits[pos] -'0')]){
            result[pos] = c;
            arrayList(pos+1,digits,result);
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.empty())
            return {};
        string result(digits);
        arrayList(0,digits,result);
        return ans;
    }
};

18.四数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        if(nums.size() < 4)
            return {};
        sort(nums.begin(),nums.end());
        long long x = (long long)(nums[0]+nums[1]) +nums[2]+nums[3];
        if(x > target)
            return {};
        
        int n = nums.size();
        x = (long long)nums[n-1]+nums[n-2]+nums[n-3]+nums[n-4];
        if(x < target)
            return {};

        int left = 0,right = n-1;
        int l,r,t;
        vector<vector<int> > v;

        for(left = 0; left < n-1;){
            for(right = n-1; right > left;){
                t = target - nums[left] - nums[right];
                l = left+1,r = right-1;
                while(l < n && r >= 0 && l < r){
                    if(nums[l] + nums[r] == t){
                        v.push_back({nums[left],nums[l],nums[r],nums[right]});
                        while(l < n && l < r && nums[l+1] == nums[l]){
                          ++l;
                        }
                        ++l;
                        while(r >= 0 && l < r && nums[r-1] == nums[r]){
                            --r;
                        }
                        --r;
                    }else if(nums[l] + nums[r] < t){
                        ++l;
                    }else{
                        --r;
                    }
                }
                while(right > left && nums[right-1] == nums[right]){
                    --right;
                }
                --right;
            }
            while(left < n-1 && nums[left+1] == nums[left]){
                ++left;
            }
            ++left;
        }


        return v;
    }
};

19.删除链表的倒数第N个结点

/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        int c = 0;
        ListNode *p = head,*q = head;
        while(p){
            ++c;
            p = p->next;
            if(c - 1 > n){
                q = q->next;
            }
        }
        if(n == c){
            head = head->next;
            delete q;
        }else{
            p = q->next;
            q->next = p->next;
            delete p;
        }
        return head;
    }
};

2022.7.31(我也不知道为什么这天我写了这么多)

25.K个一组翻转链表

https://leetcode.cn/problems/reverse-nodes-in-k-group/

/**
 * 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) {
        int length = 0;
        ListNode *p = head,*q = nullptr,*temp = nullptr,*dummy = new ListNode(),*rear = nullptr;
        while(p){
            ++length;
            p = p->next;
        }

        int c = length/k,j = 0;
        p = head,q = dummy,rear = dummy->next;
        for(int i = 0; i < c; ++i){//总共反转c次
            //头插法 
            j = 0;
            //cout << p->val << ' ' << q->val << endl;
            while(j < k){
                ++j;
                temp = p->next;
                p->next = q->next;
                q->next = p;
                if(j == 1){
                    rear = p;
                }
                p = temp;
            }
            q = rear;
        }
        q->next = p;
        return dummy->next;
    }
};

23.合并K个升序链表

https://leetcode.cn/problems/merge-k-sorted-lists/

/**
 * 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 status{
        ListNode *head;
        int val;
        status(ListNode *_head, int _val) : head(_head),val(_val){}
        bool operator <(const struct status &rightValue)const{
            return this->val > rightValue.val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //队列用什么进行排序 第一个数字的大小
        priority_queue<struct status> q;
        ListNode *head = new ListNode(),*p = head;
        for(int i = 0; i < lists.size(); ++i){
            if(lists[i]){
                q.push({lists[i],lists[i]->val});
            }
        }
        while(!q.empty()){
            auto i = q.top();
            q.pop();
            p->next = i.head;
            i.head = i.head->next;
            p = p->next;
            if(i.head){
                i.val = i.head->val;
                q.push(i);
            }
        }
        return head->next;
    }
};
/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *p = list1, *q = list2;
        ListNode *head = new ListNode(),*h = head;
        while(p && q){
            if(p->val > q->val){
                h->next = q;
                q = q->next;
            }else{
                h->next = p;
                p = p->next;
            }
            h = h->next;
        }
        if(!p){
            p = q;
        }
        h->next = p;
        return head->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //队列用什么进行排序 第一个数字的大小
        ListNode *head = new ListNode(),*p = head;
        for(int i = 0; i < lists.size(); ++i){
            head->next = mergeTwoLists(head->next,lists[i]);
        }
        return head->next;
    }
};
/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *p = list1, *q = list2;
        ListNode *head = new ListNode(),*h = head;
        while(p && q){
            if(p->val > q->val){
                h->next = q;
                q = q->next;
            }else{
                h->next = p;
                p = p->next;
            }
            h = h->next;
        }
        if(!p){
            p = q;
        }
        h->next = p;
        return head->next;
    }
    ListNode* merge(vector <ListNode*> &lists, int l, int r) {//归并的递归版本
        if (l == r) return lists[l];
        if (l > r) return nullptr;
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    }

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

27.移除元素

https://leetcode.cn/problems/remove-element/

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int k = 0;
        for(int i = 0; i < nums.size(); ++i){
            if(nums[i] != val){
                nums[k++] = nums[i];
            }
        }
        return k;
    }
};
class Solution {
public://双指针版本
    int removeElement(vector<int>& nums, int val) {
        if(nums.empty())
            return 0;
        int i = 0,j = nums.size()-1;
        while(i <= j){
            while(i <= j && nums[i] != val){
                ++i;
            }
            while(i <= j && nums[j] == val){
                --j;
            }
            if(i < j)
                swap(nums[i],nums[j]);
        }
        return i;
    }
};

24.两两交换链表中的结点

https://leetcode.cn/problems/swap-nodes-in-pairs/

/**
 * 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 || !head->next){
            return head;
        }
        ListNode *p = head,*q = head->next;

        p->next = q->next;
        q->next = p;
        p->next = swapPairs(p->next);
        return q;
    }
};

22.括号生成

https://leetcode.cn/problems/generate-parentheses/

class Solution {
public:
    vector<string> ans;
    void arrayList(string result,int left,int right,int n){
        if(left == n && right == n){
            ans.push_back(result);
            return;
        }
        if(right < n && left < n){
            result[left+right] = '(';
            arrayList(result,left+1,right,n);
        }
        if(right < left && right < n){
            result[left+right] = ')';
            arrayList(result,left,right+1,n);
        }
    }

    vector<string> generateParenthesis(int n) {
        string result(n*2,'(');
        arrayList(result,0,0,n);
        return ans;
    }
};

21.合并两个有序链表

https://leetcode.cn/problems/merge-two-sorted-lists/

/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *p = list1, *q = list2;
        ListNode *head = new ListNode(),*h = head;
        while(p && q){
            if(p->val > q->val){
                h->next = q;
                q = q->next;
            }else{
                h->next = p;
                p = p->next;
            }
            h = h->next;
        }
        if(!p){
            p = q;
        }
        h->next = p;
        return head->next;
    }
};

20.有效的括号

https://leetcode.cn/problems/valid-parentheses/

class Solution {
public:
    bool isValid(string s) {
        stack<int> st;
        map<char,int> m{{'(',1},{')',-1},{'{',2},{'}',-2},{'[',3},{']',-3}};
        for(char c:s){
            if(c == '(' || c == '{' || c == '['){
                st.push(m[c]);
            }else{
                if(st.empty()){
                    return false;
                }
                if(st.top() + m[c]){
                    return false;
                }
                st.pop();
            }
        }
        if(st.empty())
            return true;
        return false;
    }
};

102.二叉树的层次遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal/

/**
 * 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>> levelOrder(TreeNode* root) {
        if(root == nullptr){
            return {};
        }

        vector<vector<int> > ans = {{root->val}};
        queue<TreeNode*> q,qq;

        q.push(root);
        while(!q.empty()){
            TreeNode *temp = q.front();q.pop();

            if(temp->left){
                qq.push(temp->left);
            }
            if(temp->right){
                qq.push(temp->right);
            }

            if(q.empty()){
                vector<int> result;
                while(!qq.empty()){
                    q.push(qq.front());
                    result.push_back(qq.front()->val);
                    qq.pop();
                }
                if(!q.empty()){
                    ans.push_back(result);
                }
            }
        }
        return ans;
    }
};

107.二叉树的层次遍历II

https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/

/**
 * 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>> levelOrderBottom(TreeNode* root) {
        if(root == nullptr){
            return {};
        }

        vector<vector<int> > ans = {{root->val}};
        queue<TreeNode*> q,qq;

        q.push(root);
        while(!q.empty()){
            TreeNode *temp = q.front();q.pop();

            if(temp->left){
                qq.push(temp->left);
            }
            if(temp->right){
                qq.push(temp->right);
            }

            if(q.empty()){
                vector<int> result;
                while(!qq.empty()){
                    q.push(qq.front());
                    result.push_back(qq.front()->val);
                    qq.pop();
                }
                if(!q.empty()){
                    ans.push_back(result);
                }
            }
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

1161.最大层内元素和

https://leetcode.cn/problems/maximum-level-sum-of-a-binary-tree/

/**
 * 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> sum;
    void DFS(TreeNode *root,int level){
        if(level >= sum.size()){
            sum.push_back(root->val);
        }else{
            sum[level] += root->val;
        }
        if(root->left){
            DFS(root->left,level+1);
        }
        if(root->right){
            DFS(root->right,level+1);
        }
    }
    int maxLevelSum(TreeNode* root) {
        DFS(root,0);
        int ans = 0;
        for(int i = 1; i < sum.size(); ++i){
            if(sum[ans] < sum[i]){
                ans = i;
            }
        }
        return ans + 1;
    }
};
/**
 * 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 maxLevelSum(TreeNode* root) {
        //层次遍历 记录层数
        queue<TreeNode*> q,qq;
        int maxLevel = 1,maxNum = root->val,level = 2,num = 0;

        q.push(root);
        while(!q.empty()){
            TreeNode *temp = q.front();q.pop();

            if(temp->left){
                qq.push(temp->left);
                num += temp->left->val;
            }
            if(temp->right){
                qq.push(temp->right);
                num += temp->right->val;
            }
            if(q.empty()){
                while(!qq.empty()){
                    q.push(qq.front()),qq.pop();
                }
                //cout << maxNum << ' ' << num << endl;
                if(!q.empty() && maxNum < num){
                    maxNum = num;
                    maxLevel = level;
                }
                level++;
                num = 0;
            }
        }

        return maxLevel;
    }
};

2022.8.1

28.实现strStr()

https://leetcode.cn/problems/implement-strstr/

class Solution {//kmp
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size(), m = needle.size();
        if (m == 0) {
            return 0;
        }
        vector<int> pi(m);
        for (int i = 1, j = 0; i < m; i++) {//注意i j的取值
            while (j > 0 && needle[i] != needle[j]) {//匹配不成功 移动模式串
                j = pi[j - 1];//回溯
            }
            if (needle[i] == needle[j]) {//匹配成功 模式串指针右移
                j++;
            }
            pi[i] = j;//回到j-1匹配成功的地方?
        }
        for (int i = 0, j = 0; i < n; i++) {
            while (j > 0 && haystack[i] != needle[j]) {
                j = pi[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == m) {
                return i - m + 1;
            }
        }
        return -1;
    }
};
class Solution {
public:
void nextT(string &t, vector<int> &next,int n)
{
    int j = 0, k = -1;
    next[0] = -1;
    while(j < n-1) {
        if(k == -1 || t[j] == t[k]) {
            next[++j] = ++k;
        } else {
            k = next[k];
        }
    }
}
int KMP(string &s, string &t)
{
    int m = s.size();
    int n = t.size();
    int i = 0, j = 0;
    vector<int> next(n);
    nextT(t,next,n);

    while(i < m && j < n) {
        if(j == -1 || s[i] == t[j]) {
            ++i;
            ++j;
        } else {
            j = next[j];
        }
    }
    if(j == n)
        return i - j;
    return -1;
}

int strStr(string haystack, string needle)
{
    return KMP(haystack,needle);
}
};

2022.8.2

29.Divide Two Integers

https://leetcode.cn/problems/divide-two-integers/

class Solution {//类二分法 利用的是数值的表达特性 也就是一个整数 可以用十进制的方式表示出来 也可以用二进制的方式表达出来 所以times数组存储的是 [2^0*y,2^1*y,2^2*y,...,2^i*y] result 默认是0 也就是 0x0; 如果dividend 大于了其中的某个数,则说明divisor必须需要乘上2^i 不然就是后面的全都加起来了 也是2^i-1 无达达到dividend。整除的实质就是如此 dividend/divisor = result + mod; mod 是不需要管的.但是必须得保证result * divisor = dividend。如此操作后,存在的位都被标识为1。 1 << i 看做是2^i恐怕不好理解 不如看做是 在一个用二进制表示的数中从左向右第i位 被标识为1.
public:
    int divide(int dividend, int divisor) {
        //boundary condition
        if(divisor == 1){//dividend/1 = dividend
            return dividend;
        }
        if(divisor == -1){
            if(dividend == INT_MIN){//INT_MIN / -1 = INT_MAX + 1 -> overflow -> return INT_MAX 
                return INT_MAX;
            }
            if(dividend == INT_MAX){//INT_MAX / -1 = INT_MIN + 1
                return INT_MIN+1;
            }
            return 0-dividend;// a/-1 = -a = 0 - a -> return 0-dividend
        }
        
        //negetive number's opposite number will cause overflow while positive number not
        int flag = 1;//record sign is same or not
        if(divisor > 0){
            flag = !flag;
            divisor = 0 - divisor;
        }
        if(dividend > 0){
            flag = !flag;
            dividend = 0 - dividend;
        }

        //divisor and dividend are negetive
        int result = 0,x = divisor;
        vector<int> times = {divisor};
        while(times.size() < 32){//now int only can be presented by 32 bits
            if(x < (INT_MIN>>1)){//avoid overflow
                break;
            }
            x += x;
            if(x < dividend){//find the lower limit
                break;
            }
            times.push_back(x);
        }

        for(int i = times.size()-1; i >= 0; --i){
            if(dividend <= times[i]){//if exists 
                dividend -= times[i];
                result += (1 << i);// i-th bit will be set true
            }
        }
        if(flag){//same sign
            return result;
        }
        return 0-result;//opposite number
    }
};
class Solution {//二分法 重在快速乘函数 必须得理解它才能理解代码
public:
    int divide(int dividend, int divisor) {
        // 考虑被除数为最小值的情况
        if (dividend == INT_MIN) {
            if (divisor == 1) {
                return INT_MIN;
            }
            if (divisor == -1) {
                return INT_MAX;
            }
        }
        // 考虑除数为最小值的情况
        if (divisor == INT_MIN) {
            return dividend == INT_MIN ? 1 : 0;
        }
        // 考虑被除数为 0 的情况
        if (dividend == 0) {
            return 0;
        }
        
        // 一般情况,使用二分查找
        // 将所有的正数取相反数,这样就只需要考虑一种情况
        bool rev = false;
        if (dividend > 0) {
            dividend = 0-dividend;
            rev = !rev;
        }
        if (divisor > 0) {
            divisor = 0-divisor;
            rev = !rev;
        }

        // 快速乘 top-down
        auto quickAdd = [](int y, int z, int x) {//设计思路来自于快速幂 保存中间结果 避免重复计算
            // x 和 y 是负数,z 是正数
            // 需要判断 z * y >= x 是否成立
            int result = 0, add = y;//result 表示额外的奇数 add表示偶数 两种情况下都要满足 z*y >= x
            while (z) {
                if (z & 1) {
                    if (result < x - add) {//需要保证 result + add >= x     避免result+add越界
                        return false;
                    }
                    result += add;
                }
                if (z != 1) {//z-1 = 0;z*y = (z-1)/2 * 2y  如果z = 1 那么等式没必要
                    if (add < x - add) {// 需要保证 add + add >= x
                        return false;
                    }
                    add += add;
                }
                z >>= 1;// 右移一位 = 除以2
            }
            return true;
        };
        
        int left = 1, right = INT_MAX, ans = 0;//从1搜索到INT_MAX 如果找不到 那么答案必然是0
        while (left <= right) {
            // 注意溢出,并且不能使用除法
            int mid = left + ((right - left) >> 1);
            bool check = quickAdd(divisor, mid, dividend);
            if (check) {
                ans = mid;//不断逼近正确结果
                // 注意溢出
                if (mid == INT_MAX) {//如果溢出了 那么返回INT_MAX 即可
                    break;
                }
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }

        return rev ? 0-ans : ans;
    }
};

622.Design Circular Queue

https://leetcode.cn/problems/design-circular-queue/

class MyCircularQueue {//size + front
    vector<int> q;
    int front,rear;
    int size;
    int maxSize;
public:
    MyCircularQueue(int k) {
        q = vector<int>(k);
        front = 0;
        size = 0;
        maxSize = k;
    }
    
    bool enQueue(int value) {
        if(isFull()){
            return false;
        }
        q[(front+size)%maxSize] = value;
        ++size;
        return true;
    }
    
    bool deQueue() {
        if(isEmpty()){
            return false;
        }
        front = (front+1)%maxSize;
        --size;
        return true;
    }
    
    int Front() {
        if(isEmpty()){
            return -1;
        }
        return q[front];
    }
    
    int Rear() {
        if(isEmpty()){
            return -1;
        }
        return q[(front+size+maxSize-1)%maxSize];
    }
    
    bool isEmpty() {
        return size == 0;
    }
    
    bool isFull() {
        return size == maxSize;
    }
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */
class MyCircularQueue {//front rear ;rear point the next of rear of queue
    vector<int> q;
    int front,rear;
    int size;
public:
    MyCircularQueue(int k) {
        q = vector<int>(k+1);
        front = 0;
        rear = 0;
        size = k+1;
    }
    
    bool enQueue(int value) {
        if(isFull()){
            return false;
        }
        q[rear] = value;
        rear = (rear+1)%size;
        return true;
    }
    
    bool deQueue() {
        if(isEmpty()){
            return false;
        }
        front = (front+1)%size;
        return true;
    }
    
    int Front() {
        if(isEmpty()){
            return -1;
        }
        return q[front];
    }
    
    int Rear() {
        if(isEmpty()){
            return -1;
        }
        return q[(rear+size-1)%size];
    }
    
    bool isEmpty() {
        return front == rear;
    }
    
    bool isFull() {
        return (rear+1)%size == front;
    }
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */
class MyCircularQueue {//front rear; rear point the next of next of rear of queue;
    vector<int> q;
    int front,rear;
    int size;
public:
    MyCircularQueue(int k) {
        q = vector<int>(k+1);
        front = 0;
        rear = 1;
        size = k+1;
    }
    
    bool enQueue(int value) {
        if(isFull()){
            return false;
        }
        q[(rear+size-1)%size] = value;
        rear = (rear+1)%size;
        return true;
    }
    
    bool deQueue() {
        if(isEmpty()){
            return false;
        }
        front = (front+1)%size;
        return true;
    }
    
    int Front() {
        if(isEmpty()){
            return -1;
        }
        return q[front];
    }
    
    int Rear() {
        if(isEmpty()){
            return -1;
        }
        return q[(rear+size-2)%size];
    }
    
    bool isEmpty() {
        return (front + 1)%size == rear;
    }
    
    bool isFull() {
        return rear == front;
    }
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */

2022.8.3

899.有序队列

https://leetcode.cn/problems/orderly-queue/

/*
k > 2:
	对于s中任意字符 x,y 假设x <= y, 则必然可以通过一定方式 使得 在最后的字符串中必然满足 x,...,y 
	如果在原字符串s中有x,...,y 即x排列在y前面 则已经达到了要求
	如果在原字符串s中有y,...,x 即y排列在x前面 则可以通过选取y,x中间的元素 和 y前方的元素 使得s的前两个元素为yx 此时便可先选取x 后选取y 满足x,...,y 在后续的调整中 维持相对次序不变 即可达到要求
	因此对于s中任意两个字符 可得x,...,y 因此s整体有序
k == 1:
	首元素的不断出队再入队 至多经过n次迭代 回到 最开始的字符串
	因此在这n次迭代中 字典序最小的那个便是最后的结果。
*/
class Solution {
public:
    string orderlyQueue(string s, int k) {
        string result(s);
        if(k > 1){
            sort(result.begin(),result.end());
        }else{
            priority_queue<string, vector<string> , greater<string> > heap;
            string ss = s + s;
            for(int i = 0; i < s.length(); ++i){
                heap.push(ss.substr(i,s.length()));
            }
            result = heap.top();
        }

        return result;
    }
};
class Solution {//最小表示法
public:
    string orderlyQueue(string s, int k) {
        if(k > 1){
            sort(s.begin(),s.end());
            return s;
        }else{
            s += s;
            int i = 0,j = 1, l = 0,n = s.size();
            while(j + l < n){
                if(s[i+l] == s[j+l]){
                    ++l;
                }else{
                    s[i+l] > s[j+l] ? i = i+l+1 : j = j+l+1;
                    if(i == j){
                        ++j;
                    }
                    l = 0;
                }
            }
            return s.sub(i,n/2);
        }
    }
};

2022.8.4

1163.按字典序排在最后的子串

https://leetcode.cn/problems/last-substring-in-lexicographical-order/

class Solution {
public:
    string lastSubstring(string s) {
        int len = s.size();
        int l = 0,r = 1,k =0;//l r 分别是两个端点 k是当前字符串的长度
        while(r + k < len) {
            if(s[l + k] == s[r + k]) {//相同的情况下 长度增加
                k++;
            }else{//这里移动k+1长度是正确的
                /*按照如下方式理解 s[l+k] 与 s[r+k] 不相等 默认了从l到l+k-1 与 r到r+k-1都是相等的
                题目所要求的是按照字典序排在最后的子串  那么这样的子串必然满足以下条件 
                第一 必然是字典序最大的 也就是说从第一个字母开始都是最大的 也就是说对 ab,bc来说 bc是答案
                第二 必然是字典序最大里面最长的那个 也就是 对于ba,bac来说 bac是答案

                所以如果排在最后的子串如果不是字符串的后缀 那么相较于 是字符串的后缀且与该子串起始点相同的子串
                这个子串才是排在最后的子串
                对于abcc c子串必然小于 cc子串
                所以排在最后的子串必然是字符串的后缀

                如果s[l+k] > s[r+k] 那么r会不断的右移 即使长度相同
                以l作为开头的后缀 必然优于以r作为开头的后缀
                考虑dcdc l = 0,r = 1,k = 0 r 会不断移动 直至达到尾部 退出循环时 将会有 l = 0,r = 2,k = 2;
                对于m = [0,1,...,k] s[r+m]不会是最后的解答
                因为必然存在s[l+m] == s[r+m] 而s[l+k] > s[r+k] (k>=m)
                故以 l+m 为起点的子串必然优于以 r+m 为起点的子串
                故r跳过k+1 段并不会跳过优解
                
                如果s[l+k] < s[r+k] 那么l找到了一个更优的选择 按照第一点所要求的
                l必然是排在最后的子串
                考虑 aaab l = 0,r = 1,k = 3,将会出现 s[l+k] < s[r+k] 的情况
                这样移动后s[l] = b > s[r] = a;

                中间并不会出现存在最优子串的情况
                即 s[l+m] m = [0,1,...,k],s[l+m] > s[l]
                如果存在 那么对于 以r为开头的子串 在同样的位置必然有值为s[l+m]的字符
                而前提条件为s[l+k] < s[r+k]
                因此以s[l+m]为起始位置的子串 必然弱于 以s[r+m] 为起始位置的子串
                故l = l + k + 1 并不会丢弃最优解

                因此无论如何 以l为起始位置的子串都是最优的子串
                */
                s[l+k] > s[r+k] ? r = r + k + 1 : l = l + k + 1;
                
                if(l == r){//避免重复 同时尽可能保证r > l
                    ++r;
                }
                k = 0;
            }
        }
       return s.substr(l);
    }
};

1403.非递增顺序的最小子序列

class Solution {//排序题
public:
    vector<int> minSubsequence(vector<int>& nums) {
        sort(nums.begin(),nums.end());

        int sum = 0;
        for(int x:nums){
            sum += x;
        }

        int val = 0,maxVal = 0,index = 0;
        for(int i = 0; i < nums.size(); ++i){
            val+=nums[i];
            if(sum-val > val){//严格大于
                index = i+1;
            }
        }
        
        vector<int> ans;
        for(int i = nums.size()-1; i >= index; --i){
            ans.push_back(nums[i]);
        }

        return ans;
    }
};

2022.8.5

30.串联所有单词的子串

https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

class Solution {
    /*带剪枝的朴素哈希表
    思路如下
    如果s的长度小于了words中所有字符串长度之和 那么可以直接返回空
    做一个统计数组 下标对应小写字母 用于判断哪些字符在words中出现过
    之后再做一个哈希表 用于统计words中出现了哪些字符串以及这些字符串出现的次数
    之后从0开始枚举 判断words长度下 这些字符串是否出现次数是否和之前哈希表中的一致
    如果一致 则记录当前起点
    i一直后移到下一个在words中出现的字符处
    如此 时间复杂度为 O(NML) 也就是字符串s长度 words长度 words中每个字符的长度
    空间复杂度为O(NM) 也就是words的大小 因为额外需要一个哈希表去存储这些信息
    */
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int n = words.size(), m = words[0].size(), l = s.size();
        if(l < n*m){
            return {};
        }

        vector<int> ans;
        vector<int> visit(26,0);
        unordered_map<string,int> mp,temp;
        for(string& ss: words){
            ++mp[ss];
            for(char c:ss){
                visit[c-'a'] = 1;
            }
        }
        
        int i = 0,j;
        //cout << l << ' ' << n*m << endl;
        while(i + n*m <= l){
            temp = mp;
            for(j = i; j < i+n*m; j+=m){//多个重复的元素 会判断失误
                if(!temp[s.substr(j,m)]){
                    break;
                }else{
                    --temp[s.substr(j,m)];
                }
            }
           // cout << i << ' ' << j << endl;
            if(j == i+n*m){
                ans.push_back(i);
            }

            ++i;
            while(i + n*m < l && !visit[s[i]-'a']){
                ++i;
            }
        }
        return ans;
    }
};
class Solution {
    /*滑动窗口加哈希表
    利用了题目中要求必须连续的特性 将字符串看做一个整体
    每次跳过m个字符长度 提高了效率
    效率的提高正在于他只遍历了m次 每次移动都跳过了m个单位
    而朴素方法 则没有利用已知信息 去除掉一些原先可以避免的无效判断
    时间复杂度为o(n+ml)
    空间复杂度为o(nm)
    */
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int n = words.size(), m = words[0].size(), l = s.size();
        if(l < n*m)		return {};
        
        vector<int> ans;
        unordered_map<string,int> mp;
        for(string &ss:words){
            ++mp[ss];
        }
        
        for(int i = 0; i < m; ++i){
            int l = i + 0, r = i + 0;
            int count = 0;
            unordered_map<string,int> temp;
            while(r +  m <= l){
                string rightTemp = s.substr(r,m);
                if(mp.find(rightTemp) == mp.end()){//不在队列范围内
                    r += m;
                    l = r;
                    temp.clear();//整体右移k
                    count = 0;
                }else if (temp[rightTemp] == mp[rightTemp]){//如果队列已经无法再放下新元素了
                    --temp[s.substr(l,m)];
                    --count;
                    l += m;//出队
                }else {//队列还能放下新元素
                    ++temp[rightTemp];
                    ++count;
                    r += k;//入队
                    if(count == n){
                        ans.push_back(l);//记录答案
                        --temp[s.substr(l,m)];
                        --count;
                        l += m;//出队
                    }
                }
            }
        }
        return ans;
    }
};

2022.8.6

32.最长有效符号

https://leetcode.cn/problems/longest-valid-parentheses/

总共四种方法 算上暴力的话 五种

动规 栈 贪心 在线

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxLength = 0;
        vector<int> dp(s.size(),0);
        for(int i = 1; i < s.size(); ++i){
            if(s[i] == ')'){//只有这样才可能是有效的字符串
                if(s[i-1] == '('){
                    dp[i] = (i >= 2 ? dp[i-2] : 0) + 2;//增长2 因为() 加上之前的长度
                }else if(i-dp[i-1] > 0 && s[i-dp[i-1]-1] == '('){
                    dp[i] = dp[i-1] + (i-dp[i-1] > 2 ? dp[i-dp[i-1]-2] : 0) + 2;
                    //这里需要好好处理 ...(...) = i-dp[i-1]-2,i-dp[i-1]-1,...,i-1,i 此时i为) i-1 为) 而dp[i-1] 为以i-1结尾的最长有效序列的长度 减去这个长度 再向前走一步 就是刚好能包裹这个序列的一个更长序列 这个更长序列比i-1序列长2 但是此时就需要再向前走一步
                    //看看 ... = i-dp[i-1]-2 的长度 因为此时刚好搭上了这个序列 更长的需要需要累加
                }
                maxLength = max(maxLength,dp[i]);
            }
        }
        return maxLength;
    }
};
class Solution {
public:
    int longestValidParentheses(string s) {
        int maxLength = 0;
        int left = 0, right = 0;
        for(int i = 0; i < s.size(); ++i){
            if(s[i] == '('){
                ++left;
            }else{
                ++right;
            }
            if(left < right){//这里清空为0很重要 这样就能够避免了不连续但是计数的情况
                left = right = 0;
            }else if(left == right){
                maxLength = max(maxLength,left+right);
            }
        }
        left = right = 0;
        for(int i = s.size()-1; i >= 0; --i){
            if(s[i] == '('){
                ++left;
            }else{
                ++right;
            }
            if(right < left){
                left = right = 0;
            }else if(left == right){
                maxLength = max(maxLength,right+left);
            }
        }
        return maxLength;
    }
};
class Solution {
public:
    int longestValidParentheses(string s) {
        int maxLength = 0, ans = 0;
        stack<int> st;
        st.push(-1);//预先有元素 避免和定义冲突
        for(int i = 0; i < s.size(); ++i){
            if(s[i] == ')'){
                st.pop();//先弹出来再进行判断 因为最长的子序列看的是当前这个到上一个还没有被 ) 匹配的左字符的距离 而不是当前这个右括号到能匹配它的左括号之间的距离 有点反直觉啊
                if(st.empty()){
                    st.push(i);
                }else{
                    maxLength = max(maxLength,i-st.top());
                }
            }else{
                st.push(i);
            }
        }
        return maxLength;
    }
};
class Solution {
public:
    int longestValidParentheses(string s) {//最大的问题是连续 我想的是双指针加滑动窗口 解决这个问题应该很好
        if(s.empty()) return 0;
        int left = 0, right = 0;
        vector<int> dp(s.size(),0);
        for(int i = 0; i < s.size(); ++i){
            if(s[i] == ')'){
                if(left > right){
                    dp[i] = 1;
                    --left;
                }else{
                    dp[i] = 0;
                }
            }else{
                ++left;
            }
        }
        left = right = 0;
        for(int i = s.size()-1; i >= 0; --i){
            if(s[i] == ')'){
                ++right;
            }else{
                if(right > left){
                    dp[i] = 1;
                    --right;
                }else{
                    dp[i] = 0;
                }
            }
        }
        // for(int i: dp){
        //     cout << i << ' ';
        // }
        int maxLength = 0,l = 0;
        for(int i = 0; i < dp.size(); ++i){
            if(!dp[i]){
                maxLength = max(l,maxLength);
                l = 0;
            }else{
                ++l;
            }
        }
        maxLength = max(l,maxLength);
        return maxLength;
    }
};//最长连续 连续是一个很难的要求 因为我无法判别哪些是无效的比较

2022.8.7

636.函数的独占时间

https://leetcode.cn/problems/exclusive-time-of-functions/

class Solution {
public:
/*
16ms 12.8MB
清理一下思路
具体是如何执行的 以及可能会有什么样子的测试样例
从测试样例说起
2
["0:start:0","0:start:2","0:end:5","1:start:7","1:end:7","0:end:8"]
这种测试样例 就是中间执行完了一个函数 再回头去执行栈顶的元素 之后又遇到了一个新的函数 需要再次开始执行 注意 回头执行的时间也是应该算到总时间里面的
3
["0:start:0","0:end:0","1:start:1","1:end:1","2:start:2","2:end:2","2:start:3","2:end:3"]
但是上面这种方案 在这里就行不通
因为函数中间并没有其他的函数需要运行
所以 在一个函数执行完后 需要额外去判断一下 之前是否有函数在运行 即 栈顶是否还有元素
如果有的话 需要去对其进行更新 将其开始时间更新为 当前函数结束时间的下一时刻
此种 操作的目的是 因为 当前函数可能会不止一次被打断 因此 总运行时间需要不断的累加 
之后如果执行新函数之前 栈顶不为空->当前有函数正在执行 
需要让 当前真正执行的函数更新时间

更新时间注意 终止 和 起始 的计算并不一样  会有 1 的区别
*/
    vector<int> exclusiveTime(int n, vector<string>& logs) {
        vector<int> exclusive(n,0);
        stack<pair<int,int> > s;

        int id,time;
        char op[8];

        for(string &log : logs){
           sscanf(log.c_str(), "%d:%[^:]:%d", &id, op, &time);
            if(op[0] == 's'){//开始执行
                if(!s.empty()){
                    exclusive[s.top().first] += time - s.top().second;
                }
                s.push({id,time});
            }else{//终止执行
                exclusive[id] += time - s.top().second + 1;
                s.pop();
                if(!s.empty()){
                    s.top().second = time + 1;
                }
            }
        }
        return exclusive;
    }
};

33.搜索旋转排序数组

https://leetcode.cn/problems/search-in-rotated-sorted-array/

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.size() == 1) return nums[0] == target ? 0 : -1;
        int left = 0;
        int right = nums.size() - 1;
        while (left <= right) {
            int mid = (right + left) / 2;
            if (nums[mid] == target) {
                return  mid;
            }
            if (nums[0] <= nums[mid]) { // 左侧有序
                if (nums[mid] > target && nums[0] <= target) {//target在左侧 直接到左侧进行搜索即可
                    right = mid -1;
                } else {//target在右边 到右边进行搜索
                    left = mid + 1;
                }
            } else { // 右侧有序
                if(nums[mid] < target && nums[nums.size() - 1] >= target) {//target在右侧 直接到右侧进行搜索
                    left = mid + 1;
                } else {//在左侧
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
};
class Solution {
public:
    int binarySearch(vector<int>& nums, int left, int right, int target){
        while(left <= right){
            int mid = ((right - left)>> 1) + left;
            if(nums[mid] > target){
                right = mid-1;
            }else if(nums[mid] < target){
                left = mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
    int search(vector<int>& nums, int left, int right, int target){
        if(nums[left] <= nums[right]){
            if(target < nums[left] || target > nums[right]){
                return -1;
            }
            return binarySearch(nums,left,right,target);
        }else{
            int mid = (right-left)/2+left;
            int ans = search(nums,left,mid,target);
            if(ans != -1){
                return ans;
            }
            return search(nums,mid+1,right,target);
        }
    }
    int search(vector<int>& nums, int target) {
        return search(nums,0,nums.size()-1,target);
    }
};
class Solution {
public:
    int binarySearch(vector<int>& nums, int left, int right, int target){
        while(left <= right){
            int mid = ((right - left)>> 1) + left;
            if(nums[mid] > target){
                right = mid-1;
            }else if(nums[mid] < target){
                left = mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        int mid = left + ((right - left) >> 1);
        if(nums[left] < nums[right]){//升序数组 直接进行二分即可
            return binarySearch(nums,left,right,target);
        }else{
            if(nums[mid] >= nums[right] && nums[mid] <= nums[left]){//降序数组 直接二分
                while(left <= right){
                    mid = ((right-left) >> 1) + left;
                    if(nums[mid] == target){
                        return mid;
                    }else if(nums[mid] > target){
                        left = mid + 1;
                    }else{
                        right = mid - 1;
                    }
                }
                return -1;
            }else{//正常情况
                while(left <= right){
                    mid = ((right - left) >> 1) + left;
                    if(nums[mid] > nums[mid+1]){
                        break;
                    }                        
                    if(nums[mid] > nums[right]){
                        left = mid;
                    }else if(nums[mid] < nums[right]){
                        right = mid;
                    }
                }
                //cout << mid << endl;
                if(target >= nums[0]){//0...k
                    return binarySearch(nums,0,mid,target);
                }else{//k+1...n-1
                    return binarySearch(nums,mid+1,nums.size()-1,target);
                }
            }
        }
        return -1;
    }
};

2022.8.8

34.在排序数组中查找元素的第一个和最后一个位置

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/

class Solution { 
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int ans = nums.size();
        int left = 0, right = ans-1, mid =  ((right-left)>>1)+left;
        //找到等于target的第一个元素的位置 如果ans从来都没有被更新 那么说明 数组中并不存在target元素
        while(left <= right){
            mid = ((right-left)>>1)+left;
            if(nums[mid] >= target){
                if(nums[mid] == target) ans = mid;
                right = mid-1;
            }else{
                left = mid+1;
            }
        }
        //cout << ans << endl;
        if(ans == nums.size())  return {-1,-1};
        //找刚好大于target的第一个元素的前一个位置 在满足要求的情况下 搜索范围会越来越向右边靠拢
        int result = ans;
        ans = nums.size();
        left = 0, right = ans-1,mid = ((right-left)>>1)+left;
        while(left <= right){
            mid = ((right-left)>>1)+left;
            if(nums[mid] <= target){
                ans = mid;
                left = mid+1;
            }else{
                right = mid-1;
            }
        }
        //cout << ans << endl;
        return vector<int>{result,ans};
    }
};
class Solution { 
public:
    int binarySearch(vector<int>&nums, int left, int right, int target, bool isLower){
        if(left > right){//超出查找范围了 未找到
            return -1;
        }
        if(left == right){//只有一个元素直接判断
            return nums[left] == target ? left : -1;
        }
        if(left + 1 == right){//有两个元素的时候
            if(isLower){//如果是找小的那边 那么应该先判断左边是否合适 再判断右边是否合适 这样才是找的lower
                return nums[left] == target ? left : (nums[right] == target ? right : -1);
            }//同样的 找大的那边 先判断右边是否合适 再看左边
            return nums[right] == target ? right : (nums[left] == target ? left : -1);
        }
        int mid = ((right-left)>>1) + left;
        if(isLower){
            if(nums[mid] < target){//此处同样需要注意
                return binarySearch(nums,mid+1,right,target,isLower);
            }else{//Mid >= target的时候 需要包含mid了再去寻找 而不是等于mid的时候就直接返回 因为要找到一个最大的
                return binarySearch(nums,left,mid,target,isLower);
            }
        }else{
            if(nums[mid] > target){//同理
                return binarySearch(nums,left,mid-1,target,isLower);
            }else{
                return binarySearch(nums,mid,right,target,isLower);
            }
        }
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int lower = binarySearch(nums,0,nums.size()-1,target,true);//此处可先判断lower是否找到了 没找到直接返回-1即可
        int upper = binarySearch(nums,0,nums.size()-1,target,false);
        return vector<int>{lower,upper};
    }
};

2022.8.9

    //先遍历列 再遍历行 先垂直遍历 再水平遍历
    bool dfs(int i, int j, vector<vector<char> > & board){
        cout << count << endl;
        if(count == 0){
            for(int i = 0; i < 9; ++i){
                for(int j = 0; j < 9; ++j){
                    cout << board[i][j] << ' ';
                }
                cout << endl;
            }
            return true;
        }
        if(i+1 < 9){
            bool flag = false;
            for(int m = 1; m < 10; ++m){
                if(rows[i+1][m] && columns[j][m] && zooms[(i+1)/3][j/3][m]){
                    flag = true;
                    if(!dfs(i+1,j,board)){
                        return false;
                    }
                    break;
                }
                if(!rows[i+1][m] && !columns[j][m] && !zooms[(i+1)/3][j/3][m]){
                    --count;
                    rows[i+1][m] = columns[j][m] = zooms[(i+1)/3][j/3][m] = 1;
                    board[i+1][j] = '0' + m;
                    if(!dfs(i+1,j,board)){//如果当前值走不想去 退栈
                        ++count;
                        board[i+1][j] = '.';
                        rows[i+1][m] = columns[j][m] = zooms[(i+1)/3][j/3][m] = 0;
                    }else{
                        flag = true;
                        break;
                    }
                }
            }
            if(!flag){
                return false;
            }
        }
        if(j+1 < 9){
            bool flag = false;
            for(int m = 1; m < 10; ++m){
                if(rows[i][m] && columns[j+1][m] && zooms[i/3][(j+1)/3][m]){//原先在这里就存放着一个值
                    flag = true;
                    if(!dfs(i,j+1,board)){
                        return false;
                    }
                    break;
                }
                if(!rows[i][m] && !columns[j+1][m] && !zooms[i/3][(j+1)/3][m]){
                    --count;
                    rows[i][m] = columns[j+1][m] = zooms[i/3][(j+1)/3][m] = 1;
                    board[i][j+1] = '0' + m;
                    if(!dfs(i,j+1,board)){
                        ++count;
                        board[i][j+1] = '.';
                        rows[i][m] = columns[j+1][m] = zooms[i/3][(j+1)/3][m] = 0;                   
                    }else{
                        flag = true;
                        break;
                    }
                }
            }
            if(!flag){
                return false;
            }
        }
        return true;
    }
    bool dfs(int i, int j, int num,vector<vector<char> > board){//此函数无法保证正确性
        if(i == 8 && j == 8){
            for(int i = 0; i < 9; ++i){
                for(int j = 0; j < 9; ++j){
                    cout << board[i][j] << ' ';
                }
                cout << endl;
            }
            return true;
        }
        //此处是否能填写num
        if(board[i][j] != '.'){//此处已经填写了其他值 并且不是这个值
            if(board[i][j]-'0' != num){
                return false;
            }
            //此处填写的正好是这个值
            //直接开始进一步遍历
            int flag = (i+1) < 9 ? 0 : 1;//如果越界了 那么默认可以填写该值
            for(int m = 1; (i+1) < 9 && m < 10; ++m){
                flag = flag || dfs(i+1,j,m,board);
            }
            if(!flag){//填写失败 
                return false;
            }
            flag = (j+1) < 9 ? 0 : 1;//如果向右越界了 默认可以填写该值
            for(int m = 1; (j+1) < 9 && m < 10; ++m){
                flag = flag || dfs(i,j+1,m,board);//只要有一种情况是真 返回就是真
            }
            if(!flag){
                return false;
            }
            return true;
        }
        if(!rows[i][num] && !columns[j][num] && !zooms[i/3][j/3][num]){//如果满足合法条件
            board[i][j] = '0' + num;
            rows[i][num] = columns[j][num] = zooms[i/3][j/3][num] = 1;//填写当前值
            //开始进一步遍历
            int flag = (i+1) < 9 ? 0 : 1;
            for(int m = 1; (i+1) < 9 && m < 10; ++m){
                flag = flag || dfs(i+1,j,m,board);
            }
            if(!flag){//填写失败
                board[i][j] = '.';
                rows[i][num] = columns[j][num] = zooms[i/3][j/3][num] = 0;
                return false;
            }
            flag = (j+1) < 9 ? 0 : 1;
            for(int m = 1; (j+1) < 9 && m < 10; ++m){
                flag = flag || dfs(i,j+1,m,board);
            }
            if(!flag){
                board[i][j] = '.';
                rows[i][num] = columns[j][num] = zooms[i/3][j/3][num] = 0;
                return false;
            }
            return true;
        }else{
            return false;//受到数独本身规则的限制 无法进行填写
        }
    }
class Solution {
private:
    int line[9];
    int column[9];
    int block[3][3];
    bool valid;
    vector<pair<int, int>> spaces;

public:
    void flip(int i, int j, int digit) {//在i行j列 填充digit
        line[i] ^= (1 << digit);//先将digit移动到指定位置 然后进行与运算 因为新添加的数除了要补充的那个位置是1以外 其他都是0 因此 
        column[j] ^= (1 << digit);
        block[i / 3][j / 3] ^= (1 << digit);
    }

    void dfs(vector<vector<char>>& board, int pos) {
        if (pos == spaces.size()) {
            valid = true;
            return;
        }

        auto [i, j] = spaces[pos];
        int mask = ~(line[i] | column[j] | block[i / 3][j / 3]) & 0x1ff;
        for (; mask && !valid; mask &= (mask - 1)) {
            int digitMask = mask & (-mask);
            int digit = __builtin_ctz(digitMask);
            flip(i, j, digit);
            board[i][j] = digit + '0' + 1;
            dfs(board, pos + 1);
            flip(i, j, digit);
        }
    }

    void solveSudoku(vector<vector<char>>& board) {
        memset(line, 0, sizeof(line));
        memset(column, 0, sizeof(column));
        memset(block, 0, sizeof(block));
        valid = false;

        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] != '.') {
                    int digit = board[i][j] - '0' - 1;
                    flip(i, j, digit);
                }
            }
        }

        while (true) {
            int modified = false;
            for (int i = 0; i < 9; ++i) {
                for (int j = 0; j < 9; ++j) {
                    if (board[i][j] == '.') {
                        int mask = ~(line[i] | column[j] | block[i / 3][j / 3]) & 0x1ff;
                        if (!(mask & (mask - 1))) {
                            int digit = __builtin_ctz(mask);
                            flip(i, j, digit);
                            board[i][j] = digit + '0' + 1;
                            modified = true;
                        }
                    }
                }
            }
            if (!modified) {
                break;
            }
        }

        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] == '.') {
                    spaces.emplace_back(i, j);
                }
            }
        }

        dfs(board, 0);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值