ARTS 计划 第一周打卡

Algorithm

LeetCode 862. 和至少为 K 的最短子数组

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。

如果没有和至少为 K 的非空子数组,返回 -1 。

示例 1:

输入:A = [1], K = 1
输出:1

示例 2:

输入:A = [1,2], K = 4
输出:-1

示例 3:

输入:A = [2,-1,2], K = 3
输出:3

提示:

1 <= A.length <= 50000
-10 ^ 5 <= A[i] <= 10 ^ 5
1 <= K <= 10 ^ 9

看到这道题,我第一想到的就是求一下前缀和,然后两次for循环遍历下标,暴力求解。然而O(n^2)明显超时。

思路:
首先可以求出前缀和,维护一个双端队列,保证队列单调递增的。如果sum[i]-sum[dq.front] >= K,那么以队首元素为左端点开始最短长度必然是i,所以可以将左端点从队首拿出;如果当前sum[i] <= sum[队尾元素],那么一定有sum[j]-sum[i] >= sum[j]-sum[队尾元素],如果sum[j]-sum[队尾元素]>=K,那么sum[j]-sum[i] >= K,而 j > i > 队尾元素,所以 j-i < j-队尾元素。所以i和j的长度更短一些,可以舍去队尾元素。

class Solution {
public:
    vector<int> sum;
    int shortestSubarray(vector<int>& A, int K) {
        int len=A.size();
        sum.push_back(0);
        for(int i=1;i<=len;i++){
            sum.push_back(sum[i-1]+A[i-1]);
        }
        deque<int> dq;
        int minLen=0x3f3f3f3f;
        for(int i=0;i<=len;i++){
            while(!dq.empty() && sum[i]-sum[dq.front()] >= K){
                minLen=min(minLen, i-dq.front());
                dq.pop_front();
            }
            while(!dq.empty() && sum[i]<=sum[dq.back()]) dq.pop_back();
            dq.push_back(i);
        }
        return minLen==0x3f3f3f3f?-1:minLen;
    }
};

还有一种单调栈的解法

LeetCode 4. 寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

思路:
看到两个有序数组找中位数,复杂度要求O(log(m+n)),立马(这是因为我以前做过类似的题哈哈)想到了分别求两个数组的中位数,比较大小,然后进行区间缩小。但是后来又发现这思路行不通,找了一下那个题,发现题目都不一样(TAT)。
看了这道题目的大神评论后,也清楚了这道题的思路。思路整理都写在下面了。

class Solution {
    public:
	double findMedianSortedArrays(vector<int> nums1, vector<int> nums2) {
        if(nums1.size() == 0 && nums2.size() == 0) return 0.0;
        int m = nums1.size(), n = nums2.size();
        // l: 合并后数组的左半部分的最后一个数   r: 合并后数组的右半部分的第一个数
        // 当m+n为偶数时,mid=(l+r)/2   l就是左边的数 r是右边的;为奇数的时候,l=r
        int l = (m+n+1) / 2;
        int r = (m+n+2) / 2;
        //getKth 函数负责找到两个数组合并后有序(假设排序)的数组中的第 k 个元素
        if(l == r) return getKth(nums1, 0, nums2, 0, l);
        return (getKth(nums1, 0, nums2, 0, l) + getKth(nums1, 0, nums2, 0, r)) / 2.0;
    }
    private:
    //getKth函数主要是通过找两个数组中的前k/2个元素,然后进行比较两个数组的第k/2个元素,再分情况。
	double getKth(vector<int> nums1, int st1, vector<int> nums2, int st2, int k) {
        // 边界情况,如果 nums1数组已经穷尽了,则只能返回 nums2 中的第 k 个元素 在下面解释
        if(st1 > nums1.size()-1) return nums2[st2 + k - 1];
        // 同上
        if(st2 > nums2.size()-1) return nums1[st1 + k - 1];
        // 边界情况, k = 1 则返回两个数组中最小的那个
        if(k == 1) return min(nums1[st1], nums2[st2]);
        int mid1 = INT_MAX;
        int mid2 = INT_MAX;
        // 取每个的数组的第k/2个元素
        if(st1 + k/2 - 1 < nums1.size()) mid1 = nums1[st1 + k/2 - 1];//找各自的第k/2大的数 
        if(st2 + k/2 - 1 < nums2.size()) mid2 = nums2[st2 + k/2 - 1];
        //如果两个数组的中位数 mid1 < mid2, 则说明mid1后面可能还有比mid2小的数,如果有的话所取得k个数不是 总有序数组中的前k个数。合并后的中位数位于 num1.right + num2之间
        //否则合并后的中位数位于 nums2.right + nums1 之间 (right 是相对于 mid 而言的) 
        if(mid1 < mid2)//比较这两个数,进行分治,获取结果。
        	//在nums1的k/4和nums2的k/4找  最终要求k个元素的 后k/2个元素。(这句话好好理解一下,也就是分治)
            return getKth(nums1, st1 + k/2, nums2, st2, k - k/2);
        else
            return getKth(nums1, st1, nums2, st2 + k/2, k - k/2);
    }
};

Review

因为这是第一次从英文文章中学习知识,所以选择了一篇比较简单的,但是没有去学过的。
这篇文章主要是一篇简短的关于ExecutorService的简单使用的。
https://medium.com/tyanthoney-morrell/java-concurrency-1abc75757f7d
lambda expression创建线程

Thread t1 = new Thread(() -> {
	//doing something in a separate thread
});

我们可以使用lambda表达式,因为Runnable是函数式接口
当我们有很多任务在单独线程中被执行的时候会发生什么呢?
我们需要维护一个Runnable的队列并且在线程可用时分配并启动它。
这种情况下需要ExecutorService,ExecutorService可以写一个不需要自己管理线程的并发应用。

ExecutorService threadPool = Executors.newSingleThreadExecutor();
ExecutorService threadPool1 = Executors.newFixedThreadPool(10);

threadPool1.submit(runnableExample);
//ExecutorService中有   分配任务给线程   的队列。
threadPool.submit(() -> {
	//doing something in another thread
});

我们需要从线程返回一些数据的时候,在lambda表达式中直接return

Future<String> future = threadPool.submit(() -> {
	return "the string being returned";
});

这是因为Callable也满足函数式接口的定义
我们可以从call中获取到数据,但是要等到call执行完毕,
因为call是另开的一个线程,和main线程异步执行。

if(!future.isDone(){
	System.out.println("Not done");
}else{
	System.out.println(future.get());
}

在此对上述文章扩展
Java中Lambda表达式的使用

下面都是来源于链接中的内容,侵删。

Lambda表达式的本质只是“语法糖”,不过这种表达式虽然简洁但是难懂。Lambda允许通过表达式来代替函数式接口。
Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及java.util.stream 包。stream如同迭代器。

Lambda表达式

语法:
(parameters) -> expression
(parameters) -> { statements; }
如:

() -> 5//不接收参数,返回值为5
(x, y) -> x+y//接收x、y返回x+y
(String s) -> System.out.println(s);//接收字符串并打印

Lambda遍历List的使用

String[] strs = {
        "1","2","3","4"
};
List<String> list = Arrays.asList(strs);
for (String str : list) {
    System.out.println(str);
}
list.forEach((str) -> System.out.println(str));//使用Lambda
list.forEach(System.out::println);//使用双冒号操作符

Lambda使用匿名内部类

new Thread(new Runnable(){
	@Override
	public void run(){
		System.out.println("Hello World");
	}
}).start();
new Thread(() -> {
	System.out.println("Hello World");
}).start();
Runnable r = () -> System.out.println("Hello World");
Arrays.sort(list, (String s1, String s2) -> (s1.compareTo(s2)));

使用Lambda和Streams
filter()按照条件过滤
limit()可以限制结果集的个数:
sorted()排序、min()、max()…

phpProgrammers.stream()
		.filter((p) -> (p.getSalary() > 1400))
		.forEach((p) -> System.out.println("%s %s", p.getFirstName(), p.getLastName()));

javaProgrammers.stream()  
          .filter((p) -> ("female".equals(p.getGender())))  
          .limit(3)  
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName())); 

TIP

大三马上要实习了,最近在学很多东西,比较杂乱,今天总结一下学习的内容,写在另一篇博客中。
JVM内存区域

猜你喜欢

转载自blog.csdn.net/qq_39513105/article/details/88681375