【剑指Offer】16-20题(快速幂实现pow()+递归全排列生成1-9999(n个9)+删除链表的节点+字符串DP+暴力解决表示数值的字符串)

🌴数值的整数次方

剑指 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);
    }
};

🌴打印从1到最大的n位数

剑指 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主画图的方式,清晰明确。

image-20220707160810953

推导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;
    }
};
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢乙醇的四氯化碳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值