您的位置:首页 > 编程语言

编程集训第3天:队列及堆排序思想--LeetCode滑动窗口的最大值(239)

2018-12-20 19:01 183 查看

编程集训第3天:队列及堆排序思想--LeetCode滑动窗口的最大值(239)

基础知识

一、队列(queue)
队列和栈一样,在实际程序的算法设计和计算机一些其他分支里,都有很多重要的应用,比如计算机操作系统对进程 or 作业的优先级调度算法,对离散事件的模拟算法,还有计算机主机和外部设备运行速度不匹配的问题解决等,很多很多。其实队列的本质还是线性表!只不过是一种特殊的或者说是受限的线性表,是这样的:

1)、限定在表的一端插入、另一端删除。 插入的那头就是队尾,删除的那头就是队头。也就是说只能在线性表的表头删除元素,在表尾插入元素。形象的说就是水龙头和水管,流水的水嘴是队头,进水的泵是队尾,管子中间不漏水不进水。这样呲呲的流动起来,想想就是这么个过程。

2)、先进先出 (FIFO结构)。显然我们不能在表(队列)的中间操作元素,只能是在尾部进,在头部出去,还可以类似火车进隧道的过程。(first in first out = FIFO 结构)

优先队列:
听这个名字就能知道,优先队列也是一种队列,只不过不同的是,优先队列的出队顺序是按照优先级来的;在有些情况下,可能需要找到元素集合中的最小或者最大元素,可以利用优先队列ADT来完成操作,优先队列ADT是一种数据结构,它支持插入和删除最小值操作(返回并删除最小元素)或删除最大值操作(返回并删除最大元素)

二、堆(heap)
堆是一颗具有特定性质的二叉树,堆的基本要求就是堆中所有结点的值必须大于或等于(或小于或等于)其孩子结点的值,这也称为堆的性质;堆还有另一个性质,就是当 h > 0 时,所有叶子结点都处于第 h 或 h - 1 层(其中 h 为树的高度,完全二叉树),也就是说,堆应该是一颗完全二叉树;
在下面的例子中,左边的树为堆(每个元素都大于其孩子结点的值),而右边的树不是堆:

基础知识参照

编程题目

题目描述:
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。

返回滑动窗口最大值。

C++代码实现:

  • 方法一:优先队列
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k)
{
vector<int> result;
priority_queue<pair<int,int>> Q;
if (nums.size() < k || k < 1)
return result;
for (int i = 0; i < k-1; i++)
Q.push(pair<int,int>(nums[i],i));
for (int i = k-1; i < nums.size(); i++)
{
Q.push(pair<int,int>(nums[i],i));
pair<int,int> p = Q.top();
while(p.second < i-(k-1))
{
Q.pop();
p = Q.top();
}
result.push_back(p.first);
}
return result;
}
};

优先队列:
优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。

基本操作:

empty()    如果队列为空,则返回真

pop()    删除对顶元素,删除第一个元素

push()    加入一个元素

size()     返回优先队列中拥有的元素个数

top()     返回优先队列对顶元素,返回优先队列中有最高优先级的元素

  • 方法二:双端数列
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q;
int n=nums.size();
vector<int> res;
if(n==0||k<1||n<k) return res;
for(int i=0;i<n;i++)
{
while(!q.empty()&&nums[q.back()]<=nums[i])
q.pop_back();
q.push_back(i);
if(q.front()==i-k)
q.pop_front();
if(i>=k-1)
res.push_back(nums[q.front()]);
}
return res;
}
};

思路:
维护一个双端队列,这个队列保存依次遍历的下标值。入队列的规则是比队尾小的数可以入队,比队尾大的数,将队列末尾所有小的数都清除直到遇到比这个数大的数位置。每到一个窗口,都从队头读取元素,如果元素已经超出窗口范围,就在看下一个元素。

知识回顾

【任务一】
数组:学习哈希表思想,并完成leetcode上的两数之和(1)及Happy Number(202)!(要求全部用哈希 思想实现!)(笔记整理中)

【任务二】
链表:学习单链表知识,实践环形链表(142,要求至少两种方法!)及反转一个单链表(206)(笔记整理中)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: