力扣刷题整理

力扣

20210321题号:173_矩阵置零

题目描述

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
进阶:
一个直观的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个仅使用常量空间的解决方案吗?

方法一:代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        vector<vector<int>> signnum;
        for(int i=0;i<matrix.size();i++){
            for(int j=0;j<matrix[0].size();j++){
                if(matrix[i][j]==0)signnum.push_back({i,j});
            }
        }
        for(int i=0;i<signnum.size();i++){
            for(int j=0;j<matrix[0].size();j++){
                matrix[signnum[i][0]][j]=0;
            }
            for(int j=0;j<matrix.size();j++){
                matrix[j][signnum[i][1]]=0;
            }
        }
    }
};

空间复杂度:O(mn*2)

方法二:代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int row=0,col=0;
        int n=matrix.size();
        int m=matrix[0].size();
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(matrix[i][j]==0){
                    matrix[0][j]=0;
                    matrix[i][0]=0;
                    if(i==0)row=1;
                    if(j==0)col=1;
                }
            }
        }
        for(int i=1;i<n;i++){
            if(matrix[i][0]==0){
                for(int j=0;j<m;j++){
                    matrix[i][j]=0;
                }
            }
            
        }
        for(int j=1;j<m;j++){
            if(matrix[0][j]==0){
                for(int i=0;i<n;i++){
                    matrix[i][j]=0;
                }
            }
        }
        if(matrix[0][0]==0){
            if(col==1){
            for(int i=1;i<n;i++){
                matrix[i][0]=0;
            }
            }
            if(row==1){
            for(int j=1;j<m;j++){
                matrix[0][j]=0;
            }
            }
        }
    }
};

空间复杂度:O(1)

结果内存消耗依旧和之前的13MB差别不大??
在这里插入图片描述

20210322题号:191_位1的个数

题目描述

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。

方法一:除法
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int sum=0;
        while(n>0){
            if(n%2==1)sum++;
            n/=2;
        }
        return sum;
    }
};
方法二:位运算
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int sum=0;
        while(n>0){
            sum++;
            n&=n-1;
        }
        return sum;
    }
};

位运算相比于除法速度等快。

20210323题号:341. 扁平化嵌套列表迭代器

题目描述

给你一个嵌套的整型列表。请你设计一个迭代器,使其能够遍历这个整型列表中的所有整数。
表中的每一项或者为一个整数,或者是另一个列表。其中列表的元素也可能是整数或是其他列表。

解题思路

此题的嵌套列表如果需要遍历一遍的话,明显递归的方法。这里的hasnext和next两个方法,明显需要在类中保存遍历的状态。如果不改变嵌套列表则无法在每次完成一次get函数之后保存当时的状态。题目叫“扁平化”,那么不如直接在初始化的时候直接将其遍历结果计算存入到一个一维数组当中。把它当一维数组来遍历。

扁平化
/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * class NestedInteger {
 *   public:
 *     // Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     bool isInteger() const;
 *
 *     // Return the single integer that this NestedInteger holds, if it holds a single integer
 *     // The result is undefined if this NestedInteger holds a nested list
 *     int getInteger() const;
 *
 *     // Return the nested list that this NestedInteger holds, if it holds a nested list
 *     // The result is undefined if this NestedInteger holds a single integer
 *     const vector<NestedInteger> &getList() const;
 * };
 */

class NestedIterator {
    vector<int> iList;
    int n=0;
    int size;
public:
    NestedIterator(vector<NestedInteger> &nestedList) {
        int n = nestedList.size();
        for(int i=0;i<n;i++){
            calList(nestedList[i]);
        }
        size = iList.size();
    }

    void calList(NestedInteger &nestedInteger){
        if(nestedInteger.isInteger()){
            iList.push_back(nestedInteger.getInteger());
        }else{
            vector<NestedInteger> cList = nestedInteger.getList();
            int n =cList.size();
            for(int i=0;i<n;i++){
                calList(cList[i]);
            }
        }
    }

    int next() {
        return iList[n++];
    }
    
    bool hasNext() {
        return n<iList.size());
    }
};

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i(nestedList);
 * while (i.hasNext()) cout << i.next();
 */

20210324题号:456. 132模式

题目描述

给你一个整数数组 nums ,数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成,并同时满足:i < j < k 和 nums[i] < nums[k] < nums[j] 。
如果 nums 中存在 132 模式的子序列 ,返回 true ;否则,返回 false 。

  • 进阶:很容易想到时间复杂度为 O(n^2) 的解决方案,你可以设计一个时间复杂度为 O(n logn) 或 O(n) 的解决方案吗?
解题思路

利用单调栈解题,关于单调栈之后会做详细的归纳整理。

方法一:
class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int max=0;
        int n=nums.size();
        vector<int> dp(n);
        vector<int> nmax;
        dp[0]=0;
        for(int i=1;i<n;i++){
            if(nums[i]>nums[dp[i-1]]||nums[i]>nums[i-1]){
                dp[i]=dp[i-1];
                if(nums[max]<nums[i])max=i;
                if(nums[i]<nums[i-1])return true;
                for(int j=0;j<nmax.size();j++){
                    if(nums[i]<nums[nmax[j]]&&nums[i]>nums[dp[nmax[j]]])return true;
                }
            }
            else{
                dp[i]=i;
                nmax.push_back(max);
                max=i;
            }
        }
        return false;
    }
};

20210325题号:82. 删除排序链表中的重复元素 II

题目描述

存在一个按升序排列的链表,给你这个链表的头节点 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* deleteDuplicates(ListNode* head) {
        ListNode* p = head;
        ListNode* temp = head;
        ListNode* pre=nullptr;//第一个不同数值的点
        ListNode* re = head;
        while(p&&p->next){
            temp = p;
            p = p->next;
            if(temp->val==p->val){
                while(temp->val==p->val&&p->next){
                    p = p->next;
                }
                if(pre!=nullptr){
                    if(p->next||temp->val!=p->val){
                        pre->next=p;
                        p=pre;
                    }else pre->next=nullptr;
                }
                else{
                    if(p->next||temp->val!=p->val)re=p;
                    else re=nullptr;        
                }
            } else if(temp->val!=p->val)pre=temp;
        }
        return re;
    }
};

20210326题号:83. 删除排序链表中的重复元素

题目描述

存在一个按升序排列的链表,给你这个链表的头节点 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* deleteDuplicates(ListNode* head) {
        ListNode* p=head;
        while(p&&p->next){
            while(p->next&&p->next->val==p->val){
                p->next=p->next->next;
            }
            p=p->next;
        }
        return head;
    }
};

20210327题号:61. 旋转链表

题目描述

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

解答
/**
 * 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* rotateRight(ListNode* head, int k) {
        if(head==nullptr) return head;
        ListNode* p = head;
        int h=1;
        while(p->next){
            p = p->next;
            h++;
        }
        p->next=head;
        for(int i=1;i<h-(k%h);i++){
            head=head->next;
        }
        p=head;
        head=head->next;
        p->next=nullptr;
        return head;
    }
};

20210328题号:173. 二叉搜索树迭代器

题目描述

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

解答
/**
 * 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 BSTIterator {
public:
    stack<TreeNode*> trace;
    bool isok=false;
    BSTIterator(TreeNode* root) {
        trace.push(root);
    }
    
    int next() {
        TreeNode* cur=trace.top();
        if(isok==true) {
            trace.pop();
            if(cur->right){
                isok=false;
                trace.push(cur->right);
            }
            return cur->val;
        }
        while(cur->left){
            cur=cur->left;
            trace.push(cur);
        }
        trace.pop();
        isok=true;
        if(cur->right){
             isok=false;
             trace.push(cur->right);
        } 
        return cur->val;
    }
    
    bool hasNext() {
        if(trace.empty())return false;
        return true;
    }
};

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator* obj = new BSTIterator(root);
 * int param_1 = obj->next();
 * bool param_2 = obj->hasNext();
 */

20210329题号:190. 颠倒二进制位

题目描述

颠倒给定的 32 位无符号整数的二进制位。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。
进阶:
如果多次调用这个函数,你将如何优化你的算法?

解答
class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t m=pow(2,31);
        // cout<<m<<endl;
        uint32_t re=0;
        while(n>0){
            if(n%2==1)re+=m;
            n/=2;
            m/=2;
        }
        return re;
    }
};
总结:关于位运算

其实按理来说遇到这种二进制数的题(也包括一些其他题),使用位运算是首选方法,但是本人对位运算一直不太熟悉,在此做个总结。

那么为什么会有位运算?二进制的作用什么呢?二进制自然是用来在计算机当中存储数据,而它的反码和补码,存在的意义是为了做减法,也就是为了处理负数。
这里只说明一下有符号数,有符号数的高位为符号标志位。0为正1为负。在计算机当中负数的存储为它的补码形式(即反码+1),而正数是其本身(正数反码和补码都是其本身,但取反是一个运算操作,并不代表正数取反不变!

位运算的种类
  • &与运算:都为 1 时,结果才为 1,否则为 0。
  • |或运算:有一个为1,则为1,否则为0。
  • ^异或运算:不相同结果为1,否则为0。
  • ~取反运算:0变1,1变0。
  • 左移运算:向左进行移位操作,高位丢弃,低位补 0。
  • 右移运算:向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补同符号位。

位运算有什么奇技淫巧? - 力扣(LeetCode)的回答 - 知乎

位运算解法1
class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t ans=0;
        //进制的本质
        int i=32;
        while(i--)
        {
            ans<<=1;
            ans+=n&1;
            n>>=1;
        }
        return ans;
    }
};
位运算解法2:位运算分治

若要翻转一个二进制串,可以将其均分成左右两部分,对每部分递归执行翻转操作,然后将左半部分拼在右半部分的后面,即完成了翻转。

由于左右两部分的计算方式是相似的,利用位掩码和位移运算,我们可以自底向上地完成这一分治流程。
在这里插入图片描述
对于递归的最底层,我们需要交换所有奇偶位:

取出所有奇数位和偶数位;
将奇数位移到偶数位上,偶数位移到奇数位上。
类似地,对于倒数第二层,每两位分一组,按组号取出所有奇数组和偶数组,然后将奇数组移到偶数组上,偶数组移到奇数组上。以此类推。

需要注意的是,在某些语言(如Java)中,没有无符号整数类型,因此对 nn 的右移操作应使用逻辑右移。

class Solution {
private:
    const uint32_t M1 = 0x55555555; // 01010101010101010101010101010101
    const uint32_t M2 = 0x33333333; // 00110011001100110011001100110011
    const uint32_t M4 = 0x0f0f0f0f; // 00001111000011110000111100001111
    const uint32_t M8 = 0x00ff00ff; // 00000000111111110000000011111111

public:
    uint32_t reverseBits(uint32_t n) {
        n = n >> 1 & M1 | (n & M1) << 1;
        n = n >> 2 & M2 | (n & M2) << 2;
        n = n >> 4 & M4 | (n & M4) << 4;
        n = n >> 8 & M8 | (n & M8) << 8;
        return n >> 16 | n << 16;
    }
};

20210330题号:74. 搜索二维矩阵

题目描述

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

解答
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        //二分查找法
        int m=matrix.size();
        int n=matrix[0].size();
        int i=0,j=m*n-1,mid;
        while(i<=j){
            mid=(i+j)/2;
            // cout<<matrix[mid/n][mid%n]<<endl;
            if(matrix[mid/n][mid%n]==target)return true;
            else if(matrix[mid/n][mid%n]>target)j=mid-1;
            else i=mid+1;
        }
        return false;
    }
};
总结

奇怪的是,我运行了AC中的全遍历方法,也就是时间复杂度为O(mn)的运行时间竟然是0ms,而二分查找O(logmn)却用了8ms。
当然总结这部分主要想记录以下lower_bound和upper_bound两个c++标准库函数。

lower_bound:运用于有序区间中,以二分查找为基础,返回在不破坏数组顺序的情况下,不小于目标的第一个元素。也就是说,以这个函数可以查出来和目标相等的数字。

lower_bound() 函数定义在头文件中,其语法格式有 2 种,分别为:

//在 [first, last) 区域内查找不小于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

upper_bound:运用于有序区间中,以二分查找为基础,返回在不破坏数组顺序的情况下,大于目标的第一个元素。也就是说,以这个函数不可以查出来和目标相等的数字,和目标值相等的只有可能是其前一个。

upper_bound() 函数定义在头文件中,用于在指定范围内查找大于目标值的第一个元素。该函数的语法格式有 2 种,分别是:

//查找[first, last)区域中第一个大于 val 的元素。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

20210331题号:90. 子集 II

题目描述

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

解题思路

循环遍历数组,则当前的数字分为两种情况
第一次出现
直接并入到每一个目前已经有集合情况中。
第n(n>1)次出现
在目前已经有的集合中,选取出现当前数字n-1次的集合进行并入。

解答
class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> re={{}};
        vector<int> flag(21,0);
        int n=nums.size();
        for(int i=0;i<n;i++){
            int bound=flag[nums[i]+10]++;
            int rsize = re.size();
            if(bound==0){//第一次出现
                for(int j=0;j<rsize;j++){
                    vector<int> temp=re[j];
                    temp.push_back(nums[i]);
                    re.push_back(temp);
                }
            }
            else{
                for(int j=0;j<rsize;j++){
                    int msize=re[j].size();
                    int count=0;
                    for(int k=0;k<msize;k++){
                        if(re[j][k]==nums[i])
                            count++;
                    }
                    if(count==bound){
                        vector<int> temp=re[j];
                        temp.push_back(nums[i]);
                        re.push_back(temp);
                    }
                }
            }
        }
        return re;
    }
};
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问的关键。需要仔细分析目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值