Hard-题目15:239. Sliding Window Maximum

本文介绍了一种高效解决滑动窗口最大值问题的方法。利用双端队列维护当前窗口内的最大值及其候选解,通过巧妙的数据结构设计,将时间复杂度降低至O(n)。

题目原文:
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.

Window position                Max
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

Therefore, return the max sliding window as [3,3,5,5,6,7].
题目大意:
给出一个数组和一个k长度的滑动窗口,把滑动窗口从数组开头滑到结尾,求每个滑动窗口内子数组的最大值,组成一个新数组并返回。
题目分析:
朴素解法是暴力枚举每个滑动窗口的最大值,时间复杂度是O(nk),虽然也能ac,但一个hard难度的题不可能只要求到这步。
根据hint,考虑用双端队列,维持队列长度小于等于k,且队头永远为滑动窗口最大值,队列后面跟的是可能成为最大值的候选解。每次加入一个元素,先看队头元素的下标是否已经在滑动窗口之外,如果在则出队,接下来判断这个元素是否大于队尾的值,如果大于则从队尾一直弹出(因为有了这个更大的数,前面的数在往右滑的过程中不可能是解),每次记录下队头的值,即为当前滑动窗口的最大值。
那么还有一个问题,如何知道队头是否滑到了窗口外面呢?答案是,我们的deque只要存的是下标就可以了。因为队列的操作全都是常数时间的,这样的时间复杂度就降到了O(n).
源码:(language:java)

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length==0)
            return new int[0];
        int[] window = new int[nums.length-k+1];
        int j=0;
        Deque<Integer> deque = new LinkedList<>();
        for(int i = 0;i<nums.length;i++) {
            while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
                deque.removeLast();
            }
            deque.addLast(i);
            if(deque.getFirst() == i-k)
                deque.removeFirst();
            if(i>=k-1)
                window[j++] = nums[deque.getFirst()];
        }
        return window;
    }
}

成绩:
29ms,beats 62.07%,众数31ms,9.54%
Cmershen的碎碎念:
这道题在Leetcode里面算是难题了,且对面试来说算是比较困难的一题,Leetcode的国内山寨版Lintcode的难度评分中将其评为Very Hard难度(lintcode里面very hard难度只有两题,另一题是涉及到线段树的skyline problem),足见此题对普通面试者的难度。
此外discuss中还有一个仅4ms的解法,有机会研读一下。

