力扣
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;
}
};