代码随想录-笔记

算法

数组

二分法

class Solution {
public:
    int search(vector<int>& nums, int target) {
        //[l,r)
        int l=0,r = nums.size();
        int mid = (l+r)/2;
         //等号取不到
        while (l<r){
            if (nums[mid] == target) {return mid;}
            //整数除法小数点都舍掉要进一
            else if (nums[mid]<target) {l = mid+1;}
            else {r = mid;}
            mid = (l+r)/2;
        }
        return -1;
    }
};

移除元素(双指针)

移除
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int l=0,r=0;
        //遍历一次数组
        for (int  &a:nums){
            if (l!=r) {nums[l] = nums[r];}
            //左指针保留,空出位置
            if (a==val) l-=1;
            l++;
            r++;
        }
        return l;
    }
};



class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int sptr=0;
        for (int fptr=0;fptr<nums.size();fptr++){
			//遇到不同sptr加一
            if (nums[fptr]!=val) {nums[sptr++] = nums[fptr];
        }}
        return sptr;
    }
};
移动零
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int sptr = 0;
        for (int fptr=0;fptr<nums.size();fptr++){
            if (nums[fptr]!=0){
                int temp =  nums[sptr];
                nums[sptr++] = nums[fptr];
                nums[fptr] = temp;
            }
        }
    }
};

有序数组平方

给你一个按 **非递减顺序** 排序的整数数组 `nums`,返回 **每个数字的平方** 组成的新数组,要求也按 **非递减顺序** 排序。
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        //创建登场数组
        vector<int> v(nums.size(),0);
        int l=0, r=nums.size()-1;
        //从最后元素开始
        for (int k=nums.size()-1;k>-1;k--){
            //比较左端和右端的绝对值
            if (abs(nums[l])<abs(nums[r])){
                v[k] = nums[r]*nums[r];
                r--;
            }
            else {
                v[k]=nums[l]*nums[l];
                l++;
            }
        }
        return v;
    }
};

长度最小子数组

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        
        int l=0,len = nums.size()+1, sum=0;
        for (int r=0;r<nums.size();r++){
            //有一个元素大于target则返回1
            if (nums[r]>=target) return 1;
            sum += nums[r];
            //循环知道长度范围内小于target
            while (sum>=target){
                len = (len<=(r-l+1)) ? len:r-l+1;
                sum -= nums[l++];
            }
        }
		//判读累加是否有大于target
        return (len==nums.size()+1) ? 0:len;
    }
};

旋转矩阵! !

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: 
[ [ 1, 2, 3 ], 
[ 8, 9, 4 ], 
[ 7, 6, 5 ] ]
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> arr(n,vector<int>(n,0));
        //loop循环次数,xy初始值,cnt计数值
        int loop = n/2, x=0,y=0,cnt=1;
        //确定边界
        int sub=1;
        //实际坐标
        int x1,y1;
        for (;loop>0;loop--){
        //sub=1,防止边界溢出
        for (y1=y;y1<n-sub;y1++){
            arr[x][y1] = cnt++;
        }
        for (x1=x;x1<n-sub;x1++){
            arr[x1][y1] = cnt++;
        }
        for (;y1>y;y1--){
            arr[x1][y1] = cnt++;
        }
        for (;x1>x;x1--){
            arr[x1][y1] = cnt++;
        }
        //初始值进一位,边界同理
        x++;y++;
        sub++;}
        //判断是否为奇数,中间格子赋值
        if (n%2) arr[n/2][n/2] = cnt;
    return arr;
    }
};

链表

定义

struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

移除链表指定元素

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //删除目标头结点
        while (head != NULL && head->val==val){
            ListNode *temp = head;
             head = head->next;
             delete temp;
        }
        //other
        ListNode *cur = head;
        while (cur != NULL && cur->next != NULL){
            if (cur->next->val==val){
                ListNode *temp = cur->next;
                cur->next = temp->next;
                delete temp;
            }
            //一定要加else,避免连续出现目标值
            else {cur = cur->next;}

        }
        return head;
    }
};

//建立虚拟头节点
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode *virhead = new ListNode(0);
        //将虚拟头结点指向head,这样方面后面做删除操作
        virhead->next = head;
        ListNode *cur = virhead;
        while(cur->next != NULL){
            if (cur->next->val == val){
                ListNode *temp = cur->next;
                cur->next = temp->next;
                delete temp;
            }
            else {cur = cur->next;}

        }
        //重新赋值头节点
        head = virhead->next; 
        delete virhead;
        return head;
    }
};

链表类的定义 !!

class MyLinkedList {
public:
    //定义节点
    struct LinkeNode{
        int val;
        LinkeNode* next;
        //节点初始化,next指向nullptr
        LinkeNode(int val=0): val(val), next(nullptr){}
    };
    //初始化函数,便于后面操作
    MyLinkedList() {
        _size = 0;
        //虚拟头节点,方便后续操作统一
        _dummyhead = new LinkeNode(0);
    }
    
    int get(int index) {
        //无效下标
        if (index>_size-1 || index<0){
            return -1;
        }
        LinkeNode*cur = _dummyhead->next;
        while (index--){
            cur = cur->next;
        }

        return cur->val;
    }
    //添加头节点
    void addAtHead(int val) {
        
        LinkeNode* add = new LinkeNode(val);
        add->next = _dummyhead->next;
        _dummyhead->next = add;
        _size++;
    }
    
    void addAtTail(int val) {
        LinkeNode* add = new LinkeNode(val);
        LinkeNode* cur = _dummyhead;
        int i = _size;
        //不能用_size--,会让长度清零
        while(i--){
            cur = cur->next;
        }
        cur->next = add;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index<0 || index>_size){
            return;
        }
        LinkeNode* add = new LinkeNode(val);
        LinkeNode* cur = _dummyhead;
        while(index--){
            cur = cur->next;
        }
        add->next = cur->next;
        cur->next = add;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if (index<0 ||index>_size-1)
        {return;}
        LinkeNode*cur = _dummyhead;
        while(index--){
            cur = cur->next;
        }
        LinkeNode* sub = cur->next;
        cur->next = sub->next;
        //记得释放
        delete sub;
        //delete命令指示释放了sub指针原本所指的那部分内存,
        //被delete后的指针sub的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句sub=nullptr,sub会成为乱指的野指针
        //如果之后的程序不小心使用了sub,会指向难以预想的内存空间
        sub=nullptr;
        _size--;
    }

private:
    //定义数据
    int _size;
    LinkeNode* _dummyhead;
};


翻转链表

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //当前链表
        ListNode* cur = head;
        //前列表
        ListNode* pre = nullptr;
        //憨批列表
        ListNode* temp;
        //翻转一次操作
        while(cur){
        temp = cur->next;
        cur->next = pre;
        pre = cur;
        cur = temp;
        }
        head = pre;
        return head;
    }
    
};

两两交换节点

输入:head = [1,2,3,4]
输出:[2,1,4,3]

//不设置虚拟头节点来交换每两个节点中的链接会很复杂
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //创建虚拟头节点指向头节点
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        //当前变量值
        ListNode* cur = dummyhead;
        //定义临时值
        ListNode* temp;
        //cur->next->next需确保cur->next存在先
        while (cur->next != nullptr&& cur->next->next != nullptr){
            temp = cur->next;
            cur->next = cur->next->next;
            temp->next = cur->next->next;
            cur->next->next = temp;
            cur = cur->next->next;   
        }
        return dummyhead->next;
    }
};

删除 倒数第n个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead = new ListNode(0);
            dummyhead->next = head;
        	//双指针,快指针检测终点,慢指针做删除动作
            ListNode * fptr=dummyhead, *sptr=dummyhead;
        	//快指针提前走n步
            for (int i=0;i<n;i++){
                fptr = fptr->next;
            }
        //检测终点
            while (fptr->next != nullptr){
                fptr = fptr->next;
                sptr = sptr->next;
            }
        //删除操作
            ListNode* temp = sptr->next;
            sptr->next = temp->next;
            delete temp;
            return dummyhead->next;
    }
};

