LeetCode数据结构与算法学习Day03

这篇博客介绍了LeetCode中涉及的数据结构与算法题目,包括滑动窗口最大值、队列的最大值、字符串转整数、斐波那契数列、青蛙跳台阶问题、正则表达式匹配和连续子数组的最大和等,详细解析了解题思路,探讨了动态规划、优先队列和栈等解题方法。
摘要由CSDN通过智能技术生成


题目引用自

https://leetcode-cn.com/

图解数据结构与算法

每日学习Day03学习笔记

59 I 滑动窗口最大值

题目描述:

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

解题思路:

1、使用双层for循环,第一层为创建窗口,第二层为遍历找最大值,时间复杂度过高,对于数据量巨大的测试用例会造成时间超时。
2、利用min函数栈的思想实现。
3、优先队列实现。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        if(n <= 0){
            vector<int>p;
            return p;
        }
        priority_queue<pair<int, int>> q;
        for (int i = 0; i < k; ++i) {
            q.emplace(nums[i], i);
        }
        vector<int> ans = {q.top().first};
        for (int i = k; i < n; ++i) {
            q.emplace(nums[i], i);
            while (q.top().second <= i - k) {
                q.pop();
            }
            ans.push_back(q.top().first);
        }
        return ans;
    }
};
执行用时:
16 ms, 在所有 C++ 提交中击败了95.36%的用户
内存消耗:
16.7 MB, 在所有 C++ 提交中击败了6.99%的用户

59 II 队列的最大值

题目描述:

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

解题思路:

本题个人认为与min栈有相同之处,解题思路为维护一个保存最大值的队列,当弹入元素时,对队列尾部元素判断,弹出尾部元素直到尾部元素不小于该值的时候停止。弹出元素时进行判断,弹出值是否为队列头部元素,是的话一起弹出,如果不是则最大值队列不作弹出动作。

class MaxQueue {
public:
    MaxQueue() {

    }
    
    int max_value() {
        return q_max.empty()?-1:q_max.front();
    }
    
    void push_back(int value) {
        q_s.push(value);
        while(!q_max.empty()&&q_max.back()<value)
            q_max.pop_back();
        q_max.push_back(value);
    }
    
    int pop_front() {
        if(q_s.empty())
            return -1;
        int tp =  q_s.front();
        q_s.pop();
        if(tp == q_max.front())
        {
            q_max.pop_front();
        }
        return tp;
    }
    queue<int>q_s;
    deque<int>q_max;
};
执行用时:
148 ms, 在所有 C++ 提交中击败了38.17%的用户
内存消耗:
47.4 MB, 在所有 C++ 提交中击败了98.63%的用户

67 把字符串转换成整数

题目描述:

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

解题思路:

1、直接对每个字符进行判断处理,同时拼接前进行越界判断。
2、利用状态机判断(最近着重在看这部分,试着实现了一下,反而效率比第一种方法低,可能是由于if判断过多?不太确定)

//状态机
class Solution {
public:
	enum stats {
		START,
		BLANK,
		SIGNALP,
		SIGNALD,
		NUMBER
	};
	enum chartype {
		intnum,
		signp,
		signd,
		blank,
		other
	};
	chartype whattype(char c) {
		if (c == ' ')
			return blank;
		else if (c >= '0' && c <= '9')
			return intnum;
		else if (c == '+')
			return signp;
		else if (c == '-')
			return signd;
		else
			return other;
	}

