滑动窗口算法-day3 (定长滑窗)

1.定长子串中元音的最大数目

题目

解析

1.解析

  • 定长滑动窗口三步走:
  • 1.指针 i 不断右移,直到窗口长度为 K;
  • 2.更新窗口,一般为最大值 / 最小值;
  • 3.移动窗口,检查出窗口元素,改变相应的变量;
  • 时间复杂度:O(n);空间复杂度:O(1);

代码

class Solution {
public:
    int maxVowels(string s, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = s.size();
        int ans = 0;
        int vowel = 0; // 当前元音个数
        
        for(int i = 0;i < n;i ++){
            // 进入窗口
            if(s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u'){
                vowel ++;
            }

            // 窗口长度不够 k
            // 当 i = k - 1 后,后面的窗口就固定长度了
            if(i < k - 1) continue; 

            // 每移动一次,更新一次
            ans = max(ans,vowel);

            // 离开窗口
            char out = s[i - k + 1];
            if(out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u'){
                vowel --;
            }
        }

        return ans;
    }
};

2.子数组最大平均数

题目

解析

1.解析

  • 思路没什么区别,就是这题注意要用 double 返回结果;

代码

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int ans = -1e8;
        int sum = 0;

        for(int i = 0;i < nums.size();i ++){
            sum += nums[i];

            if(i < k - 1) continue;

            ans = max(ans,sum);

            sum -= nums[i - k + 1];
        }

        return (double) ans / k;
    }
};

3.大小为K且平均值大于等于阈值的子数组数

题目

解析

1.解析

  • 没啥说的,一样的思路

代码

class Solution {
public:
    int numOfSubarrays(vector<int>& arr, int k, int threshold) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int ans = 0;
        int aver;
        int sum = 0;

        for(int i = 0;i < arr.size();i ++){
            sum += arr[i];

            if(i < k - 1) continue;

            aver = sum / k;
            if(aver >= threshold) ans ++;

            sum -= arr[i - k + 1];
        }

        return ans;
    }
};

4.半径为 K 的子数组平均值

题目

解析

1.解析

  • 题目要求没有平均值的地方为-1,所以直接开始就设定一个全为-1的数组;
  • 我们把左右半径内的子数组作为滑动窗口,i 计算的为右边界,答案要赋值给 i - K;
  • 这题需要注意下标的一一对应;

代码

class Solution {
public:
    vector<int> getAverages(vector<int>& nums, int k) {
        // 时间复杂度;O(n)
        // 空间复杂度:O(1)

        int n = nums.size();
        long long sum = 0;
        vector<int> ans(n,-1); // 提前设置为-1,后面只计算存在平均值的位置

        for(int i = 0;i < n;i ++){
            sum += nums[i];

            if(i < 2 * k) continue;

            ans[i - k] = sum / (2 * k + 1);
            
            sum -= nums[i - 2 * k];
        }

        return ans;
    }
};

5.得到 K 个黑块的最少涂色次数

题目

解析

1.解析

  • 没啥说的,一样的思路

代码

class Solution {
public:
    int minimumRecolors(string blocks, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = blocks.size();
        int cnt = 0;
        int ans = k + 1;

        for(int i = 0;i < n;i ++){
            if(blocks[i] == 'W') cnt ++;

            if(i < k - 1) continue;

            ans = min(ans,cnt);

            if(blocks[i - k + 1] == 'W') cnt --;
        }

        return ans;
    }
};

6.几乎唯一子数组的最大和

题目

解析

1.解析

  • 核心:用哈希表存储每个滑动窗口内元素的个数,其大小为不同元素的个数;
  • 当哈希表个数大于等于 m 时,说明窗口内不同元素个数大于等于 m,此时可以更新长度;
  • 最后去除左边元素的同时,还要给对应哈希表的值减 1;
  • 如果此时该数的哈希表值为 0,则把它从哈希表中删除,防止占位;
  • 时间复杂度:O(n);空间复杂度:O(k);

代码

class Solution {
public:
    long long maxSum(vector<int>& nums, int m, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度;O(k)

        int n = nums.size();
        long long ans = 0;
        long long sum = 0;

        unordered_map<int,int> cnt;

        for(int i = 0;i < n;i ++){
            sum += nums[i];
            cnt[nums[i]] ++;
            
            if(i < k - 1) continue;

            // 哈希表的大小,即当前窗口不同元素的个数
            if(cnt.size() >= m) ans = max(ans,sum);
            
            int s = i - k + 1;
            sum -= nums[s];

            // 如果减去左边的元素对应哈希表值变为0,去除该键值对
            // 如果不去除,该元素还是占了哈希表的一个位置
            cnt[nums[s]] --;
            if(cnt[nums[s]] == 0) cnt.erase(nums[s]);
        }

        return ans;
    }
};

7.长度为 K 子数组中的最大和

题目

解析

1.解析

  • 思路和上一题一模一样,没什么说的;

代码

class Solution {
public:
    long long maximumSubarraySum(vector<int>& nums, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(k)

        long long ans = 0;
        long long sum = 0;

        unordered_map<int,int> cnt;

        for(int i = 0;i < nums.size();i ++){
            sum += nums[i];
            cnt[nums[i]] ++;

            if(i < k - 1) continue;

            if(cnt.size() == k) ans = max(ans,sum);

            int s = i - k + 1;
            sum -= nums[s];
            cnt[nums[s]] --;
            if(cnt[nums[s]] == 0) cnt.erase(nums[s]);
        }

        return ans;
    }
};

 8.爱生气的书店老板

题目

解析

1.解析

  • 先计算所有满意的顾客,不满意的顾客最大值用滑动窗口解决,这里 K = minutes;
  • 注意点:移动窗口时,若左边界为不满意的顾客才需要减去;

代码

class Solution {
public:
    int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = customers.size();
        int sum = 0;
        int max_sum = 0;
        int ans = 0;

        // 满意的顾客
        for(int i = 0;i < n;i ++){
            if(grumpy[i] == 0) ans += customers[i];
        }

        // 不满意的顾客在窗口内最大数
        for(int i = 0;i < n;i ++){
            if(grumpy[i] == 1) sum += customers[i];

            if(i < minutes - 1) continue;

            max_sum = max(max_sum,sum);

            if(grumpy[i - minutes + 1] == 1) sum -= customers[i - minutes + 1];
        }        

        return ans + max_sum;
    }
};

9.可获得的最大点数

题目

解析

1.解析

  • 本题运用了反向思维,题目求两端最大点数,我们可以转化为求中间滑动窗口点数和的最小值;
  • ans 设置为了最大值,INT_MAX;

代码

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        // 时间复杂度:O(n)
        // 空间复杂度:O(1)

        int n = cardPoints.size();
        int ans = INT_MAX; // 最大值
        int sum = 0;

        // 记录所有点数之和
        int cnt = 0;
        for(int i = 0;i < n;i ++) cnt += cardPoints[i]; 

        // 全拿的情况单独讨论
        if(k == n) ans = 0;

        // 反向思维,讨论其余卡牌和最小的情况
        for(int i = 0;i < n;i ++){
            sum += cardPoints[i];

            if(i < n - k - 1) continue;

            ans = min(ans,sum);

            sum -= cardPoints[i - n + k + 1];
        }

        return cnt - ans;
    }
};

10.总结

定长滑动窗口有以下几步:

  • 1.移动指针 i ,直到窗口长度变为 K,在过程中记录某些变量的值;
  • 2.根据题目条件,更新结果取值;
  • 3.移动窗口,即去除左边第一个数,并根据该值去更新某些变量;
  • 注意点:移动窗口过程中,下标的变化;
<think>好的,我现在需要帮用户解决如何用Python判断股票是否是上涨形态的问题。首先,我得回想一下用户之前的提问历史。用户之前询问过股票上涨的形态有哪些,我详细介绍了头肩底、双底、上升三角形等形态。现在用户进一步想用Python来实现判断,这说明用户可能有一定的编程基础,并且希望将理论知识应用到实际中,进行自动化分析或交易。 接下来,我需要考虑用户可能的需求。用户可能希望有一个具体的代码示例,能够读取股票数据,然后通过某些技术指标或形态识别来判断是否处于上涨趋势。但股票形态识别本身比较复杂,尤其是像头肩底、双底这样的形态,需要复杂的模式识别和数据处理。对于刚入门的用户来说,直接实现这些可能比较困难,所以可能需要简化问题,先介绍基于均线或简单技术指标的方法,比如均线多头排列、MACD金叉等,这些相对容易实现,同时也能给出上涨趋势的信号。 然后,我需要考虑用户的实际应用场景。用户可能是在做量化交易的基础学习,或者想要构建自己的交易策略。因此,代码需要具备实用性,比如使用pandas处理数据,用matplotlib可视化,以及如何获取实时或历史数据。同时,用户可能对代码的结构和注释有要求,希望代码清晰易懂,方便修改和扩展。 另外,用户可能没有提到的深层需求是,如何处理数据获取的问题,比如从哪里获得股票数据,可能需要推荐一些常用的库,如akshare、tushare等。此外,用户可能还需要了解如何优化判断逻辑,比如参数调整、避免过拟合等,但作为初步的解决方案,可能需要先提供基础方法,再给出扩展建议。 在技术实现方面,我需要分步骤来构建这个Python程序。首先,获取股票数据,然后计算必要的技术指标,比如均线、MACD、RSI等,接着根据这些指标设计判断条件,比如短期均线上穿长期均线形成金叉,或者MACD的DIF线上穿DEA线等。这些条件可以作为上涨趋势的初步判断依据。 对于更复杂的形态识别,比如双底或头肩底,可能需要使用更高级的方法,比如寻找局部极值点,然后根据这些点的位置和形态来确认。这部分实现起来比较复杂,可能需要用到滑动窗口、极值检测算法,或者机器学习模型。但考虑到用户的可能水平,这部分可以作为进阶内容简要提及,并给出参考方向。 最后,需要提醒用户注意的风险,比如技术指标的滞后性、市场噪音导致的假信号,以及回测和实盘的区别。这些内容可以帮助用户更全面地理解自动化判断的局限性,避免盲目依赖代码结果。 总结来说,我需要提供一个结构清晰、逐步实现的Python代码示例,涵盖数据获取、指标计算、趋势判断,并给出可视化和进一步优化的建议,同时指出潜在的风险和注意事项,确保用户能够理解并应用这些方法到实际中。</think>以下是使用Python判断股票上涨形态的**分步指南**,涵盖数据获取、技术指标计算、形态识别逻辑及完整代码示例: --- ### 一、数据准备 #### 1. 安装依赖库 ```python # 安装必要库 !pip install akshare pandas mplfinance ta ``` #### 2. 获取股票数据 ```python import akshare as ak def get_stock_data(symbol, start_date, end_date): """获取股票历史数据(前复权)""" df = ak.stock_zh_a_hist(symbol=symbol, period="daily", start_date=start_date, end_date=end_date, adjust="hfq") df[&#39;日期&#39;] = pd.to_datetime(df[&#39;日期&#39;]) df.set_index(&#39;日期&#39;, inplace=True) return df[[&#39;开盘&#39;, &#39;最高&#39;, &#39;最低&#39;, &#39;收盘&#39;, &#39;成交量&#39;]] # 示例:获取贵州茅台2023年数据 data = get_stock_data("600519", "20230101", "20231231") ``` --- ### 二、基础上涨趋势判断 #### 1. 均线多头排列 ```python def check_ma_trend(data, short=5, medium=20, long=60): """判断均线是否多头排列(短>中>长)""" data[&#39;MA5&#39;] = data[&#39;收盘&#39;].rolling(short).mean() data[&#39;MA20&#39;] = data[&#39;收盘&#39;].rolling(medium).mean() data[&#39;MA60&#39;] = data[&#39;收盘&#39;].rolling(long).mean() current_status = ( data[&#39;MA5&#39;].iloc[-1] > data[&#39;MA20&#39;].iloc[-1] and data[&#39;MA20&#39;].iloc[-1] > data[&#39;MA60&#39;].iloc[-1] ) return current_status if check_ma_trend(data): print("均线呈多头排列:短期趋势向上") ``` #### 2. MACD金叉 ```python from ta.trend import MACD def check_macd_golden_cross(data): """检测MACD金叉(DIF上穿DEA)""" macd = MACD(data[&#39;收盘&#39;]) data[&#39;DIF&#39;] = macd.macd() data[&#39;DEA&#39;] = macd.macd_signal() # 最近两天的DIF和DEA值 dif_today, dif_yday = data[&#39;DIF&#39;].iloc[-1], data[&#39;DIF&#39;].iloc[-2] dea_today, dea_yday = data[&#39;DEA&#39;].iloc[-1], data[&#39;DEA&#39;].iloc[-2] return (dif_yday < dea_yday) and (dif_today > dea_today) if check_macd_golden_cross(data): print("MACD出现金叉:潜在上涨信号") ``` --- ### 三、典型形态识别(进阶) #### 1. 双底形态检测 ```python def detect_double_bottom(data, window=30, tolerance=0.03): """检测最近window天内是否形成双底""" close_prices = data[&#39;收盘&#39;].iloc[-window:] # 寻找局部最低点 minima = close_prices[(close_prices.shift(1) > close_prices) & (close_prices.shift(-1) > close_prices)] if len(minima) >= 2: first_low = minima[-2] second_low = minima[-1] # 检查双底条件 is_similar = abs(first_low - second_low)/first_low <= tolerance is_rising = close_prices[-1] > (first_low + second_low)/2 return is_similar and is_rising return False if detect_double_bottom(data): print("检测到双底形态:潜在反转信号") ``` #### 2. 上升三角形检测 ```python def detect_ascending_triangle(data, window=20): """检测上升三角形(高点水平,低点逐步抬高)""" recent_data = data.iloc[-window:] resistance = recent_data[&#39;最高&#39;].max() lows = recent_data[&#39;最低&#39;] # 检查低点是否形成上升趋势线 from sklearn.linear_model import LinearRegression X = np.arange(len(lows)).reshape(-1, 1) y = lows.values.reshape(-1, 1) model = LinearRegression().fit(X, y) slope = model.coef_[0][0] return (slope > 0) and (recent_data[&#39;最高&#39;].std() < 0.02*resistance) if detect_ascending_triangle(data): print("检测到上升三角形:突破可能性较高") ``` --- ### 四、可视化验证 ```python import mplfinance as mpf # 绘制K线图+均线 mpf.plot(data[-120:], type=&#39;candle&#39;, mav=(5, 20, 60), volume=True, style=&#39;yahoo&#39;, title=&#39;股票走势分析&#39;, figratio=(12, 6)) ``` --- ### 五、综合判断函数 ```python def is_bullish_pattern(data): """综合判断上涨形态""" signals = { &#39;均线多头&#39;: check_ma_trend(data), &#39;MACD金叉&#39;: check_macd_golden_cross(data), &#39;双底形态&#39;: detect_double_bottom(data), &#39;上升三角形&#39;: detect_ascending_triangle(data) } print("===== 形态分析报告 =====") for k, v in signals.items(): print(f"{k}: {&#39;✅&#39; if v else &#39;❌&#39;}") return any(signals.values()) if is_bullish_pattern(data): print("\n⚠️ 注意:检测到看涨信号,建议结合成交量验证!") ``` --- ### 六、注意事项 1. **参数优化**: - 根据股票波动性调整`window`(检测周期)和`tolerance`(形态容差) - 使用$$ \text{自适应参数} = \frac{252}{\text{年交易天数}} \times \text{基准参数} $$调整时间参数 2. **假信号过滤**: - 增加成交量验证:`data[&#39;成交量&#39;].iloc[-1] > 1.2 * data[&#39;成交量&#39;].rolling(20).mean().iloc[-1]` - 结合基本面数据(如PE、ROE)进行交叉验证 3. **实时更新**: ```python # 定时获取最新数据(示例) import schedule import time def daily_check(): new_data = get_stock_data("600519", "20240101", datetime.today().strftime("%Y%m%d")) is_bullish_pattern(new_data) schedule.every().day.at("15:30").do(daily_check) # 每天收盘后执行 while True: schedule.run_pending() time.sleep(1) ``` --- ### 扩展建议 - 使用机器学习(LSTM/Prophet)预测趋势延续概率 - 加入波动率指标(ATR)评估风险收益比 - 对接交易API实现自动化操作(需谨慎!) 代码需根据实际需求调整参数,建议先用历史数据回测(backtrader库)验证策略有效性!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值