链表相交

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        //计算长度,确定起点
        int lena=0, lenb=0;
         ListNode*cur = headA;
        
         while (cur != nullptr){
            lena++;
            cur = cur->next;
         }
         cur = headB;
         while (cur != nullptr){
            lenb++;
            cur = cur->next;
         }
        //确保heada为长节点
         if (lenb>lena){  
            swap(lena, lenb);
            swap(headA, headB);
         }
         ListNode* cura = headA;
        //长节点开头对其短节点
         for (int sub = lena-lenb;sub>0;sub--){
            cura = cura->next;
         }
        cur = headB;
         while(cura != nullptr){
             if (cur==cura){
                 return cur;
             }
             cur = cur->next;
             cura = cura->next;
             
         }
         return NULL;
    }
};

环形链表!!

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

image-20231020223052906

相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z

//引用代码随想录

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fptr = head, *sptr = head;
        while (fptr != NULL && fptr->next != NULL){
            fptr = fptr->next->next;
            sptr = sptr->next;
			// 一快一慢,若相遇则有环
            if (fptr==sptr){
                //根据数学公式推导,与头节点相遇即为入口
                while (head!=sptr){
                    head = head->next;
                    sptr = sptr->next;
                }
                return sptr;
            }

        }
        return NULL;
    }
};

哈希表

 int hash[26] = {0};//数组,长度确定时使用
unordered_set<int> result_set;//无序集合,自动去重
unordered_map<int,int> hash_table; hash_table[key] = val;//python字典,

相同字母字符串

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.size()!=t.size()) return false;
        int hash[26] = {0};
        //记录每个字母出现的次数
        for (int i=0;i<s.size();i++){
            hash[s[i]-'a']++;
        }
        //字母出现负数就为false,相同则减1
        for (int i=0;i<t.size();i++){
            if (hash[t[i]-'a']==0) return false;
            else
            hash[t[i]-'a']--;
        }
        //若不全为零则字母不相同
        for (int i=0;i<26;i++){
            if (hash[i]!=0) return false;
        }
    return true;
    }
};

求数组交集

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //创建hash表,无序无重复;
        unordered_set<int> result_set;
		//nums1求集合,去重
        unordered_set<int> nums_set(nums1.begin(),nums1.end());
        for (int n:nums2){
            if (nums_set.find(n)!=nums_set.end()){
                //unordered_ser自动去重
                result_set.insert(n);
            }
        }
        return vector<int> (result_set.begin(),result_set.end());
    }
};

求快乐数

快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:输入:n = 19      输出:true
解释:
12 + 92 = 82
=82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
class Solution {
public:
    //依次求位数上的平方和
    int ret(int n){
        int num=0;
        int a;
        while (n>=1){
            a = n%10;
            num+=a*a;
            n/=10;
        }
        return num;
    }
    bool isHappy(int n) {
        unordered_set<int> hash_set;
        while ((n=ret(n))!=1){
            //如果不存在就插入
            if (hash_set.find(n)==hash_set.end()){
                hash_set.insert(n);
            }
            //存在说明陷入循环
            else return false;
        }
        return true;
    }
};

两数之和

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //创建map,便于值键对应
        unordered_map<int,int> hash_table;
        for (int i=0;i<nums.size();i++){
            auto iter = hash_table.find(target-nums[i]);
            if (iter != hash_table.end()){
                //如果找到缺少的数的下标就放回
                return {iter->second,i};
            }
            //没有则放入
            hash_table[nums[i]] = i;
        }
        return {};
    }
};

四数之和

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
//利用哈希表存值,使时间复杂度缩短2个指数
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>map;
        //map:key记录a+b,val记录count(a+b)
        for (int a:nums1){
            for (int b:nums2){
                map[a+b]++;
            }
        }
        int cnt = 0;
        //如果c+d+a+b则cnt加一
        for (int c:nums3){
            for (int d:nums4){
                if (map.find(0-(c+d)) != map.end()){
                    cnt += map[0-c-d];
                }
            }
        }
        return cnt;
    }
    
};

找字符串

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = "a", magazine = "b"
输出:false
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int arr[26]={0};
        //出现在rans中就加一
        for (auto &s:ransomNote){
            arr[s-'a']++;
        }
        //出现在maga中则对应值减一
        for (auto &s:magazine){
            arr[s-'a']--;
        }
        for (int i:arr){
            //存在大于零的则说明rans中有字符没被maga抵消
            if (i>0) return false;
        }
        return true;
    }
};

三数之和!!

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> arr;
        //排序,确定指针方向
        sort(nums.begin(),nums.end());
        //左右指针
        int l,r;
        int sum;
        for (int i=0;i<nums.size()-2;i++){
            //后面都比零大,不可能为零
            if (nums[i]>0) break;
            //去重重复元素
            if (i>0 &&nums[i]==nums[i-1]) continue;
            l = i+1;
            r = nums.size()-1;

            while (l<r){
                sum = nums[i]+nums[l]+nums[r];
            if (sum==0){
                arr.push_back(vector<int>{nums[i],nums[l],nums[r]});
                //去除相同的值
                while(l<r && nums[l]==nums[l+1]) l++;
                while(l<r && nums[r]==nums[r-1]) r--;
                //指针各自更新
                l++;r--;
                }
            else if (sum>0){
                r--;
            }
            else l++;
        }
        }
    return arr;
    }
};

四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> arr;
       	//双指针使用前要排序
        sort(nums.begin(), nums.end());
        int n=nums.size();
        int l,r;
        //注意长度
        long sum;
        for (int i=0;i<n-3;i++){
            //后面都大于等于零则总和无法减小
            if (nums[i]>target && nums[i]>=0) break;
            //避免重复操作
            if (i>0 && nums[i]==nums[i-1]) continue;
            for (int j=i+1;j<n-2;j++){
                if (nums[i]+nums[j]>target && nums[j]>=0) break;
                //注意,j要大于i+1才有效
                if (j>i+1 && nums[j]==nums[j-1]) continue;
                l=j+1;r=n-1;
                while (l<r){
                    //强制转换
                    sum = (long) nums[i]+nums[j]+nums[l]+nums[r];
                    if (sum==target){
                        arr.push_back(vector<int>{nums[i],nums[j],nums[l],nums[r]});
                        //过滤掉重复的
                        while (l<r && nums[l+1]==nums[l]) l++;
                        while (l<r && nums[r-1]==nums[r]) r--;
                        //l,r各自更新
                        l++;r--;
                    }
                    else if (sum<target) l++;
                    else r--;
                }
            }
        }
        return arr;

    }
};

字符串

反转字符串

class Solution {
public:
    void reverseString(vector<char>& s) {
        int n = s.size();
        int temp;
        //对称交换
        for (int l = s.size()/2;l>0;l--){
            temp = s[l-1]; 
            s[l-1]=s[n-l];
            s[n-l] = temp;
        }
    }
};


class Solution {
public:
    void reverseString(vector<char>& s) {
        //双链表交换,减少运算
        for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
            swap(s[i],s[j]);
        }
    }
};

反转字符串 ||

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
class Solution {
public:
    
    //定义反转函数
    void reverse(string& s, int start, int end){
            for (;start<(start+end+1)/2;start++,end--){
                swap(s[start], s[end]);
            }
        }
    string reverseStr(string s, int k) {
        
        int i=0;
        //判断是否能够完成,每隔 2k 个字符的前 k 个字符进行反转
        for (;i+2*k<s.size();i+=(2*k)){
            reverse(s,i,i+k-1);
        }
		//判断剩余数量的字符是否到达K
        if (i+k-1<s.size()-1){
            reverse(s,i,i+k-1);
        }
        //不到达则全部反转
        else{
            reverse(s,i,s.size()-1);
        }
    return s;
    }
};

反转字符串中的单词

输入:s = "the sky is blue"  输入:s = "    the       sky   is blue     "
输出:"blue is sky the"      输出:"blue is sky the"
class Solution {
public:
    //定义反转函数
    void reserve(string &s, int start, int end){
        for (;start<(start+end+1)/2;start++,end--){
            swap(s[start],s[end]);
        }
    }
    string reverseWords(string s) {
        int sptr=0,fptr=0;
        //过滤前面的空格
        while (s[fptr]==' ') fptr++;
        //替换中间的空格
        for (;fptr<s.size();fptr++){
            if (s[fptr]!=' '){
                s[sptr++] = s[fptr];
            }
            else{
                if (s[fptr-1]!=' ')
                    s[sptr++] = s[fptr];
                
            }
        }
        //如果后面有空格就替换掉
        if (s[sptr-1]==' ') s.resize(sptr-1);
        //没有就直接截掉后面空间
        else s.resize(sptr);
        //全部反转
        reserve(s,0,s.size()-1);
        sptr = 0;fptr=0;
        for (;fptr<s.size();fptr++){
            if (s[fptr]==' '){
                //每个单词反转
                reserve(s, sptr, fptr-1);
                sptr=fptr+1;
            }
        }
        //最后一个单词反转
        reserve(s,sptr,fptr-1);
    return s;
    }
};

字符串左旋

输入: password = "s3cur1tyC0d3", target = 4
输出: "r1tyC0d3s3cu"
class Solution {
public:
    //定义反转函数
    void reverse(string& s, int start, int end){
        for (;start<(start+end+1)/2;start++,end--){
            swap(s[start], s[end]);
        }
    }
    string dynamicPassword(string password, int target) {
        int n = password.size();
        //全部反转
        reverse(password,0,n-1);
        //以target为界分别反转
        reverse(password,0,n-target-1);
        reverse(password,n-target,n-1);
        return password;
    }
};

KMP算法!!

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 。
  1. 前缀表

    //计算s中的前后缀相同字符串的长度 (aabaa)(01012)
    void getNext(int *next, string s){
        int j=0; next[0]=0;
        for (int i=1;i<s.size();i++){
            //如果不同则依次寻找相同前缀,直到j为0
            while (j>0&&s[j]!=s[i]){
                j = next[j-1];
            }
            //如果相等则前缀加一
            if (s[j]==s[i]){
                j++;
            }
            //j既可以表示长度,也可以表示相同前缀的后一字符
            next[i]=j;
        }
    }
    
  2. 具体比较

    int strStr(string haystack, string needle) {
            if (needle.size()==0) return 0;
            int next[needle.size()];
        	//得到前缀表
            getNext(next, needle);
            int j=0;
        	//遍历数组
            for (int i=0;i<haystack.size();i++){
                //如果不同,这返回到j-1下标相同前缀的后一字符进行比较,直到j为0
                while (j>0&&haystack[i]!=needle[j]){
                    j = next[j-1];
                }
    			//相同这needle下标加1
                if (haystack[i]==needle[j]){
    
                    j++;
                }
                //退出条件
                if (j==needle.size()) {
                    return (i-needle.size()+1);}
            }
            return -1;
        }
    
    

判断是否为重复字符串

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

原理

假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。

因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,(这里如果不懂,看上面的推理)

所以如果 nx % (n - m)x = 0,就可以判定有重复出现的子字符串。

class Solution {
public:
    //运用kmp算法,数组长度减去最长相同前后缀
    void getNext(int *next, string s){
        int j=0;next[0]=0;
        for (int i=1;i<s.size();i++){
            while (j>0&&s[j]!=s[i]){
                j = next[j-1];
            }

            if (s[j]==s[i]){
                j++;
            }
            next[i] = j;
        }

    }
    bool repeatedSubstringPattern(string s) {
        int n = s.size();
        if (n==1) return false;
        int next[n];
        getNext(next,s);
        //next[n-1]不能为零
        if (next[n-1]!=0 && n%(n-next[n-1])==0) return true;
        return false;
    }
};

栈与队列

//双向队列
deque<int> deq; deq.front();deq.back();
deq.pop_back();deq.pop_front();
deq.push_back();deq.push_front();
//单向队列
queue<int> que;
que.pop();que.push();que.top();
//栈
stack<int> st;
st.pop();st.push();st.top();

栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
class MyQueue {
public:
    MyQueue() {

    }
    //定义进栈和出栈
    stack<int> stIn, stOut;
    void push(int x) {
        stIn.push(x);
    }
    
    int pop() {
        //如何出栈为空,则进栈依次进入出栈
        if (stOut.empty()){
            while (!stIn.empty()){
                stOut.push(stIn.top());
                stIn.pop();
            }
        }
        int result = stOut.top();
        stOut.pop();
        return result;
    }
    
    int peek() {
        int top = this->pop();
        //模拟的队列,记得加回出栈
        stOut.push(top);
        return top;
    }
    
    bool empty() {
        //同时空是为空
        return (stIn.empty() && stOut.empty());
    }
};

队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
//两个队列,一个复制拷贝
class MyStack {
public:
    MyStack() {

    }

    queue<int> que1,que2;

    void push(int x) {
        que1.push(x);
    }
    
    int pop() {
        int size = que1.size();
        //留下一个元素,que2复制que1
        while (--size){
            que2.push(que1.front());
            que1.pop();
        }
        int result = que1.front();
        que1 = que2;
        while (!que2.empty()) que2.pop();
        return result;
    }
    //栈的top对应队列的末尾
    int top() {
        return que1.back();
    }
    
    bool empty() {
        return que1.empty();
    }
};



class MyStack {
public:
    MyStack() {

    }
    queue<int> que;
    void push(int x) {
        que.push(x);
    }
    
    int pop() {
        int size = que.size();
        //出队的队列再次进队
        while (--size){
            que.push(que.front());
            que.pop();
        }
        int result = que.front();
        que.pop();
        return result;
    }
    
    int top() {
        return que.back();
    }
    
    bool empty() {
        return que.empty();
    }
};

有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
输入:s = "()[]{}"        输入:s = "(]"
输出:true				  输出:false
class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        //如果为奇数这错误
        if (s.size()%2 == 1) return false;
        for(int i=0;i<s.size();i++){
            //右括号添加对应的左括号,方便消除
            if (s[i]=='{') {st.push('}');}
            else if (s[i]=='('){ st.push(')');}
            else if (s[i]=='['){ st.push(']');}
            //如果添加左括号找不到对应的或者为空则错误
            else if (st.empty() || st.top()!=s[i]) return false;
            else st.pop();
        }
        if (st.empty()) return true;
        else return false;
    }
};

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

输入:"abbaca"
输出:"ca"
class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> st;
        //不同就进入
        for (char a:s){
            if (st.empty()||st.top()!=a){
                st.push(a);
            }
            //相同则抵消
            else{
                st.pop();
            }
        }
        string result;
        while (!st.empty()){
            result += st.top();
            st.pop();
        }
        //记得倒叙
        reverse(result.begin(),result.end());
        return result;
    }
};

class Solution {
public:
    string removeDuplicates(string s) {
        string result;
        //直接用字符串操作
        for (char a:s){
            if (result.empty()||result.back()!=a){
                result.push_back(a);
            }
            else result.pop_back();
        }
        return result;
    }
};

逆波兰表达式求值

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long>st;
        for (int i=0;i<tokens.size();i++){
            if (tokens[i]=="*"||tokens[i]=="-"||tokens[i]=="+"||tokens[i]=="/"){
                //取出右边两个元素
                long long num2 = st.top();
                st.pop();
                long long num1 = st.top();
                st.pop();
                //加入结果
                if (tokens[i]=="+") {st.push(num1+num2);}
                else if (tokens[i]=="-") {st.push(num1-num2);}
                else if (tokens[i]=="*") {st.push(num1*num2);}
                else if (tokens[i]=="/") {st.push(num1/num2);}
            }
            else {
                //数字直接进入
                st.push(stoll(tokens[i]));
            }
        }
        //返回栈顶元素
    return st.top();
    }
};