	int strToInt(string str) {
		unordered_map<stats, unordered_map<chartype, stats>>dp{
			{
				START,{
					{blank,BLANK},{signp,SIGNALP},
					{intnum,NUMBER},{signd,SIGNALD},
				}
			},
			{
				BLANK,{
					{blank,BLANK},{signp,SIGNALP},
					{intnum,NUMBER},{signd,SIGNALD},
				}
			},
			{
				NUMBER,{
					{intnum,NUMBER},
				}
			},
			{
				SIGNALP,{
					{intnum,NUMBER},
				}
			},
			{
				SIGNALD,{
					{intnum,NUMBER},
				}
			},
		};
		int length = str.length();
		stats ss = START;
		bool flag = false;
		long long sum = 0;
		for (int i = 0; i < length; ++i) {
			chartype tp = whattype(str[i]);
			if (dp[ss].find(tp) == dp[ss].end())
				if (sum == 0)
					return 0;
				else
					break;
			else
				ss = dp[ss][tp];
			if (ss == SIGNALD)
				flag = true;
			else if (ss == NUMBER)
			{
				sum = sum * 10 + str[i] - '0';
				if (sum > INT_MAX)
				{
					if (flag)
						return INT_MIN;
					return INT_MAX;
				}								
			}
		}
		if (flag)
			sum = -sum;
		return sum;
	}
};
执行用时:
20 ms, 在所有 C++ 提交中击败了24.39%的用户
内存消耗:
9.5 MB, 在所有 C++ 提交中击败了5.03%的用户
class Solution {
public:
    int strToInt(string str) {
        int res = 0, bndry = INT_MAX / 10;
        int i = 0, sign = 1, length = str.size();
        if(length == 0) return 0;
        while(str[i] == ' ')
            if(++i == length) return 0;
        if(str[i] == '-') sign = -1;
        if(str[i] == '-' || str[i] == '+') i++;
        for(int j = i; j < length; j++) {
            if(str[j] < '0' || str[j] > '9') break;
            if(res > bndry || res == bndry && str[j] > '7')
                return sign == 1 ? INT_MAX : INT_MIN;
            res = res * 10 + (str[j] - '0');
        }
        return sign * res;
    }
};
执行用时:
0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:
6.1 MB, 在所有 C++ 提交中击败了61.68%的用户

10 I 斐波那契数列

题目描述:

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

解题思路:

1、利用推导推出Fn = 3Fn-3 + 2Fn-4,递归求出结果。
2、利用动态规划。

class Solution {
public:
    int fib(int n) {
        int a = 0,b = 1,sum;
        for(int i = 0;i<n;++i){
            sum = (a+b)% 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
};
执行用时:
0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:
5.8 MB, 在所有 C++ 提交中击败了58.29%的用户

10 II 青蛙跳台阶问题

题目描述:

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

解题思路:

与上述题目一致,利用动态规划。

class Solution {
public:
    int numWays(int n) {
        int a = 1,b = 1,sum;
        for(int i = 0;i<n;++i)
        {
            sum = (a+b) %1000000007;
            a = b;
            b = sum;
        }
        return a;

    }
};

19 正则表达式匹配

题目描述:

请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

解题思路:

利用动态规划,判断出s[i]与p[j]能否匹配的条件,写出程序。

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();

        auto matches = [&](int i, int j) {
            if (i == 0) {
                return false;
            }
            if (p[j - 1] == '.') {
                return true;
            }
            return s[i - 1] == p[j - 1];
        };

        vector<vector<int>> f(m + 1, vector<int>(n + 1));
        f[0][0] = true;
        for (int i = 0; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (p[j - 1] == '*') {
                    f[i][j] |= f[i][j - 2];
                    if (matches(i, j - 1)) {
                        f[i][j] |= f[i - 1][j];
                    }
                }
                else {
                    if (matches(i, j)) {
                        f[i][j] |= f[i - 1][j - 1];
                    }
                }
            }
        }
        return f[m][n];
    }
};
执行用时:
4 ms, 在所有 C++ 提交中击败了92.49%的用户
内存消耗:
6.9 MB, 在所有 C++ 提交中击败了29.85%的用户

42 连续子数组的最大和

题目描述:

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。

解题思路:

由于连续子数组的最大和可视为数组当前元素与前一个元素的关系,如果前一个元素<0,则对当前元素而言是负收益,而如果前一个元素>0,则对当前元素而言是正收益,可将两数相加后存入原位。同时初始时将第0位数字保存起来,每次都与其判断其中最大值。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res = nums[0];
        for(int i = 0,j = i+1;j <nums.size();++i,++j)
        {
            if(nums[i] > 0)
                nums[j] += nums[i];
            if(nums[j] > res)
                res = nums[j];
        }
        return res;
    }
};
执行用时:
28 ms, 在所有 C++ 提交中击败了41.56%的用户
内存消耗:
22.4 MB, 在所有 C++ 提交中击败了69.27%的用户
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值