leetcode1226:
(此图盗用【力扣】)
最多只允许 4 个哲学家去持有叉子,可保证至少有 1 个哲学家能吃上意大利面
用Semaphore(信号量)去实现上述的限制:Semaphore eatLimit = new Semaphore(4);
一共有5个叉子,视为5个ReentrantLock,并将它们全放入1个数组中。
class DiningPhilosophers {
//1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
private final ReentrantLock[] lockList = {
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()};
//限制 最多只有4个哲学家去持有叉子
private Semaphore eatLimit = new Semaphore(4);
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher + 1) % 5; //左边叉子的编号
int rightFork = philosopher; //右边叉子的编号
eatLimit.acquire(); //限制的人数-1
lockList[leftFork].lock(); //获取左边的叉子锁
lockList[rightFork].lock(); //获取右边的叉子锁
pickLeftFork.run(); //拿起左边叉子的具体执行
pickRightFork.run(); //拿起右边叉子的具体执行
eat.run(); //吃意大利面
putLeftFork.run(); //放下左边叉子的具体执行
putRightFork.run(); //放下右边叉子的具体执行
lockList[leftFork].unlock(); //释放左边叉子锁
lockList[rightFork].unlock(); //释放右边叉子锁
eatLimit.release();//限制的人数+1
}
}
leetcode218:
由相邻两个横坐标以及最大高度,可以确定一个矩形。
题目要我们 输出每个矩形的“上边”的左端点,同时跳过可由前一矩形“上边”延展而来的那些边。
因此我们需要实时维护一个最大高度,可以使用优先队列(堆)。
实现时,我们可以先记录下bs中所有的左右端点横坐标及高度,并根据端点横坐标进行从小到大排序。
在从前往后遍历处理时(遍历每个矩形),根据当前遍历到的点进行分情况讨论:
左端点:因为是左端点,必然存在一条从右延展的边,但不一定是需要被记录的边,因为在同一矩形中,我们只需要记录最上边的边。这时候可以将高度进行入队;
右端点:此时意味着之前某一条往右延展的线结束了,这时候需要将高度出队(代表这结束的线不被考虑)。
然后从优先队列中取出当前的最大高度,为了防止当前的线与前一矩形“上边”延展而来的线重合,我们需要使用一个变量 prev 记录上一个记录的高度。
class Solution {
public List<List<Integer>> getSkyline(int[][] bs) {
List<List<Integer>> ans = new ArrayList<>();
// 预处理所有的点,为了方便排序,对于左端点,令高度为负;对于右端点令高度为正
// 正负代表左右
List<int[]> ps = new ArrayList<>();
for (int[] b : bs) {
int l = b[0], r = b[1], h = b[2];
ps.add(new int[]{l, -h});
ps.add(new int[]{r, h});
}
// 先按照横坐标进行排序 如果横坐标相同,则按照左端点排序
// 如果相同的左右端点,则按照高度进行排序
Collections.sort(ps, (a, b)->{
if (a[0] != b[0]) return a[0] - b[0];
return a[1] - b[1];
});
PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->b-a); //大根堆
int prev = 0;
q.add(prev);
for (int[] p : ps) {
int point = p[0], height = p[1];
if (height < 0) {
// 如果是左端点,说明存在一条往右延伸的可记录的边,将高度存入优先队列
q.add(-height);
} else {
// 如果是右端点,说明这条边结束了,将当前高度从队列中移除
q.remove(height);
}
// 取出最高高度,如果当前不与前一矩形“上边”延展而来的那些边重合,则可以被记录
int cur = q.peek();
if (cur != prev) {
List<Integer> list = new ArrayList<>();
list.add(point);
list.add(cur);
ans.add(list);
prev = cur;
}
}
return ans;
}
}
leetcode642:
长度最小的区间必然是需要我们在计算中找到的,但是起点最小的区间我们是可以知道的。就是从每个区间中找最小的元素,组成的新的区间,我们称其为起始区间。如果之后没有长度比起始区间长度更短的,那么起始区间就是我们所求的最小区间,因为起始区间的起点是最小的。
那么我们所需要做的就是搜索是否有比起始区间还要短的区间了。那么该如何搜索呢?
答案是 每次都将当前区间中最小的元素丢弃,换成其原始数组中的下一个元素
class Solution {
class NumGroup{
public NumGroup(int num, int grp){
this.num = num;
this.grp = grp;
}
int num; //数值
int grp; //组号
}
public int[] smallestRange(List<List<Integer>> nums) {
//最小堆
PriorityQueue<NumGroup> numgrp = new PriorityQueue<>(new Comparator<NumGroup>(){
@Override
public int compare(NumGroup n1, NumGroup n2){
return n1.num - n2.num;
}
});
int end = -100001;
//记录每个数组当前的指针位置,一开始都指向第0个元素,即每个区间的最小元素
int[] index = new int[nums.size()];
//起始区间
for(int i = 0; i < nums.size(); i++){
if(nums.get(i).get(0) > end) {
end = nums.get(i).get(0);
}
NumGroup num = new NumGroup(nums.get(i).get(0), i);
numgrp.offer(num);
}
int max = end;
int start = numgrp.peek().num;
int min = start;
int len = end - start + 1;
while(true){
//grp为当前最小元素的原数组号
int grp = numgrp.poll().grp;
//如果当前最小元素已经是原数组最大元素了,则退出
if(index[grp]+1 == nums.get(grp).size()) break;
//索引++,并将当前最小元素的原数组中的下一个元素压入优先级队列
index[grp]++;
NumGroup n = new NumGroup(nums.get(grp).get(index[grp]), grp);
numgrp.offer(n);
//当前最大值
if(n.num > max) {
max = n.num;
}
min = numgrp.peek().num;
//长度变小
if(max-min+1 < len){
start = min;
end = max;
len = max-min+1;
}
}
return new int[]{start, end};
}
}
leetcode133:
用dfs遍历整个图然后克隆即可
class Solution {
// 建立一个一对一的映射
Map<Node,Node> map = new HashMap<>();
public Node cloneGraph(Node node)
{
if(node == null) return null;
return dfs(node);
}
Node dfs(Node node)
{
//node节点已经被访问过了,直接取出对应的克隆节点返回。
if(map.containsKey(node)) {
return map.get(node);
}
Node clone = new Node(node.val); //克隆节点
map.put(node,clone); //建立源节点到克隆节点的映射
for(Node ver: node.neighbors) //克隆边
{
clone.neighbors.add(dfs(ver));
}
return clone;
}
}
leetcode208:
class Trie {
class TrieNode {
boolean end;
TrieNode[] tns = new TrieNode[26];
}
TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String s) {
TrieNode p = root;
for(int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) {
p.tns[u] = new TrieNode();
}
p = p.tns[u];
}
p.end = true;
}
public boolean search(String s) {
TrieNode p = root;
for(int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) {
return false;
}
p = p.tns[u];
}
return p.end;
}
public boolean startsWith(String s) {
TrieNode p = root;
for(int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) {
return false;
}
p = p.tns[u];
}
return true;
}
}