滑动列表最大值

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
class Solution {
private:
    //定义单调递减队列
    class MyQueue{
        public:
        deque<int> que;
        void pop(int val){
            //相等则排出
            if (!que.empty()&&que.front()==val){
                que.pop_front();
            }
        }
        
        void push(int val){
            //若前面元素小于val,则前面元素排出
            while(!que.empty()&&que.back()<val){
                que.pop_back();
            }
            que.push_back(val);
        }

        int top(){
            return que.front();
        }

    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> arr;
        MyQueue que;
        //确保最大值为front
        for (int i=0;i<k;i++){
            que.push(nums[i]);
        }
        arr.push_back(que.top());
        for (int i=k;i<nums.size();i++){
            que.pop(nums[i-k]);
            que.push(nums[i]);
            arr.push_back(que.top());
        }
        return arr;
    }
};

//不定义
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        deque<int> que;
        int i=0;
        for (;i<k;i++){
            while (!que.empty()&&que.back()<nums[i]){
                que.pop_back();
            }
            que.push_back(nums[i]);
        }
        result.push_back(que.front());
        for (;i<nums.size();i++){
            if (nums[i-k]==que.front()){
                que.pop_front();
            }
            while (!que.empty()&&que.back()<nums[i]){
                que.pop_back();
            }
            que.push_back(nums[i]);
            result.push_back(que.front());
        }
    return result;
    }

};

二叉树

理论基础
类型
  1. 满二叉树

    image-20231030150320722
  2. 完全二叉树

image-20231030150401474

  1. 二叉搜索树

    image-20231030150714240
  2. 平衡二叉搜索树

    image-20231030150749773
    存储
    1. 链表
    2. 数组 image-20231030150935046
    image-20231030150904531
遍历方式

image-20231030151032960

类定义
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
前中后序遍历(深度)
递归
//前序
void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;//确定遍历终止条件
        vec.push_back(cur->val);    // 中
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
    }
//中序
void traversal(TreeNode* cur, vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    vec.push_back(cur->val);    // 中
    traversal(cur->right, vec); // 右
}
//后序
void traversal(TreeNode* cur, vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
    vec.push_back(cur->val);    // 中
}
迭代
//前序
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        st.push(root);
        TreeNode* cur;
        //中右左放入,先进后出
        while (!st.empty()){
            cur = st.top();
            st.pop();
            if (cur==nullptr) continue;
            result.push_back(cur->val);
            if (cur->right) st.push(cur->right);//右
            if (cur->left) st.push(cur->left);//左
        }
        return result;
    }
};
//中序
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur=root;
        while (cur!=nullptr || !st.empty()){
            //一直向左下迭代
            if (cur!=nullptr){
                st.push(cur);
                cur = cur->left;
            }
            //没有左节点则中节点添加到result中,再向右节点判断
            else{
                cur = st.top();
                result.push_back(cur->val);
                st.pop();
                cur = cur->right;
            }

        }
        return result;
    }
};
//后序
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur=root;
        st.push(cur);
        //中  左右放入
        while(!st.empty()){
            cur = st.top();
            st.pop();
            if (cur==nullptr) continue;
            result.push_back(cur->val);
            //先进后出
            if (cur->left) st.push(cur->left);
            if (cur->right) st.push(cur->right);
        }
        //反转则成为左右中
        reverse(result.begin(), result.end());
        return result;
    }
};
层序遍历(宽度)
//迭代
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
       vector<vector<int>> result;
       queue<TreeNode*> que;
       TreeNode*cur=root; 
       if (root) que.push(root);
        //队列
        while (!que.empty()){
            int size = que.size();
            vector<int> vec;
            //每层循环
            for (int i=0; i<size;i++){
                cur = que.front();
                que.pop();
                vec.push_back(cur->val);
                if (cur->left) que.push(cur->left);
                if (cur->right) que.push(cur->right);
            }
            result.push_back(vec);
        }
    return result;
    }
};

//递归
class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int deepth){
        if (!cur) return;
        //不存在则初始化
        if (result.size()==deepth) result.push_back(vector<int>());
        result[deepth].push_back(cur->val);
        //递归
        if (cur->left) order(cur->left,result,deepth+1);
        if (cur->right) order(cur->right, result, deepth+1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        //deepth作为层数标记
        order(root, result, 0);
        return result;
    }
};
对称二叉树
image-20231031100744048
class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right){
        if (left==nullptr&& right==nullptr) return true;
        else if (left==nullptr&&right!=nullptr) return false;
        else if (left!=nullptr&&right==nullptr) return false;
        else if (left->val!=right->val) return false;
		//外层和内层是否都为1
        return (compare(left->left, right->right)&& compare(left->right, right->left));

    }
    bool isSymmetric(TreeNode* root) {
        if (root==nullptr) return true;
        return compare(root->left, root->right);
    }
};
求二叉树深度
class Solution {
public:
    int getdepth(TreeNode* node){
        if (node==nullptr) return 0;
     	//节点数加一
        return 1+max(getdepth(node->left), getdepth(node->right));
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};
求二叉树节点数
//通用
class Solution {
private:
    int getNodesNum(TreeNode* cur) {
        if (cur == NULL) return 0;
        int leftNum = getNodesNum(cur->left);      // 左
        int rightNum = getNodesNum(cur->right);    // 右
        //累加
        int treeNum = leftNum + rightNum + 1;      // 中
        return treeNum;
    }
public:
    int countNodes(TreeNode* root) {
        return getNodesNum(root);
    }
};

//求完全二叉树
class Solution {
public:
    int cntnode(TreeNode* node){
        if (node==nullptr) return 0;
        int left=0,right=0;
        TreeNode*leftn = node->left;
        TreeNode*rightn = node->right;
        //完全二叉树中子树若为满二叉树则最右和最左深度抑制
        while (leftn){
            left++;
            leftn=leftn->left;
        }
        while (rightn){
            right++;
            rightn=rightn->right;
        }
        //相等则说明是完全二叉树,2*2**left-1
        if (left==right) return (2<<left)-1;
        //不等则左右节点相加
        else return 1+cntnode(node->left)+cntnode(node->right);
    }
    int countNodes(TreeNode* root) {
        return cntnode(root);
    }
};
判断平衡二叉树
image-20231031144023885
class Solution {
public:
    int getdepth(TreeNode* node){
        if (node==nullptr) return 0;
        //左右节点有一个为-1则为-1,每个节点都要判断
        int leftdepth = getdepth(node->left);
        if (leftdepth==-1) return -1;
        int rightdepth = getdepth(node->right);
        if (rightdepth==-1) return -1;
		//若大于一则不是平衡节点,返回-1,否则返回长度
        return (abs(leftdepth-rightdepth)>1) ?-1:1+max(leftdepth, rightdepth);
    }
    bool isBalanced(TreeNode* root) {
        return getdepth(root)==-1?false:true;
    }
};
二叉树所有路径
image-20231031145730980
class Solution {
public:
    vector<string> result;
    //递归函数
    void getpath(TreeNode*node, string path){
        //若为叶节点则将path加入result
        if (!node->left&&!node->right){
            result.push_back(path);
            return;
        }
        //左右节点有一个存在有则可以继续递归
        if (node->left)
        getpath(node->left, path+"->"+to_string(node->left->val));
        if (node->right)
        getpath(node->right, path+"->"+to_string(node->right->val));
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        getpath(root,to_string(root->val));
        return result;
    }
};
左子叶之和
image-20231031150859836
class Solution {
public:
    int sum=0;
    
    void get_left(TreeNode* node, int is_left){
        if (node->left==nullptr&&node->right==nullptr){
            //若if_left为1则加入sum
            if (is_left) sum+=node->val;
            return;
        }
        //左节点标记为1
        if (node->left) get_left(node->left, 1);
        if (node->right) get_left(node->right, 0);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        if (root==nullptr) return 0;
        //根节点不标记
        get_left(root,0);
        return sum;
    }
};
找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

image-20231031161307378
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        int result;
        queue<TreeNode*> que;
        que.push(root);
        //层序遍历
        while (!que.empty()){
            int size = que.size();
            //第一个就是每层的最左边
            result=que.front()->val;
            TreeNode*cur;
            for (int i=0;i<size;i++){
                cur=que.front();
                que.pop();
                if (cur->left) que.push(cur->left);
                if (cur->right) que.push(cur->right);
            }   
        }
        return result;
    }
};
路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
image-20231031173710100
class Solution {
public:
    bool sumpath(TreeNode*node, int sub){
        if (node==nullptr) return false;
        if (node->left==nullptr&&node->right==nullptr){
            //为叶节点且总和为target
            if (sub-node->val==0) return true;
            return false;
        }
        sub = sub-node->val;
        bool rbool = sumpath(node->right, sub); 
        bool lbool = sumpath(node->left, sub);
        //有一个是TRUE就为true
        return rbool||lbool;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        return sumpath(root,targetSum);
    }
};
后序遍历
给定中序和后序数组,构建树
image-20231031203538108
class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
        if (postorder.size()==0) return nullptr;
        //创建根节点
        int root_val = postorder[postorder.size()-1];
        TreeNode* root = new TreeNode(root_val);
        //后序只有一个说明是叶节点
        if (postorder.size()==1) return root;
        //更新长度
        postorder.resize(postorder.size()-1);
        int order_index;
        //找到中序中对应的值
        for (order_index=0;order_index<inorder.size();order_index++){
            if (inorder[order_index]==root_val) break;
        }
		//以根节点为中心分开
        vector<int> leftorder(inorder.begin(),inorder.begin()+order_index);
        vector<int> rightorder(inorder.begin()+order_index+1, inorder.end());
		//和中序长度一致即可
        vector<int> leftpost(postorder.begin(),postorder.begin()+order_index);
        vector<int> rightpost(postorder.begin()+order_index,postorder.end());
		//递归
        root->left=traversal(leftorder, leftpost);
        root->right=traversal(rightorder, rightpost);
        return root;

    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return traversal(inorder, postorder);
    }
};
前序遍历
image-20231031205407503
class Solution {
public:
    TreeNode* traversal(vector<int>& preorder, vector<int>& inorder){
        if (preorder.size()==0) return nullptr;
        //前序是数组起点
        int root_val = preorder[0];
        TreeNode* root = new TreeNode(root_val);
        if (preorder.size()==1) return root;
        int inidex;
        for (inidex=0;inidex<inorder.size();inidex++){
            if (inorder[inidex]==root_val) break;
        }
        vector<int> left_in(inorder.begin(),inorder.begin()+inidex);
        vector<int> right_in(inorder.begin()+inidex+1,inorder.end());
        //0下标记得跳过
        vector<int> left_pre(preorder.begin()+1,preorder.begin()+1+inidex);
        vector<int> right_pre(preorder.begin()+inidex+1,preorder.end());
        root->left=traversal(left_pre,left_in);
        root->right=traversal(right_pre,right_in);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return traversal(preorder,inorder);
    }
};
最大二叉树
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
image-20231031211137502
class Solution {
public:
    TreeNode* traversal(vector<int>& vec){
        int n = vec.size();
        if (n==0) return nullptr;
        int j=0;
        //找出最大值
        for (int i=1;i<n;i++){
            if (vec[j]<vec[i]) j=i;
        }
        TreeNode* root = new TreeNode(vec[j]);
        if (n==1) return root;//叶节点
        //以最大值为分割
        vector<int> left_vec(vec.begin(), vec.begin()+j);
        root->left = traversal(left_vec);
        vector<int> right_vec(vec.begin()+j+1,vec.end());
        root->right = traversal(right_vec);
        return root;
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return traversal(nums);
    }
};
合并二叉树
image-20231101103258449
class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        //有一个为空则返回另一个,都为空则返回nullptr
        if (root1==nullptr) return root2;
        if (root2==nullptr) return root1;
        //都不为空,值相加
        TreeNode* root = new TreeNode(root2->val+root1->val);
        //前序遍历
        root->left = mergeTrees(root1->left, root2->left);
        root->right = mergeTrees(root2->right, root1->right);
        return root;
    }
};
二叉树搜索
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root==nullptr) return nullptr;
        if (root->val==val) return root;
        //右节点大于根节点
        else if (root->val<val) return searchBST(root->right,val);
        //左节点小于根节点
        else return searchBST(root->left,val);
    }
};
验证二叉树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
class Solution {
public:
    //中序遍历
    void insort(TreeNode*cur, vector<int>& arr){
        if (cur==nullptr) return;
        insort(cur->left, arr);
        arr.push_back(cur->val);
        insort(cur->right, arr);
    }

    bool isValidBST(TreeNode* root) {
        vector<int> arr;
        insort(root, arr);
        //若不递增则为FALSE
        for (int i=1;i<arr.size();i++){
            if (arr[i]<=arr[i-1]) return false;
        }
        return true;
    }
};

//second
//递归中序
class Solution {
public:
    long long maxval = LONG_MIN;//从左到右记录最大值
    bool isValidBST(TreeNode* root) {
        if (root==nullptr) return true;//终止条件
        //左中右
        bool left = isValidBST(root->left);
        if (maxval<root->val) maxval=root->val;//若左边比右边大则返回false
        else return false;
        bool right = isValidBST(root->right);
        return left&&right;//判断左右节点是否都为搜索二叉树
    }
};
二叉树中的众数
class Solution {
public:
    //中序遍历
    void invec(TreeNode* node, vector<int>& vec){
        if (node==nullptr) return;
        invec(node->left,vec);
        vec.push_back(node->val);
        invec(node->right,vec);
    }
    vector<int> findMode(TreeNode* root) {
        vector<int> vec;
        invec(root, vec);
        int max_time = 1;
        int cur_time = 1;
        vector<int> result;
        //遍历vec,出现不同就更新上一元素的出现评率,若为最大则更新最大值,并清零数组再添加元素
        for (int i=1;i<vec.size();i++){
            if (vec[i]==vec[i-1]) {
                cur_time++;
            }
            else {
                if (cur_time>max_time){
                    result.clear();
                    result.push_back(vec[i-1]);
                    max_time=cur_time;
                    
                }
                else if (cur_time==max_time){
                    result.push_back(vec[i-1]);   
                }
            cur_time=1;
            }
        }
        //注意,数组末端元素要独立判断一次
        if (cur_time>max_time){
            result.clear();
            result.push_back(vec[vec.size()-1]);
        }
        else if (cur_time==max_time) result.push_back(vec[vec.size()-1]);
    return result;
    }
};
二叉树的最近公共祖先
image-20231102090043217
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //最先出现目标节点向上移动
        if (root==p||root==q||root==NULL) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p,q);
        TreeNode* right = lowestCommonAncestor(root->right, p,q);
        //都不为空说明目标节点在左右子树,返回根节点
        if (left!=NULL&&right!=NULL) return root;
        //有一个为空则说明两个目标节点都在另一个子树上
        else if (left==NULL&&right!=NULL) return right;
        else if (left!=NULL&&right==NULL) return left;
        else return NULL;
    }
};
搜索二叉树的最近公共组训
class Solution {
public:
    //搜索二叉树左右节点大小可以确定
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root->val>p->val&&root->val>q->val){
            //都大于则祖先在左边
            TreeNode* left = lowestCommonAncestor(root->left, p,q);
            if (left!=NULL) return left;
        }
        都小于祖先在右边
        if (root->val<p->val&&root->val<q->val){
            TreeNode* right = lowestCommonAncestor(root->right,p,q);
            if (right!=NULL) return right;
        }
        //第一个位于中间的一定是最近祖先,搜索二叉树的性质可以看出
        return root;

    }
};
二叉树插入节点
class Solution {
public:
    void insert(TreeNode* node, int val){
        //节点存在就递归下去,不存在就直接添加新节点
        if (val>node->val){
            if (node->right==nullptr)
            {node->right = new TreeNode(val);
            }
            else insert(node->right, val);
            return;
        }
        if (val<node->val){
            if (node->left==nullptr)
            {node->left = new TreeNode(val);
            return;}
            else insert(node->left,val);
            return;
        }

    }
        
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        //为空就创建一个树
        if (root==nullptr) return new TreeNode(val);
        insert(root,val);
        return root;

    }
};

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root==nullptr) return new TreeNode(val);
        if (val<root->val) root->left = insertIntoBST(root->left, val);
        if (val>root->val) root->right = insertIntoBST(root->right, val);
        return root;
    }
};
删除搜索二叉树节点
image-20231105094620109
class Solution {
public:
    //递归函数
    TreeNode* delete1(TreeNode* node, int key){
        //遍历到空节点就直接返回
        if (node==nullptr) return node;
        //找到值就继续删除操作
        if (node->val==key){
            //左右有一个为空另一个直接接上
            if (node->right!=nullptr&&node->left==nullptr){
                TreeNode* ret = node->right;
                delete node;
                return ret;
            }
            if (node->right==nullptr&&node->left!=nullptr){
                TreeNode* ret = node->left;
                delete node;
                return ret;
            }
            //都为空就返回空节点
            if (node->left==nullptr&&node->right==nullptr){
                delete node;
                return nullptr;
            }
            //都不为空就让右节点接到左节点最右下方
            if (node->left!=nullptr&&node->right!=nullptr){
                TreeNode* cur = node->left;
                while (cur->right!=nullptr){
                    cur = cur->right;
                }
                cur->right = node->right;
   				//记得删除操作
                TreeNode* ret = node->left;
                delete node;
                return ret;
            }
        }
        //判断目标节点位置
        else if (node->val>key){
            node->left = delete1(node->left,key);
        }
        else node->right = delete1(node->right,key);
        //返回
        return node;
    }