import cv2 import numpy as np import os import glob import time import random from skimage.feature import hog from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt from scipy.ndimage import label, maximum_filter # 设置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False class VehicleDetector: def __init__(self): self.clf = None # SVM分类器 self.scaler = None # 特征标准化器 self.hog_params = { 'orientations': 9, 'pixels_per_cell': (8, 8), 'cells_per_block': (2, 2), 'transform_sqrt': True, 'block_norm': 'L2-Hys', 'visualize': False } self.window_size = (64, 64) # 检测窗口大小 def extract_features(self, image): """从图像中提取HOG特征""" # 转换为灰度图 if len(image.shape) > 2: image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 调整图像大小 image = cv2.resize(image, self.window_size) # 计算HOG特征 features = hog(image, **self.hog_params) return features def load_dataset(self, vehicle_dir, non_vehicle_dir, sample_size=1500): """加载车辆和非车辆数据集""" features = [] labels = [] # 获取所有图像路径 vehicle_paths = glob.glob(os.path.join(vehicle_dir, '*.png')) + \ glob.glob(os.path.join(vehicle_dir, '*.jpg')) non_vehicle_paths = glob.glob(os.path.join(non_vehicle_dir, '*.png')) + \ glob.glob(os.path.join(non_vehicle_dir, '*.jpg')) # 检查是否找到图像 if not vehicle_paths: raise FileNotFoundError(f"在 {vehicle_dir} 中未找到车辆图像") if not non_vehicle_paths: raise FileNotFoundError(f"在 {non_vehicle_dir} 中未找到非车辆图像") # 使用更多样本 vehicle_paths = random.sample(vehicle_paths, min(sample_size, len(vehicle_paths))) non_vehicle_paths = random.sample(non_vehicle_paths, min(sample_size, len(non_vehicle_paths))) print(f"找到 {len(vehicle_paths)} 张车辆图像, {len(non_vehicle_paths)} 张非车辆图像") # 处理车辆图像 print("提取车辆图像特征...") for i, path in enumerate(vehicle_paths): if i % 100 == 0: print(f"已处理 {i}/{len(vehicle_paths)} 张车辆图像") image = cv2.imread(path) if image is None: print(f"无法加载车辆图像: {path}") continue feat = self.extract_features(image) features.append(feat) labels.append(1) # 处理非车辆图像 print("提取非车辆图像特征...") for i, path in enumerate(non_vehicle_paths): if i % 100 == 0: print(f"已处理 {i}/{len(non_vehicle_paths)} 张非车辆图像") image = cv2.imread(path) if image is None: print(f"无法加载非车辆图像: {path}") continue feat = self.extract_features(image) features.append(feat) labels.append(0) print(f"数据集加载完成: {len(features)}个样本") return np.array(features), np.array(labels) def train(self, X, y): """训练SVM分类器""" print("正在训练分类器...") start_time = time.time() # 数据验证 if len(X) == 0 or len(y) == 0: raise ValueError("训练数据为空!请检查数据集路径和格式") # 标准化特征 self.scaler = StandardScaler() X_scaled = self.scaler.fit_transform(X) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.2, random_state=42 ) # 创建并训练线性SVM self.clf = LinearSVC(C=0.1, max_iter=10000, random_state=42, verbose=1) self.clf.fit(X_train, y_train) # 评估模型 train_acc = self.clf.score(X_train, y_train) test_acc = self.clf.score(X_test, y_test) print(f"训练完成! 耗时: {time.time() - start_time:.2f}秒") print(f"训练准确率: {train_acc:.4f}, 测试准确率: {test_acc:.4f}") # 生成分类报告 y_pred = self.clf.predict(X_test) print("\n分类报告:") print(classification_report(y_test, y_pred, target_names=['非车辆', '车辆'])) return train_acc, test_acc def detect_vehicles(self, image, threshold=2.0, scale=1.3, window_step=16): """在图像中检测车辆 - 改进的检测方法""" if self.clf is None: raise ValueError("请先训练分类器!") # 复制原始图像 orig = image.copy() # 转换为HSV色彩空间 - 更好地处理光照变化 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 创建热力图 heatmap = np.zeros_like(image[:, :, 0]).astype(np.float32) # 使用np.float32 # 仅处理图像的下半部分(道路区域) height, width = image.shape[:2] roi_y_start = height // 2 roi = image[roi_y_start:, :] # 创建图像金字塔 pyramid = [] current_scale = 1.0 min_size = self.window_size # 添加原始图像 pyramid.append((roi, current_scale)) # 生成金字塔 (限制层数) for _ in range(6): # 减少金字塔层数 # 计算下一层尺度 current_scale /= scale w = int(roi.shape[1] * current_scale) h = int(roi.shape[0] * current_scale) # 如果图像太小则停止 if w < min_size[0] or h < min_size[1]: break # 缩放图像并添加到金字塔 resized = cv2.resize(roi, (w, h)) pyramid.append((resized, current_scale)) # 在每个金字塔层级上滑动窗口 for i, (layer, scale_factor) in enumerate(pyramid): # 滑动窗口 for y in range(0, layer.shape[0] - self.window_size[1], window_step): for x in range(0, layer.shape[1] - self.window_size[0], window_step): # 提取窗口 window_img = layer[y:y+self.window_size[1], x:x+self.window_size[0]] # 提取特征 features = self.extract_features(window_img) # 标准化特征 if self.scaler: features = self.scaler.transform([features]) # 预测 prediction = self.clf.decision_function(features) # 如果置信度高于阈值,添加到热力图 if prediction > threshold: # 计算原始图像中的坐标 x1 = int(x / scale_factor) y1 = int(y / scale_factor) + roi_y_start x2 = int((x + self.window_size[0]) / scale_factor) y2 = int((y + self.window_size[1]) / scale_factor) + roi_y_start # 在热力图上增加权重 heatmap[y1:y2, x1:x2] += prediction # 应用高斯模糊和阈值处理 heatmap = cv2.GaussianBlur(heatmap, (35, 35), 0) # 增大高斯核尺寸 heatmap_thresh = np.zeros_like(heatmap) heatmap_thresh[heatmap > threshold] = 1 # 应用形态学操作 - 开运算 kernel = np.ones((25, 25), np.uint8) heatmap_thresh = cv2.morphologyEx(heatmap_thresh, cv2.MORPH_OPEN, kernel) # 连接组件分析 labels, num_labels = label(heatmap_thresh) # 为每个连接的组件创建边界框 detections = [] for i in range(1, num_labels + 1): # 找到组件的像素坐标 nonzero = (labels == i).nonzero() nonzeroy = np.array(nonzero[0]) nonzerox = np.array(nonzero[1]) # 定义边界框 x1 = np.min(nonzerox) y1 = np.min(nonzeroy) x2 = np.max(nonzerox) y2 = np.max(nonzeroy) # 计算置信度(热力图的平均值) confidence = np.mean(heatmap[y1:y2, x1:x2]) # 添加边界框 detections.append((x1, y1, x2, y2, confidence)) # 绘制边界框 cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) label_text = f"Vehicle: {confidence:.2f}" cv2.putText(image, label_text, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) print(f"检测到 {len(detections)} 个有效车辆") return image, detections def test_single_image(self, image_path, threshold=2.0, scale=1.3, window_step=16): """测试单张图像并显示结果""" # 读取测试图像 image = cv2.imread(image_path) if image is None: print(f"无法读取图像: {image_path}") return None, [] # 检测车辆 start_time = time.time() result, detections = self.detect_vehicles( image.copy(), threshold=threshold, scale=scale, window_step=window_step ) print(f"检测耗时: {time.time() - start_time:.2f}秒") # 转换为RGB格式以在matplotlib中显示 result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) orig_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 显示结果 plt.figure(figsize=(15, 8)) plt.subplot(1, 2, 1) plt.imshow(orig_rgb) plt.title('原始图像') plt.axis('off') plt.subplot(1, 2, 2) plt.imshow(result_rgb) plt.title(f'检测结果 (检测到 {len(detections)} 辆车)') plt.axis('off') plt.tight_layout() plt.savefig('vehicle_detection_result.png') plt.show() return result, detections def main(): # 创建车辆检测器 detector = VehicleDetector() # 数据集路径 vehicle_dir = r"C:\Users\29810\Desktop\shijue\vehicles" non_vehicle_dir = r"C:\Users\29810\Desktop\shijue\non-vehicles" # 检查数据集是否存在 if not os.path.exists(vehicle_dir): print(f"车辆目录不存在: {vehicle_dir}") return if not os.path.exists(non_vehicle_dir): print(f"非车辆目录不存在: {non_vehicle_dir}") return # 加载数据集 try: X, y = detector.load_dataset(vehicle_dir, non_vehicle_dir, sample_size=1500) except Exception as e: print(f"加载数据集失败: {str(e)}") return # 训练分类器 try: train_acc, test_acc = detector.train(X, y) except Exception as e: print(f"训练分类器失败: {str(e)}") return # 测试图像路径 test_images = [ "C:/Users/29810/Desktop/shijue/test_images/test 1.jpg", "C:/Users/29810/Desktop/shijue/test_images/test 2.jpg", "C:/Users/29810/Desktop/shijue/test_images/test 3.jpg", ] # 测试参数 - 使用更严格的设置 params = { 'threshold': 2.0, # 提高阈值减少误报 'scale': 1.3, # 优化缩放比例 'window_step': 16 # 减小步长提高检测精度 } # 测试所有图像 for i, img_path in enumerate(test_images): print(f"\n测试图像 {i+1}/{len(test_images)}: {img_path}") if not os.path.exists(img_path): print(f"测试图像不存在: {img_path}") continue detector.test_single_image(img_path, **params) print("\n车辆检测完成!") if __name__ == "__main__": main() 输出的三张照片里对车尾的检测不正确,修改代码
06-20
这是我的报错,请问它反映了哪些问题?你又是如何锁定问题的重点并考虑出解决方案的: 025-03-04 11:39:26,407 - INFO - download models from model hub: ms Downloading Model to directory: C:\Users\QwQ\.cache\modelscope\hub\models\damo\speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch 2025-03-04 11:39:27,033 - modelscope - WARNING - Model revision not specified, use revision: v2.0.9 2025-03-04 11:39:29,017 - WARNING - enable bias encoder sampling and contextual training 2025-03-04 11:39:29,738 - INFO - Loading pretrained params from C:\Users\QwQ\.cache\modelscope\hub\models\damo\speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch\model.pt Downloading Model to directory: C:\Users\QwQ\.cache\modelscope\hub\models\damo\speech_fsmn_vad_zh-cn-16k-common-pytorch 2025-03-04 11:39:31,712 - INFO - Building VAD model. 2025-03-04 11:39:31,713 - INFO - download models from model hub: ms 2025-03-04 11:39:33,038 - modelscope - WARNING - Model revision not specified, use revision: v2.0.4 2025-03-04 11:39:33,662 - INFO - Loading pretrained params from C:\Users\QwQ\.cache\modelscope\hub\models\damo\speech_fsmn_vad_zh-cn-16k-common-pytorch\model.pt Downloading Model to directory: C:\Users\QwQ\.cache\modelscope\hub\models\damo\punc_ct-transformer_cn-en-common-vocab471067-large 2025-03-04 11:39:33,681 - INFO - Building punc model. 2025-03-04 11:39:33,681 - INFO - download models from model hub: ms 2025-03-04 11:39:34,053 - modelscope - WARNING - Model revision not specified, use revision: v2.0.4 2025-03-04 11:39:37,655 - INFO - Loading pretrained params from C:\Users\QwQ\.cache\modelscope\hub\models\damo\punc_ct-transformer_cn-en-common-vocab471067-large\model.pt 2025-03-04 11:39:40,014 - INFO - Loading LLM model... The `load_in_4bit` and `load_in_8bit` arguments are deprecated and will be removed in the future versions. Please, pass a `BitsAndBytesConfig` object in `quantization_config` argument instead. Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值