剑指 Offer 16. 数值的整数次方 - 力扣(LeetCode)
模拟pow的实现
比如求3^1000;1000D==001111101000B
一个一个乘效率太低了,时间按复杂度是O(N),而且还会超时,
你问我怎么知道的
利用快速幂,时间复杂度是O(logn)。
以31000 ,1000=512 +256+128+64 +32+8。
所以31000=3512 · 3256 · 3128· 364 332 ·38 。其中512,256,128等这些数字对应的是 001111101000B 中为1的位置。
所以实现思路就出来了,找到指数的二进制中为1的那几位,将其累乘即可。又因为这些都是二的倍数所以可以通过1,2,4,8,16……这样的办法快速拿到。比如知道了22 就可以知道24 ,从而知道了28,这样就可以一直递推下去,而且是以指数的方式增长,效率很高
推荐用这种,好理解。
class Solution {
public:
double myPow(double x, int n) {
if(x==1) return 1;
if(x==0) return 0;
long long times=n;//n可能是int范围内最小的负数,如果不这样处理,将n转成正数的时候就可能溢出
double base=x;
if(times<0)
{
times=-times;
base=1/base;
}
double res=1;
while(times)
{
if(times&1)//相当于times%2==1
{
res*=base;
}
base*=base;
times>>=1;//相当于times/=2,但是位运算更快
}
return res;
}
};
class Solution {
public:
double quickMul(double x, long long n)
{
if(n==0)
{
return 1;
}
double ans=quickMul(x,n>>1);//算出x的n/2次方
return n&1?ans*ans*x:ans*ans;
}
double myPow(double x, int n) {
long long times=n;
if(times<0)
{
return 1/quickMul(x,-times);
}
return quickMul(x,times);
}
};
剑指 Offer 17. 打印从1到最大的n位数 - 力扣(LeetCode)
需要考虑大数,力扣上面是简单题因为不用考虑大数,但是书上是要求考虑大数的。
铺垫:全排列+递归生成数字序列,以打印的形式打印出来。
递归思想:先固定第一位,再去固定第二位…(力扣大佬题解里的图很清晰表述了这个过程)
int start;
int nine = 0;
void dfs(vector<int>& nums,int x,int length)//nine和start的引入是为了去除前导0
{
if (x == length )
{
for (int i=start;i<nums.size();i++)
{
cout << nums[i];
}
cout << endl;
if (nine + start == length)//当打印的是9,99,999..这样的数时说明下一次打印需要多打印一位了,start前移
{
start--;
}
return;
}
for (int i = 0; i <= 9; i++)
{
if (i == 9)
{
nine++;
}
nums[x] = i;
dfs(nums, x + 1, length);
}
nine--;//回溯 保证每一层的9的个数不会累加,而是这一层结束立马变回原样,nine变为2是在099时。
}
#define N 4
int main()
{
vector<int>nums;
nums.resize(N);
start = N - 1;
dfs(nums, 0, N);
return 0;
}
改一下上面的代码提交力扣
class Solution {
public:
vector<int> printNumbers(int n) {
vector<int>nums(n);
dfs(nums,0,n);
return ret;
}
vector<int>ret;
int start;
int nine = 0;
void dfs(vector<int>& nums, int x, int length)//nine和start的引入是为了去除前导0
{
if (x == length)
{
string res;
for (int i = start; i < nums.size(); i++)
{
res+=(nums[i]+'0');
}
int x=stoi(res);//这里最好直接转成整数判断去掉0,用字符串可能出现判断“0”和“00”这样的
//我的vs上判断的字符串if(res!="0")可以去掉0,但在力扣上并不行,所以转成整数判断了
if(x)
{
ret.push_back(x);
}
if (nine + start == length)
{
start--;
}
return;
}
for (int i = 0; i <= 9; i++)
{
if (i == 9)
{
nine++;
}
nums[x] = i;
dfs(nums, x + 1, length);//固定第x+1位。
}
nine--;//回溯 保证每一层的9的个数不会累加,而是这一层结束立马变回原样,nine变为2是在099时。
}
};
简单题,改变指针指向即可。
剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(head->val==val)
{
head=head->next;
return head;
}
ListNode* cur=head->next;
ListNode* prev=head;
while(cur!=NULL)
{
if(cur->val==val)
{
prev->next=cur->next;
return head;
}
prev=cur;
cur=cur->next;
}
return head;
}
};
83. 删除排序链表中的重复元素 - 力扣(LeetCode)
简单题,和上面一样的双指针
链表是有序的,所以重复的数是连续的,所以我们遍历一遍链表就可以做到删掉所有的重复元素。
/**
* 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) {
if(head==nullptr||head->next==nullptr)
{
return head;
}
ListNode* prev=head;
ListNode* cur=head->next;
while(cur!=nullptr)
{
if(prev->val==cur->val)
{
prev->next=cur->next;
cur=cur->next;
}
else
{
prev=cur;
cur=cur->next;
}
}
return head;
}
};
剑指 Offer 19. 正则表达式匹配 - 力扣(LeetCode)
经典字符串DP。
我是跟着下面这个视频推的
https://www.bilibili.com/video/BV1Br4y1v7SA?spm_id_from=333.337.top_right_bar_window_history.content.click&vd_source=68554a723b373e22a24bbc1d3c5ec4c1
可以学习up主画图的方式,清晰明确。
推导DP的状态方程思想是:(?*) 这种组合可以匹配0个、1个、2个、…n个 ?字符,这里的 ?字符表示任意一个字符。
class Solution {
public:
bool isMatch(string s, string p) {
//多开一个字节的大小,因为dp[0][0]表示空串,这种状态也需要存储起来
int sz_s=s.size()+1;
int sz_p=p.size()+1;//最后直接返回dp[sz_s-1][sz_p-1],因为即使s和p是空串,也开了空间存储
//dp[i][j]表示前i个字符和前j个字符是否匹配
vector<vector<bool>>dp(sz_s,vector<bool>(sz_p));
dp[0][0]=1;
for(int i=2;i<sz_p;i+=2)
{
if(p[i-1]=='*')//当s为空串时进行特殊处理
{
dp[0][i]=dp[0][i-2];
}
}
for(int i=1;i<sz_s;i++)
{
for(int j=1;j<sz_p;j++)
{
if(p[j-1]=='*')
{
//j=1时不可能到这,因为*前面肯定还有一个字符,即*最小都是第二个字符
//这个公式的推导的思想是(?*)这种组合可以匹配0个、1个、2个、n个等等
//我自己是下跟着B站视频推的,代码自己写的
dp[i][j]=(dp[i][j-2])
||(dp[i-1][j]&&(s[i-1]==p[j-2]||p[j-2]=='.'));
}
else
{
dp[i][j]=dp[i-1][j-1]&&(s[i-1]==p[j-1]||p[j-1]=='.');
}
}
}
return dp[sz_s-1][sz_p-1];
}
};
剑指 Offer 20. 表示数值的字符串 - 力扣(LeetCode)
没有人比我更懂暴力
class Solution {
public:
bool isNumber(string s) {
int start=0;
int end=s.size()-1;
while(s[start]==' ')
{
start++;
}
while(end>start&&s[end]==' ')
{
end--;
}
if(start==s.size())
{
return false;//全空格
}
if(s[end]=='e'||s[end]=='E'||s[end]=='+'||s[end]=='-') return false;
if(s[start]=='e'||s[start]=='E') return false;
bool flag=false;//看有没有小数点
bool flag2=false;//看E后面有没有小数点
bool flag3=false;//看里面有无数字
for(int i=start;i<=end;i++)
{
if(s[i]==' ') return false;
if(s[i]>='0'&&s[i]<='9') flag3=true;
if(s[i]>='a'&&s[i]<='z'&&s[i]!='e') return false;
if(s[i]>='A'&&s[i]<='Z'&&s[i]!='E') return false;
if(i+1<=end&&s[i]=='+'&&s[i+1]=='+') return false;
if(i+1<=end&&s[i]=='+'&&s[i+1]=='-') return false;
if(i+1<=end&&s[i]=='-'&&s[i+1]=='+') return false;
if(i+1<=end&&s[i]=='-'&&s[i+1]=='-') return false;
if(i-1>=0&&(s[i]=='+'||s[i]=='-')&&(s[i-1]>='0'&&s[i-1]<='9')) return false;
if(s[i]=='.'&&flag) return false;
if((s[i]=='e'||s[i]=='E')&&flag2) return false;
if(i+1<=end&&s[i]=='.'&&(s[i+1]=='+'||s[i+1]=='-'))return false;
if(s[i]=='.'&&flag2) return false;
if(i+1<=end&&s[i]=='.'&&(s[i+1]=='e'||s[i+1]=='E')&&flag3==false) return false;
if(i-1>=0&&(s[i]=='e'||s[i]=='E')&&(s[i-1]=='+'||s[i-1]=='-')) return false;
if(s[i]=='.') flag=true;
if(s[i]=='e'||s[i]=='E') flag2=true;
}
return flag3;
}
};