leetcode刷题之HOT100简单题

(C++)

21. 合并两个有序链表

链接:21. 合并两个有序链表
迭代难点:内存申请的时机,next的指向

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1==nullptr) return list2;
        if(list2==nullptr) return list1;

        ListNode* newlist=new ListNode();
        ListNode* newHead=newlist;
        // newlist.val=(list1.val<list2.val)?list1.val:list2.val;

        while(list1!=nullptr && list2!=nullptr){
            // newlist=new ListNode();//在循环内申请空间对空链表友好
            if(list1->val<=list2->val){
                newlist->val=list1->val;
                list1=list1->next;
            }else{
                newlist->val=list2->val;
                list2=list2->next;
            }
            // cout<<newlist->val<<" "<<list1->val<<" "<<list2->val<<endl;
            newlist->next=new ListNode();
            newlist=newlist->next;
        }
        // cout<<newlist->val<<" "<<list2->val<<endl;
        if(list1!=nullptr) {
            newlist->val=list1->val;
            newlist->next=list1->next;

        }
        if(list2!=nullptr) {
            newlist->val=list2->val;
            newlist->next=list2->next;
        }
        return newHead;
    }
};

101. 对称二叉树

101. 对称二叉树
进阶:你可以运用递归迭代两种方法解决这个问题吗?
迭代:

/**
* 迭代的方法
*/
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        queue<TreeNode*> ql, qr;
        ql.push(root->left);
        qr.push(root->right);

        while (!ql.empty() && !qr.empty()) {
            TreeNode* l = ql.front(); ql.pop();
            TreeNode* r = qr.front(); qr.pop();
            if (l == nullptr && r == nullptr) {
                continue;  // 左右子树都为空,继续检查下一层
            }
            if (l == nullptr || r == nullptr || l->val != r->val) {
                return false;  // 左右节点有一个为空或者值不相等,则不是轴对称
            }
            ql.push(l->left);
            qr.push(r->right);
            ql.push(l->right);
            qr.push(r->left);
        }
        // 队列长度必须同时为空,否则不是对称的
        return ql.empty() && qr.empty();
    }
};

递归:

/**
* 递归的方法
*/
class Solution {
public:
    bool check(TreeNode* l,TreeNode* r){
    	//如果两个节点都为空,这两个节点对称
        if(l==nullptr && r==nullptr) return true;
        //其中 一方空一方不空,不对称;值不同不对称
        if(l==nullptr || r==nullptr || l->val != r->val) return false;
        //最终的结果为左子树和右子树都对称
        return check(l->left,r->right) && check(l->right,r->left);
    }
    bool isSymmetric(TreeNode* root) {
        TreeNode* l=root->left;
        TreeNode* r=root->right;
        return check(l,r);    
    }
};

104. 二叉树的最大深度

链接:104. 二叉树的最大深度
方法:递归

/**
 * 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 maxDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        if(root->left==nullptr && root->right==nullptr) return 1;
        return max(maxDepth(root->left),maxDepth(root->right))+1; 
    }
};

160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

题目数据保证整个链式结构中不存在环。
注意,函数返回结果后,链表必须保持其原始结构

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

思路1(似乎不对):快慢指针
AB链表都分为长度为偶数、奇数两种情况:

  1. 如果AB长度都为偶数:偶数一次走两步 则只会出现在
  2. 如果AB长度一奇一偶:奇数一次走两步、偶数一次走一步,一定会遇到
  3. 如果AB长度都为奇数:一步和两步都能遍历链表中的所有元素,

思路2(通过):将地址放进set中。分别遍历两个链表,首次出现一样地址的就是答案。
时间O(m+n)、空间O(m+n)。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        set<ListNode*> s;//指针类型的set 学到了
        ListNode *p=headA;
        while(p!=NULL){
            s.insert(p);
            p=p->next;
        }
        p=headB;
        while(p!=NULL){
            if(s.find(p)==s.end())
                s.insert(p);
            else
                return p;
            p=p->next;
        }
        return p;
    }
};

思路3(通过):双指针。如果AB相交,假设从相交的地方划分开,公共段称为l,AB分别可以分为la+l和lb+l。要想判断出相交 就要创造出两个指针同时指着同一个地方的情景。即使公式两边相等:la+l+()=lb+l+()。往两边添加不同的项la+l+(lb)=lb+l+(la),公式成立。
对应两个指针分别从headA、headB出发,到达链表末尾后交换,指针相等的地方恰好是相交处。
时间O(m+n),空间O(1)。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA==NULL || headB==NULL) return NULL;

        ListNode *pa=headA;
        ListNode *pb=headB;
        while(pa!=NULL && pb!=NULL){//由于循环开始时没有判断null的情况 所以需要在前面判断
            pa=pa->next;
            pb=pb->next;
        }//在某一个链表遍历完成后会退出循环
        if(pa==NULL) pa=headB;
        if(pb==NULL) pb=headA;

        while(pa!=NULL && pb!=NULL){
            if(pa==pb) return pa;
            pa=pa->next;
            pb=pb->next;
        }
        if(pa==NULL) pa=headB;
        if(pb==NULL) pb=headA;
        
        while(pa!=NULL && pb!=NULL){
            if(pa==pb) return pa;
            pa=pa->next;
            pb=pb->next;
        }
        return NULL;
    }
};
  • 代码优化后:先统计长度,再对齐指针
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) return nullptr;

        ListNode *longer = headA, *shorter = headB;
        int lenA = 0, lenB = 0;
        // 计算两个链表的长度
        while (longer) {
            ++lenA;
            longer = longer->next;
        }
        longer = headA; // 重置longer为headA,因为在计算长度时已遍历至nullptr
        
        while (shorter) {
            ++lenB;
            shorter = shorter->next;
        }
        shorter = headB; // 重置shorter为headB
        
        // 调整指针,使较长链表的指针先走差值步
        if (lenA > lenB) {
            for (int i = 0; i < lenA - lenB; ++i) longer = longer->next;
        } else {
            for (int i = 0; i < lenB - lenA; ++i) shorter = shorter->next;
        }
        // 同时遍历两个链表直到找到交点或都为空
        while (longer != shorter) {
            longer = longer->next;
            shorter = shorter->next;
        }
        return longer; // 或shorter,因为此时两者相等
    }
};

136. 只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间

思路:要么1次要么2次 这个次数很特殊
题解:异或运算

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        //要么1次要么2次 这个次数很特殊啊
        //线性时间复杂度 O(n)
        //如果用桶的话 空间复杂度可能会超 桶大小=6e4
        //set/map
        //排序时间复杂度O(nlogn) 不需要额外空间
        //ok final 异或运算
        int result=0;
        for(int i=0;i<nums.size();i++){
             result=result^nums[i];
        }
        return result;
    }
};

141. 环形链表

链接:141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
思路1:哈希 时间O(n)空间O(n)
思路2:快慢指针 时间O(n)空间O(1)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==nullptr) return false;

        ListNode* pre=head->next;
        ListNode* last=head;
        while(pre != last){
            //前面的指针走到末尾
            if(pre==nullptr || pre->next==nullptr) return false;
            //前面的可走则后面一定可走
            pre=pre->next->next;
            last=last->next;
        }
        //一定有(pre==last) 
        return true;
    }
};

169. 多数元素

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
方法1:hashmap时间复杂度为 O(n)、空间复杂度为 O(n)

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        //多数元素最多只存在1个
        //hashMap
        int n=nums.size()/2;
        map<int,int> m;
        for(auto& val:nums){
            if(m.find(val)==m.end()){
                m[val]=1;
            }else{
                m[val]=m[val]+1;
            }
            if(m[val]>n){
                return val;
            }
        }
        return -1;
    }
};

方法2:摩尔投票 时间复杂度为 O(n)、空间复杂度为 O(1)
题解链接:https://leetcode.cn/problems/majority-element/solutions/2362000/169-duo-shu-yuan-su-mo-er-tou-piao-qing-ledrh/
当n1 != x时,抵消的所有数字中 n1占一半 非n1占一半,那么x有多少个?最少0个最多也就是一半。
所以前面的部分中x占比小于等于一半 则在后面的部分 x占比一定大于一半,仍为后面的众数,也即“缩小剩余数组区间”。
摩尔投票流程:
假设首位为众数x,票数总计votes=0.
遍历中投票数=0 ——》当前数字设为x,并一直按照投票规则投票;
返回最终的x。
摩尔投票的正确性: 一开始很怀疑摩尔投票的正确性,因为总觉得有特殊反例的存在。
官方题解指出——投票数votes一定非负,因为每当开始新的一轮计数第一个数的存在使得votes>=1,而当votes=0时刷新x,开始新一轮的投票于是votes又是从1开始。
以及后面一段结束时count==value的证明也很精彩。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        //摩尔投票
        int n=nums.size()/2;
        int votes=0;
        int x=-1;
        for(auto& val:nums){
            if(votes==0){
                x=val;
                votes=1;
            }else{
                if(x==val) votes++;
                else    votes--;
            }
        }
        return x;
    }
};

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
**进阶:**链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
思路:遍历的同时头插法创立链表

递归:

/**
 * 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* headInsert(ListNode* head,int num){
    //     ListNode* newhead=new ListNode(num,head);
    //     return newhead;
    // }
    ListNode* reverseList(ListNode* head) {
        ListNode* p=head;
        ListNode* newhead=nullptr;
        while(p!=nullptr){
            newhead=new ListNode(p->val,newhead);
            p=p->next;
        }
        return newhead;
    }
};

迭代:

/**
 * 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* headInsert(ListNode* head,int num){
    //     ListNode* newhead=new ListNode(num,head);
    //     return newhead;
    // }
    ListNode* reverseList(ListNode* head) {
        ListNode* p=head;
        ListNode* newhead=nullptr;
        while(p!=nullptr){
            newhead=new ListNode(p->val,newhead);
            p=p->next;
        }
        return newhead;
    }
};

226. 翻转二叉树

链接:226. 翻转二叉树
方法:递归
思路:将大问题拆解成小问题。要做的事情对于每个节点也就是将左右指针的指向交换。

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

        TreeNode* tmp=root->left;
        root->left=invertTree(root->right);
        root->right=invertTree(tmp);

        return root;
    }
};

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
**进阶:**你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路1:放进数组中判断回文。O(n) 时间复杂度、O(n) 空间复杂度。
思路2:要求空间O(1)的话,找出前一半与后一半的对应关系,多段遍历时间可能O(n^2)。
题解链接🔗:官方题解
题解思路1:空间O(1)+对称,使用函数递归栈。

题解思路2:改变链表结构然后再还原回去。

class Solution {
private:
    ListNode* reverseList(ListNode* head,ListNode* tail){
        if(head==tail) {
            head->next=nullptr;
            return head;
        }

        ListNode* idx=head->next;
        ListNode* pre=head;
        ListNode* nxt;
        while(idx!=nullptr && pre!=tail){
            nxt=idx->next;
            idx->next=pre;
            pre=idx;
            idx=nxt;
        }
        head->next=nullptr;
        return tail;
    }
public:
    bool isPalindrome(ListNode* head) {
        if(head==nullptr||head->next==nullptr) return true;

        //反转前一半的链表然后比较前后
        ListNode* tail=head;
        int len=1;
        while(tail->next!=nullptr){
            tail=tail->next;
            len++;
        }
       
        ListNode* half=head;
        for(int i=1;i<len/2;i++){
            half=half->next;
        }
        ListNode* rear;
        if(len%2==0)
            rear=half->next;
        else
            rear=half->next->next;


        reverseList(head,half);

        ListNode* pre=half;
        // while(pre!=nullptr){
        //     cout<<pre->val<<" ";
        //     pre=pre->next;
        // }
        // cout<<endl;
        // while(rear!=nullptr){
        //     cout<<rear->val<<" ";
        //     rear=rear->next;
        // }
        while(pre!=nullptr && rear!=nullptr){
            // cout<<pre->val<<" "<<rear->val<<endl;
            if(pre->val!=rear->val){
                reverseList(head,half);
                half->next=rear;
                return false;
            }
            pre=pre->next;
            rear=rear->next;
        }
        return true;
    }
};

543. 二叉树的直径

二叉树的直径是指树中任意两个节点之间最长路径的长度。这条路径可能经过也可能不经过根节点root。
思路:两个根节点之间一定有唯一一个最大公共祖先(不一定是root)。

/**
 * 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 depth(TreeNode* root){
        if(root==nullptr) return 0;
        // if(root->left==nullptr&&root->right==nullptr) return 1;
        // if(root->left==nullptr) return 
        return max(depth(root->left),depth(root->right))+1;

    }
    int diameterOfBinaryTree(TreeNode* root) {
        if(root==nullptr) return 0;
        //左空 右空
        if(root->left==nullptr&&root->right==nullptr) return 0;
        //左空 右不空
        if(root->left==nullptr) return max(diameterOfBinaryTree(root->right),depth(root->right));
        //左不空 右空
        if(root->right==nullptr) return max(diameterOfBinaryTree(root->left),depth(root->left));
        //左有 右有
        return max(max(diameterOfBinaryTree(root->left),diameterOfBinaryTree(root->right)) ,depth(root->left)+depth(root->right));
    }
};

其他思路: 关于“任意两点间的距离”,
如果路径经过node,则maxlen=maxlen(经过左)+maxlen(经过右)
如果不经过node,则maxlen=maxlen(经过左)和maxlen(经过右)中更大的那个。
方法:DFS


617. 合并二叉树

链接:617. 合并二叉树

/**
 * 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* mergeNodes(TreeNode* node1, TreeNode* node2){
        if(node1==nullptr && node2==nullptr) return nullptr;
        if(node1==nullptr) return node2;
        if(node2==nullptr) return node1;
        //递归的部分卡壳了 主要在考虑是否需要申请新空间
        //以下代码对node1进行补充
        node1->val +=node2->val;
        node1->left=mergeNodes(node1->left,node2->left);
        node1->right=mergeNodes(node1->right,node2->right);
        return node1;
        // else return new pair<TreeNode*,TreeNode*>(node1,node2);
    }
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        return mergeNodes(root1,root2);
    }
};
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值