    TreeNode* deleteNode(TreeNode* root, int key) {
        root = delete1(root, key);
        return root;
    }
};
修剪二叉树
image-20231105114619176
class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root==nullptr) return nullptr;
        //小于则递归右节点,直到出现正常的右节点
        if (root->val<low){
            TreeNode* right = trimBST(root->right, low, high);
            return right;
        }
        //大于则递归左节点
        if (root->val>high){
            TreeNode* left = trimBST(root->left,low,high);
            return left;
        }
        //没问题就向下递归
        root->left = trimBST(root->left,low,high);
        root->right =  trimBST(root->right, low, high);
        return root;
    }
};
将递增数组转化成二叉树
image-20231105204050836
class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int n = nums.size();
        //数组只有一个元素直接返回节点
        if (n==1) return new TreeNode(nums[0]);
        //没有元素就返回空
        if (n==0) return nullptr;
        int midd = nums[n/2];
        //以中间元素作为头节点可以确保左右子树节点数最大相差1,是平衡二叉树
        TreeNode* root=new TreeNode(midd);
        //引用需要创建变量
        //以中间节点为界左右分开
        vector<int> left_nums(nums.begin(),nums.begin()+n/2);
        root->left = sortedArrayToBST(left_nums);
        vector<int> right_nums(nums.begin()+n/2+1,nums.end());
        root->right = sortedArrayToBST(right_nums);
        return root;
    }
};
二叉树转化成累加树
image-20231105205919860
class Solution {
public:
    int sum=0;
    //递归,右中左遍历
    void changeVal(TreeNode* node){
        if (node==nullptr) return;
        changeVal(node->right);
        sum+=node->val;
        node->val= sum;
        changeVal(node->left);
    }
    TreeNode* convertBST(TreeNode* root) {
        changeVal(root);
        return root;
    }
};

回溯

组合问题

输入:n = 4, k = 2                          输入:n = 1, k = 1
输出:                                       输出:[[1]]
[ [2,4],                  
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4], ]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    //回溯函数
    void backtrack(int n, int k, int start){
        //终止条件
        if (path.size()==k){
            result.push_back(path);
            return;
        }
        //优化:for (int i=start;i<=n-(k-path.size())+1;i++)  优化(例如当k=4,n=4时,若start=2,则不可能完成4个元素)
        for (int i=start;i<=n;i++){
            path.push_back(i);
            backtrack(n,k,i+1);
            //回溯
            path.pop_back();
        }

    }
    vector<vector<int>> combine(int n, int k) {
        backtrack(n,k,1);
        return result;
    }
};

