记录:2022-9-4 滑动窗口最大值 操作系统同步 临界区问题 软件与硬件解决方案

本文探讨了LeetCode中使用滑动窗口求解最大值的问题,原始算法由于时间复杂度过高导致超时,解决方案是采用大根堆优化。详细分析了大根堆的实现和比较规则。此外,还讲解了操作系统中的同步问题,以两个并发进程修改共享变量为例,阐述了临界区问题及Peterson解决方案,强调了同步的必要性和几种同步机制。
摘要由CSDN通过智能技术生成

学习时间 2022-9-4

学习内容

1、LeetCode 一道困难题

在这里插入图片描述
这道题先使用滑动窗口,在51/51案例时超时了,有点可惜
附上自己的算法

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int L = 0;
        int R = k-1;
        int[] results = new int[nums.length-k+1];
        int max = findMaxInRange(nums,L,R);
        results[0] = max;
        while(L < results.length-1){
            int out = nums[L++];
            int in = nums[++R];
            if(out == max && in < max){
                max = findMaxInRange(nums,L,R);
            }
            if(in > max){
                max = in;
            }
            results[L] = max;
        }
        return results;

    }
    public int findMaxInRange(int[] nums,int L,int R){
        //左闭右闭
        int max = nums[L];
        for(int i = L+1;i<=R;i++){
            max = Math.max(max,nums[i]);
        }
        return max;
    }
}

大致思路如下:当进一个和出一个时,更新max,如果出去的那个值是max并且进来的值比出去的max小,则重新寻找max,否则max可以保持不变 这个解法超时了,问题就在这个寻找的过程,有O(N)的复杂度,导致整体复杂度偏高,超时
其实我在优化的时候想到了用大根堆,但是没有做出来,题解中使用了大根堆,用堆去更新最大值,每次去取顶部即可,最大的问题是如何让元素移除?因为移除一个元素需要知道他的index,而堆如何去存储index也是一个问题,看了题解才知道,他把大根堆做成了一个数组,存放num和index。大根堆代码如下:

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
            public int compare(int[] pair1, int[] pair2) {
                return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
            }
        });
        for (int i = 0; i < k; ++i) {
            pq.offer(new int[]{nums[i], i});
        }
        int[] ans = new int[n - k + 1];
        ans[0] = pq.peek()[0];
        for (int i = k; i < n; ++i) {
            pq.offer(new int[]{nums[i], i});
            while (pq.peek()[1] <= i - k) {
                pq.poll();
            }
            ans[i - k + 1] = pq.peek()[0];
        }
        return ans;
    }
}

对于这个题,我应该思考以下几点:

  1. PriorityQueue的compare为啥这样去填写?
  2. peek()[1] <= i-k 为何就可以确定移除元素?
  3. 对于求最大值,我们需要去维护最大值以外的值吗?在这个题中我被限制了,一直在想如何在堆中去remove出去的元素,实则不必

2、《操作系统概念》同步

为什么需要同步

现在假设有两个进程,都有一个整形变量counter,假设现在counter=5,生产者进程每次增加一项时,counter++,消费者进程每消费一项时,让counter–,现在让生产者和消费者并发执行,counter会出现4 5 6 三种答案,但其实只有5是对的。
这两个赋值可以看作(经典机器上的机器语言):

register1 = counter;
register1 = register1 + 1;
counter = register1;
register2 = counter;
register2 = register2 - 1;
counter = register2;

但并发执行,会让上述的语句按任意的顺序执行,所以可以出现:

register1 = counter;//生产者  register1 = 5
register1 = register1 + 1;//生产者  register1 = 6
register2 = counter;//消费者  register1 = 5
register2 = register2 - 1;//消费者  register1 = 4
counter = register2;//消费者  counter = 4
counter = register1;//生产者  counter = 6

最后输出的结果就是6,这就是问题所在

临界区问题

现在假设某个系统有n个进程。每个进程有一段代码,称为临界区(critical section),进程在该区域可以改变公共变量。这个区的特征是:当一个进程在临界区时,不允许其他进程也在临界区,也就是说,没有两个进程可以在他们的临界区内同时执行。
在进入临界区前,每个进程应请求许可。实现这一请求的代码区段称为进入区,临界区之后有退出区,其他代码为剩余区。

do{
	//进入区
	//临界区
	//退出区
	//剩余区
}while(true)

临界区问题应该满足三个要求:

  1. 互斥 两个进程不能同时在临界区执行
  2. 进步 当有进程需要进入临界区时,不能是在剩余区执行的进程
  3. 有限等待 在进入区的进程的请求不能是无限的

Peterson经典解决方案

这是基于软件的临界区问题解决方案
这些方案都必须满足临界区的三个要求
假设有Pj和Pi两个进程

do{
	//进入区
	flag[i] = true;
	turn = j;
	while(flag[j] && turn == j);//如果进不去则等待
	//临界区
	//xxxx
	//退出区
	flag[i] = false;
	//剩余区
}while(true);

1、互斥:turn只能有一个值 满足互斥
2、进步:Pj进程修改turn后,Pi依然可以重新进入临界区,满足进步
3、有限等待 最多一次即可进入

硬件同步

这种方式主要通过加锁实现

对单处理器内核

在修改共享变量时禁止中断出现

对多处理器

使用指令让公共部分的flag改变,如何通过这个flag来锁、释放资源
一共有两种
test_and_set(&lock) 查看当前锁状态,并附加true 比如现在为false了,输出false,并让lock重新等于true
compare_and_swap(&lock,except_value,new_value) 拿到当前锁状态,如果和期望值一样,则改成new_value,比如拿到lock=0,当前能使用锁,与期望值0相同,则把锁改成1,并往后执行临界区
在这里插入图片描述
在这里插入图片描述

这种算法可以满足互斥,但是不能满足有限等待,因为可能会让其他进程无限的等待,解决办法是使用一个waiting数组,这个数组可以记录下一个执行的进程是谁,这样就可以保证每一个进程都能执行到

boolean lock;
boolean waiting[n];

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值