算法题练习

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;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值