组合累和

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
class Solution {
public:
    vector<int> path;
    vector<vector<int>> resulr;
    void backstrack(int k, int sub, int start){
        //若小于零说明累加已经大于目标值了
        if (sub<0) return;
        //等于则返回
        if (k==path.size()){
            //目标值
            if (sub==0)
                resulr.push_back(path);
            return;
        }
        //起点不能大于剩余值
        int top = 9<sub?9:sub; 
        //保留空余位置
        for (;start<=top-(k-path.size())+1;start++){
            path.push_back(start);
            backstrack(k, sub-start, start+1);
            path.pop_back();
        }

    }
    vector<vector<int>> combinationSum3(int k, int n) {
        backstrack(k,n,1);
        return resulr;
    }
};
电话号码组合
image-20231103162337259
class Solution {
public:
    vector<string> result;
    string path;
    const string letterMap[10] = {
    "", // 0
    "", // 1
    "abc", // 2
    "def", // 3
    "ghi", // 4
    "jkl", // 5
    "mno", // 6
    "pqrs", // 7
    "tuv", // 8
    "wxyz", // 9
};
    void backstrack(string digits, int index, int k){
        int num = digits[index]-'0';//string转出int
        //终止条件
        if (path.size()==k) {
            result.push_back(path);
            return;}
        for (int i=0;i<letterMap[num].size();i++){
            path.push_back(letterMap[num][i]);
            backstrack(digits, index+1, k);
            path.pop_back();//回溯
        }

    }
    vector<string> letterCombinations(string digits) {
        if (digits.size()==0) return result;
        backstrack(digits, 0, digits.size());
        return result;
    }
};
数组组合(可重复)
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtrack(vector<int> target, int index,int sub){
        if (sub==0){ result.push_back(path); return;
        }
        //sub-target[i]>=0剪枝
        for (int i=index;i<target.size()&&sub-target[i]>=0;i++){
            path.push_back(target[i]);
            //继续从i开始
            backtrack(target, i,sub-target[i]);
            path.pop_back();
        }
        

    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        //从小到大排序,若加上candidates[i]已经超过target了,后面就不用进行了
        sort(candidates.begin(),candidates.end());
        backtrack(candidates, 0,target);
        return result;
    }
};
数组组合(可重复)||
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtrack(vector<int>& vec, int i,int sub){
        if (sub==0){
            result.push_back(path);
            return;
        }
        for (int index = i;index<vec.size()&&sub-vec[index]>=0;index++){
            //避免出现重复的组合
            if (index>i&&vec[index]==vec[index-1]) continue;
            path.push_back(vec[index]);
            backtrack(vec,index+1,sub-vec[index]);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        //排序,方便剪枝
        sort(candidates.begin(),candidates.end());
        backtrack(candidates, 0,target);
        return result;
    }
};
分割回文字符串
输入:s = "aab"  			            输入:s = "a"						                  输出:[["a","a","b"],["aa","b"]]        输出:[["a"]]
class Solution {
public:
    vector<vector<string>> result;
    vector<string> path;
    //判断是否是回文字符串
    bool is_s(string s){
        for (int i=0,j=s.size()-1;i<(s.size()/2);i++,j--){
            if (s[i]!=s[j]) return false;
        }
        return true;
    }
    void dfs(string s){
        int n = s.size();
        if (n==0){
            result.push_back(path);
            return;
        }
        //从首字符开始判断前缀是否为回文,是则递归下去
        for (int i=1;i<=n;i++){
            if (is_s(string(s.begin(),s.begin()+i))){
                path.push_back(string(s.begin(),s.begin()+i));
                dfs(string(s.begin()+i,s.end()));
                path.pop_back();
            }
        }

    }
    vector<vector<string>> partition(string s) {
        dfs(s);
        return result;
    }
};
复原ip地址
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
示例 1:                                            示例 2:
输入:s = "25525511135"                             输入:s = "0000"
输出:["255.255.11.135","255.255.111.35"]           输出:["0.0.0.0"]                         
class Solution {
public:
    vector<string> result;//返回值
    string path;//记录字符串
    void dfs(string& s, int start, int depth){
        //如果下标大于
        if (start>s.size()) return;
        //说明分割了4份了
        if (depth==4){
            //遍历完全了
            if (start==s.size()){
                //记得删掉后面小数点
                path.erase(path.end()-1);
                result.push_back(path);
                //加上,统一操作
                path+='.';    
            }
        return;
        }
        int n = s.size()-start;
        //剪枝。每份最小一个数字,最大三个数字
        if (n<(4-depth)||n>3*(4-depth)) return;
        
        if (start<s.size()&& s[start]=='0'){
            //不能用下标索引
            path+="0.";
            dfs(s,start+1,depth+1);
            //删除加上小数点
            path.erase(path.end()-2,path.end());
            return;
        }
        int i=1;
        for (;i<3;i++){
            if (start+i>s.size()) return;
            string pathone(s.begin()+start,s.begin()+start+i);
            path+=pathone+'.';
            dfs(s,start+i,depth+1);
            path.erase(path.end()-i-1,path.end());
        }
        //三位数数字才可能大于255
        if (start+i>s.size()) return;
        string pathone(s.begin()+start,s.begin()+start+i);
        if (stoi(pathone)<=255){
            path+=pathone+'.';
            dfs(s,start+i,depth+1);
            path.erase(path.end()-i-1,path.end());
        }
    }
    vector<string> restoreIpAddresses(string s) {
        dfs(s,0,0);
        return result;
    }
};
求子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3],   [1],   [2],   [1,2,3],   [1,3],   [2,3],   [1,2],   [] ]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void dfs(vector<int>& vec, int start){
        //加上空集
        result.push_back(path);
        for (;start<vec.size();start++){
        //每次长度减一,元素不重复取
        path.push_back(vec[start]);
        dfs(vec, start+1);
        path.pop_back();}
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(nums, 0);
        return result;
    }
};
求子集||
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void dfs(vector<int>& nums, int start){
        result.push_back(path);
        for (int i=start;i<nums.size();i++){
            //多加一个判断即可
            if (i>start&&nums[i]==nums[i-1]) continue;
            path.push_back(nums[i]);
            dfs(nums,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        //排序,确保重复元素相邻
        sort(nums.begin(),nums.end());
        dfs(nums,0);
        return result;
    }
};
递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
输入: [4, 6, 7, 7]
输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    //判断是否为递增数列
    bool is_add(vector<int>& nums){
        for (int i=1;i<nums.size();i++){
            if (nums[i-1]>nums[i]) return false;
        }
        return true;
    }
    void dfs(vector<int>& nums, int start){
        //剪枝,不是递增直接返回
        if (!is_add(path)) return;
        if (path.size()>1) result.push_back(path);
        //记录该层出现的元素
        unordered_set<int> used;
        for (int i=start;i<nums.size();i++){
            //防止重复元素出现
            if (used.find(nums[i])!=used.end())continue;
            used.insert(nums[i]);
            path.push_back(nums[i]);
            dfs(nums,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        dfs(nums,0);
        return result;
    }
};
全排列
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void dfs(vector<int>& nums, vector<int>& used){
        //相等则添加,并返回
        if (path.size()==nums.size()){
            result.push_back(path);
            return;
        }
        //用used记录已使用的数字
        for (int i=0;i<nums.size();i++){
            if (used[i]==1) continue;
            used[i]=1;
            path.push_back(nums[i]);
            dfs(nums,used);
            //回溯
            used[i]=0;
            path.pop_back();
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> used(nums.size(),0);
        dfs(nums,used);
        return result;
    }
};
全排列||
输入:nums = [1,1,2]
输出:[[1,1,2],[1,2,1],[2,1,1]]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void dfs(vector<int>& nums,vector<int>& used){
        if (path.size()==nums.size()){
            result.push_back(path);
            return;
        }
        for (int i=0;i<nums.size();i++){
            //重复且前一个数字没被使用过才跳过
            if (used[i]==1||(i>0&&used[i-1]==0 &&nums[i]==nums[i-1])) continue;
            used[i]=1;
            path.push_back(nums[i]);
            dfs(nums,used);
            used[i]=0;
            path.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<int> used(nums.size(),0);
        dfs(nums,used);
        return result;
    }
};
安排行程
给你一份航线列表 `tickets` ,其中 `tickets[i] = [fromi, toi]` 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 `JFK`(肯尼迪国际机场)出发的先生,所以该行程必须从 `JFK` 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

- 例如,行程 `["JFK", "LGA"]` 与 `["JFK", "LGB"]` 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

img

输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出:["JFK","MUC","LHR","SFO","SJC"]
class Solution {
public:
    //出发地点:[目的地:票数]
    unordered_map<string, map<string, int>> target;
    vector<string> result;
    bool dfs(int all_num){
        //票数加一即为总路程
        if (result.size()==all_num) return true;
        //result末尾就是出发地,遍历该出发点到达的目的地
        for (auto& to_map:target[result[result.size()-1]]){
            //前提目的地可以到达
            if (to_map.second>0){
                to_map.second--;
                result.push_back(to_map.first);
                //满足条件直接返回
                if (dfs(all_num)) return true;
                to_map.second++;
                result.pop_back();
            }
        }
    return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        for (const auto& ticket:tickets){
            target[ticket[0]][ticket[1]]++;
        }
        result.push_back("JFK");
        dfs(tickets.size()+1);
        return result;
    }
};
N皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
image-20231109203104410
class Solution {
public:
    vector<vector<string>> result;
    vector<string> path;
    void dfs(vector<int> vec, int row, int n){
        //终止条件
        if (row==n){
            result.push_back(path);
            return;
        }
        //每列尝试
        for (int i=0;i<n;i++){
            int sign = 1;
            for (int j=0;j<vec.size();j++){
                //不能在斜下方||不能在同一列
                if (abs(vec[j]-i)==(row-j)||vec[j]==i){
                    sign = 0;
                    //满足一个条件都不成立
                    break;
                }}
            if (sign){
                path[row][i]='Q';
                vec.push_back(i);
                //确保不会出现在同一行
                dfs(vec,row+1,n);//回溯
                vec.pop_back();
                path[row][i]='.';
            }
            }
        }
    
    vector<vector<string>> solveNQueens(int n) {
        path = vector<string>(n,string(n,'.'));
        vector<int> vec;
        dfs(vec,0,n);
        return result;
    }
};
解数读
image-20231110113744554
class Solution {
public:
    //检验
    bool check(vector<vector<char>>& board, int row, int col,char val){
        //列
        for (int i=0;i<9;i++){
            if (i==row) continue;
            if (board[i][col]==val) return false;
        }
        //行
        for (int i=0;i<9;i++){
            if (i==col) continue;
            if (board[row][i]==val) return false;
        }
        //九宫格中的
        int startr = (row/3)*3; int startc = (col/3)*3;
        for (int i=startr;i<startr+3;i++){
            for (int j = startc;j<startc+3;j++){
                if (i==row&&j==col) continue;
                if (board[i][j]==val) return false;
            }
        }
        return true;
    }
    //二次递归,用循环来控制方向
    bool dfs(vector<vector<char>>& board){
        for (int i=0;i<9;i++){
            for (int j=0;j<9;j++){
                //有数字就跳过
                if (board[i][j]!='.') continue;
                //1-9重复遍历
                for (char c='1';c<='9';c++){
                //符合就递归
                if (check(board, i, j,c)){
                    board[i][j] = c;
                    //dfs为TRUE说明数字已满且都符合要求
                    if (dfs(board)) return true;
                    //回溯
                    board[i][j] = '.';
                }
                }
                //九个数尝试了都没结果就直接返回
                return false;
            }
        }
        return true;
    }
    void solveSudoku(vector<vector<char>>& board) {
        dfs(board);
    }
};

贪心

分配饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int cnt =0;
        //每个饼干分配给胃口满足且最小的
        for (int i=0;i<s.size();i++){
            if (cnt<g.size()&&g[cnt]<=s[i]) cnt++;
        }
        return cnt;
    }
};
摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

image-20231108221932138

注意:必须取最大的峰值,不然结果数偏小

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        if (n==1) return 1;
        int i = 1;
        //若开头相同则以最后一个相同数作为起始点
        while (i<n&&nums[i]==nums[0]) i++;
        //全部相同则返回1
        if (i==n) return 1;
        int cnt=1;
        int num = nums[0];
        //以sign作为判断,若前面是小于则后面应该是大于
        int sign = nums[0]<nums[i]?1:-1;
        for (;i<n;i++){
            if (sign*num<sign*nums[i]){
                num=nums[i];
                cnt+=1;
                sign*=-1;
            }
            //替换直到到达峰值
            else num=nums[i];
        }
        return cnt;
    }
};
最大字序列和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int cnt = 0;
        int result=INT_MIN;
        //从左开始遍历
        for (int i=0;i<nums.size();i++){
            //记录累加值
            cnt+=nums[i];
            //更新
            if (result<cnt) result=cnt;
            //若累加值小于0,就置零
            if (cnt<0) cnt=0;
        }
        return result;
    }
};
股票买入卖出
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。总利润为 4 + 3 = 7 。
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int cnt=0;
        //只要数值上升就记录,累加
        for (int i=1;i<prices.size();i++){
            if (prices[i]>prices[i-1]){
                cnt += prices[i]-prices[i-1];
            }
        }
        return cnt;


    }
};
跳跃游戏
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
//遍历每次能到达的最大下标
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int finall = nums[0];
        if (nums.size()==1) return true;
        int i =1;
        for (;i<=finall;i++){
            //更新最大下标
            finall = max(finall, nums[i]+i);
            if (finall>=nums.size()-1) return true;
        }
        return false;
    }
};
跳跃游戏||
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:   0 <= j <= nums[i];  i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位
class Solution {
public:
    //每次到达之前最大下标就加一,然后判断当前最大下标能不能到达末尾
    int jump(vector<int>& nums) {
        if (nums.size()==1) return 0;
        int n=0;
        int cur_cover=0,cover = 0;
        int i=0;
        for (;i<=cover;i++){
            cover = max(cover,i+nums[i]);
            if (i==cur_cover){
                n++;
                cur_cover=cover;
                if (cur_cover>nums.size()-2) break;
            }

        }
        
        return n;
    }
};
K 次取反后最大化的数组和
给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。

示例 1:

输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。
class Solution {
public:
 //绝对值大小从大到小
static bool cmp(int a,int b){
        return abs(a)>abs(b);
    }
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end(), cmp);
        //依次将最大的负数转为正数
        for (int i=0;i<nums.size();i++){
            if (nums[i]<0 && k>0){
                nums[i]*=-1;
                k--;
            }
        }
        //正数中反复将最小值取负
        if (k%2) nums[nums.size()-1]*=-1;
        int sum=0;
        for (int i:nums){
            sum+=i;
        }
        return sum;
    }
};
加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

示例 1:

输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        //记录剩余油量
        int sum_s=0;
        for (int i =0;i<gas.size();i++){
            gas[i]-=cost[i];
            sum_s+=gas[i];
        }
        //剩余油量小于0说明无论如何启动都不够油
         if (sum_s<0) return -1;
        int cur=0;
        int j=-1;
        //从左到右遍历,cur记录以j起点累加值,若cur<0则j重置
        for (int i=0;i<gas.size();i++){
            if (cur ==0&&cur+gas[i]>=0){
                j=i;
            }

            cur+= gas[i];

            if (cur<0){ cur=0;j=-1;}
        }
        return j;
    }
};

//方法二
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        //sum_g记录剩余油量,min记录遍历出现过的最小值
        int sum_g=0;int min=INT_MAX;
        int n = gas.size();
        for (int i=0;i<n;i++){
            gas[i]-=cost[i];
            sum_g+=gas[i];
            if (sum_g<min){
                min=sum_g;
            }
        }
        //剩余油量小于零,说明无论如何启动都不够油
        if (sum_g<0) return -1;
        //说明从0下标开始没有出现过不够油的情况
        if (min>-1) return 0;
        int i=n-1;
        //因为最小值只能在前面出现,从右到左遍历,若最小值大于等于0则说明从该下标开始可以一直循环下去
        for (;i>-1;i--){
            min += gas[i];
            if (min>=0) break;
        }
        return i;
    }
};

的位置,跳 1 步,然后跳 3 步到达数组的最后一个位




```c++
class Solution {
public:
    //每次到达之前最大下标就加一,然后判断当前最大下标能不能到达末尾
    int jump(vector<int>& nums) {
        if (nums.size()==1) return 0;
        int n=0;
        int cur_cover=0,cover = 0;
        int i=0;
        for (;i<=cover;i++){
            cover = max(cover,i+nums[i]);
            if (i==cur_cover){
                n++;
                cur_cover=cover;
                if (cur_cover>nums.size()-2) break;
            }

        }
        
        return n;
    }
};
K 次取反后最大化的数组和
给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。

示例 1:

输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。
class Solution {
public:
 //绝对值大小从大到小
static bool cmp(int a,int b){
        return abs(a)>abs(b);
    }
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end(), cmp);
        //依次将最大的负数转为正数
        for (int i=0;i<nums.size();i++){
            if (nums[i]<0 && k>0){
                nums[i]*=-1;
                k--;
            }
        }
        //正数中反复将最小值取负
        if (k%2) nums[nums.size()-1]*=-1;
        int sum=0;
        for (int i:nums){
            sum+=i;
        }
        return sum;
    }
};
加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

示例 1:

输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        //记录剩余油量
        int sum_s=0;
        for (int i =0;i<gas.size();i++){
            gas[i]-=cost[i];
            sum_s+=gas[i];
        }
        //剩余油量小于0说明无论如何启动都不够油
         if (sum_s<0) return -1;
        int cur=0;
        int j=-1;
        //从左到右遍历,cur记录以j起点累加值,若cur<0则j重置
        for (int i=0;i<gas.size();i++){
            if (cur ==0&&cur+gas[i]>=0){
                j=i;
            }

            cur+= gas[i];

            if (cur<0){ cur=0;j=-1;}
        }
        return j;
    }
};

//方法二
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        //sum_g记录剩余油量,min记录遍历出现过的最小值
        int sum_g=0;int min=INT_MAX;
        int n = gas.size();
        for (int i=0;i<n;i++){
            gas[i]-=cost[i];
            sum_g+=gas[i];
            if (sum_g<min){
                min=sum_g;
            }
        }
        //剩余油量小于零,说明无论如何启动都不够油
        if (sum_g<0) return -1;
        //说明从0下标开始没有出现过不够油的情况
        if (min>-1) return 0;
        int i=n-1;
        //因为最小值只能在前面出现,从右到左遍历,若最小值大于等于0则说明从该下标开始可以一直循环下去
        for (;i>-1;i--){
            min += gas[i];
            if (min>=0) break;
        }
        return i;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值