算法-常见数据结构设计

文章目录

    • 1. 带有setAll功能的哈希表
    • 2. LRU缓存结构
    • 3. O(1)时间插入删除随机(去重)
    • 4. O(1)时间插入删除随机(不去重)
    • 5. 快速获取数据流中的中位数
    • 6. 最大频率栈
    • 7. 全O(1)结构
    • 8. LFU缓存结构

本节的内容比较难, 大多是leetcodeHard难度级别的题目

1. 带有setAll功能的哈希表

哈希表常见的三个操作时put、get和containsKey,而且这三个操作的时间复杂度为O(1)。现在想加一个setAll功能,就是把所有记录value都设成统一的值。请设计并实现这种有setAll功能的哈希表,并且put、get、containsKey和setAll四个操作的时间复杂度都为O(1)。

在这里插入图片描述

思路分析 :
原始的哈希表的put,get,containsKey方法的时间复杂度都是O(1), 现在我们需要添加一个setAll方法, 把表中所有元素的值都改为一个特定的value,显然直接遍历的时间复杂度肯定不是O(1)
为了做到时间复杂度是O(1)我们采用时间戳计数的方法
给定一个属性time记录下本次操作的时间节点, 然后给定一个属性是setAllValue和setAllTime,记录下来我们的setAll调用时候是时间节点和设置的变量, 然后get方法返回的时候如果是大于该节点我们就进行返回原有的值, 如果小于该节点, 我们就返回setAllValue的值
代码实现如下…


/**
 * 下面这个高频的数据结构是带有setAll功能的HashMap实现
 * 原理就是传入一个时间戳, 然后进行模拟的判断
 */
class HashMapConSetAll {

    //基础的HashMap结构
    private HashMap<Integer, int[]> map = new HashMap<>();
    //定义一个时间戳
    private int time = 0;
    //定义一个是用setAll方法的时间节点以及相关的值
    private int setAllValue = 0;
    private int setAllTime = -1;

    //给一个无参的构造方法
    public HashMapConSetAll() {

    }

    //我们实现的特殊HashMap的put方法
    public void put(int key, int value) {
        if (!map.containsKey(key)) {
            map.put(key, new int[]{value, time++});
        } else {
            int[] temp = map.get(key);
            temp[0] = value;
            temp[1] = time++;
        }
    }

    //我们实现的特殊的HashMap的get方法
    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        //代码执行到这里说明我们的map里面没有这个元素
        int[] temp = map.get(key);
        if (temp[1] > setAllTime) {
            return temp[0];
        } else {
            return setAllValue;
        }
    }

    //特殊的containsKey方法
    public boolean containsKey(int key) {
        return map.containsKey(key);
    }

    //最重要的setAll方法
    public void setAll(int value) {
        this.setAllTime = time++;
        this.setAllValue = value;
    }
}


2. LRU缓存结构

在这里插入图片描述

该数据结构的实现借助的是哈希表加上双向链表的方式, 我们定义一个节点类Node, 里面的基础属性就是key与val, 我们的哈希表中的key就是节点中的key,我们的val就是该节点的地址, 为什么要用节点的地址作为val其实也非常的好理解, 用Node作为地址我们可以直接找到这个节点的位置然后进行操作,下面是我们的代码实现


/**
 * LRU缓存结构
 * 实现的方法就是双向链表 + 哈希表, 可以直接通过哈希表在O(1)的时间复杂度下获取到位置节点的地址
 * 手写一个双向链表就行了
 */
class LRUCache {

    // 首先定义的是双向链表的节点类
    public static class Node {
        int key;
        int val;
        Node prev;
        Node next;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    // 双向链表的实现方式
    public static class DoubleLinkedList {
        // 链表的头部(代表的是最早操作的数据)
        public Node first;
        // 链表的尾部(代表的是最新操作的数据)
        public Node last;

        // 给一个无参数的构造方法
        public DoubleLinkedList() {
            first = null;
            last = null;
        }

        // 添加节点的方法
        public void addNode(Node node) {
            if (node == null) {
                return;
            }
            if (first == null && last == null) {
                first = node;
                last = node;
                return;
            }
            last.next = node;
            node.prev = last;
            last = node;
        }

        //重构一下remove()和removeToLast()方法
        public Node remove() {
            //没节点你删除个damn啊
            if (first == null && last == null) {
                return null;
            }

            //只有一个节点的情况
            Node node = first;
            if (first == last) {
                last = null;
                first = null;
            } else {
                Node next = first.next;
                first.next = null;
                first = next;
                next.prev = null;
            }
            return node;
        }

        //下面是重构的removeToLast方法
        public void removeToLast(Node node) {
            //首先排除掉特殊的情况
            if (node == null || first == null) {
                return;
            }

            //如果只有一个的话或者是尾巴节点
            if (first == last || node == last) {
                return;
            }

            //如果是头节点的节点 / 普通的节点
            if (node == first) {
                Node next = node.next;
                node.next = null;
                first = next;
                first.prev = null;
            } else {
                node.prev.next = node.next;
                node.next.prev = node.prev;
                node.prev = null;
                node.next = null;
            }
            //此时node节点完全是一个独立的节点
            last.next = node;
            node.prev = last;
            last = node;
        }
    }

    private HashMap<Integer, Node> map = new HashMap<>();
    DoubleLinkedList list = new DoubleLinkedList();
    private int capacity;

    public LRUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        Node node = map.get(key);
        list.removeToLast(node);
        return node.val;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.val = value;
            list.removeToLast(node);
        } else {
            Node node = new Node(key, value);
            if (map.size() < capacity) {
                map.put(key, node);
                list.addNode(node);
            } else {
                Node nde = list.remove();
                map.remove(nde.key);
                list.addNode(node);
                map.put(key, node);
            }
        }
    }

    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(2);
        lruCache.put(1, 0);
        lruCache.put(2, 2);
        int res = lruCache.get(1);
        lruCache.put(3, 3);
        int res1 = lruCache.get(2);
        lruCache.put(4, 4);
        int res2 = lruCache.get(1);
        int res3 = lruCache.get(3);
        int res4 = lruCache.get(4);
    }
}

3. O(1)时间插入删除随机(去重)

在这里插入图片描述

这个类的实现还是基于的哈希表, 外加上一个动态数组, key就是该数字, val是在动态数组里面的下标的位置, 如果删除元素, 动态数组中的那个空位置我们就用最后一个元素去填充, 然后删掉最后一个游戏(这样就保证了这个时间的复杂度O(1)), 同时在哈希表里面删除该值, 并该变最后一个元素的val为删除元素的之前的下标, 代码实现如下

class RandomizedSet {

    //其实现的方式是通过动态数组也就是我们的ArrayList跟HashMap共同实现的
    //其实从我们的经验可以了解到, 凡是涉及到O(1)的操作一般都是通过散列表的形式实现的
    HashMap<Integer, Integer> map = new HashMap<>();
    ArrayList<Integer> list = new ArrayList<>();

    //构造方法这里其实没什么用, 我们直接设置为空方法体的
    public RandomizedSet() {

    }

    //常规的insert方法, 我们的map结构第一个整数存储的就是值, 第二个是下标
    public boolean insert(int val) {
        if (!map.containsKey(val)) {
            list.add(val);
            map.put(val, list.size() - 1);
            return true;
        }
        return false;
    }

    //remove方法是一个比较重要的方法
    public boolean remove(int val) {
        if (map.containsKey(val)) {
            int valIndex = map.get(val);
            int endValue = list.get(list.size() - 1);
            map.put(endValue, valIndex);
            list.set(valIndex, endValue);
            map.remove(val);
            list.remove(list.size() - 1);
            return true;
        }
        return false;
    }

    public int getRandom() {
        int randIndex = (int) (Math.random() * list.size());
        return list.get(randIndex);
    }

    public static void main1(String[] args) {
        RandomizedSet randomizedSet = new RandomizedSet();
        randomizedSet.remove(0);
        randomizedSet.remove(0);
        randomizedSet.insert(0);
        int res = randomizedSet.getRandom();
        randomizedSet.remove(0);
        randomizedSet.insert(0);
    }
}

4. O(1)时间插入删除随机(不去重)

在这里插入图片描述

这个题目跟上一个题目只有一个不一样的地方就是这个哈希表的val是我们的HashSet,也就是一个下标集合, 我们进行元素删除的时候我们就随意选择一个待删除元素的下标进行删除即可, 我们的getRandom方法是用动态顺序表的数据进行返回的, 所以自带加权的功能, 代码实现如下

class RandomizedCollection {

    // 这个其实原来的HashMap中的value不再是整数, 而是一个HashSet集合
    ArrayList<Integer> arr = new ArrayList<>();
    HashMap<Integer, HashSet<Integer>> map = new HashMap<>();

    public RandomizedCollection() {

    }

    //insert方法是比较简单的, 就是直接放入元素即可
    public boolean insert(int val) {
        HashSet<Integer> set = map.get(val);
        if(set == null){
            HashSet<Integer> tempSet = new HashSet<>();
            tempSet.add(arr.size());
            map.put(val,tempSet);
            arr.add(val);
            return true;
        }else{
            set.add(arr.size());
            arr.add(val);
            return false;
        }
    }

    public boolean remove(int val) {
        HashSet<Integer> set = map.get(val);
        if(set == null){
            return false;
        }
        int indexValue = set.iterator().next();
        int swapValue = arr.get(arr.size() - 1);
        int swapIndex = arr.size() - 1;
        if(swapValue == val){
            arr.remove(arr.size() - 1);
            set.remove(arr.size());
            
        }else{
            HashSet<Integer> end = map.get(swapValue);
            end.remove(swapIndex);
            end.add(indexValue);
            set.remove(indexValue);
            arr.set(indexValue,swapValue);
            arr.remove(swapIndex);
        }
        if(set.size() == 0){
            map.remove(val);
        }
        return true;
    }

    public int getRandom() {
        return arr.get((int) (Math.random() * arr.size()));
    }
}

/**
 * Your RandomizedCollection object will be instantiated and called as such:
 * RandomizedCollection obj = new RandomizedCollection();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

5. 快速获取数据流中的中位数

在这里插入图片描述

该数据结构的实现就是通过我们的堆结构来实现的, 建立一个大根堆来装入小半部分的数据, 建立一个小根堆来建立大半部分的数据, 记住在装入数据的过程中我们要保持我们的堆中的元素的大小, 也就是通过一个balance方法, 来保持两边的数量差值不超过1, 获取中位数的时候我们, 如果两边数量不一致, 就返回多的哪一方的堆顶, 如果一致, 我们就返回两个堆顶的平均值, 代码实现如下

class MedianFinder {
    private PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    // 默认就是小根堆
    private PriorityQueue<Integer> minHeap = new PriorityQueue<>();

    public MedianFinder() {

    }

    public void addNum(int num) {
        if (maxHeap.isEmpty() || num < maxHeap.peek()) {
            maxHeap.offer(num);
        } else {
            minHeap.offer(num);
        }
        balance();
    }

    public double findMedian() {
        if (minHeap.size() == maxHeap.size()) {
            return (minHeap.peek() + maxHeap.peek()) / 2.0;
        } else if (maxHeap.size() < minHeap.size()) {
            return minHeap.peek();
        } else {
            return maxHeap.peek();
        }
    }

    // 平衡堆结构的方法
    private void balance() {
        if (Math.abs(minHeap.size() - maxHeap.size()) == 2) {
            if (minHeap.size() - maxHeap.size() == 2) {
                maxHeap.offer(minHeap.poll());
            } else {
                minHeap.offer(maxHeap.poll());
            }
        }
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();

6. 最大频率栈

在这里插入图片描述

这道题目的思路就是用两个哈希表, 第一个哈希表存储的是词频统计, 第二个哈希表是一个类似桶的结构, key就是出现的词频, val是一个动态的顺序表, 代码实现如下

class FreqStack {
    //本质就是通过哈希来模拟
    //哈希表的key是词频统计, val是一个动态的顺序表, 里面存放这该词频的数字
    HashMap<Integer,ArrayList<Integer>> map = new HashMap<>();
    //再创建一个哈希表用于词频的统计
    HashMap<Integer,Integer> frq = new HashMap<>();
    //最大的词频(用于pop()操作指示)
    private int maxFrq = 0;
    public FreqStack() {
        //构造方法里面不写东西
    }
    
    public void push(int val){
        //首先进行词频的统计
        frq.put(val,frq.getOrDefault(val,0) + 1);
        maxFrq = Math.max(maxFrq,frq.get(val));
        ArrayList res = map.get(frq.get(val));
        if(res == null){
            ArrayList<Integer> arr = new ArrayList<>();
            arr.add(val);
            map.put(frq.get(val),arr);
        }else{
            res.add(val);
        }
    }
    
    //pop方法的时候应该进行的元素的删除操作
    public int pop() {
        ArrayList<Integer> arr = map.get(maxFrq);
        if(arr.size() == 1){
            int temp = arr.remove(0);
            map.remove(maxFrq--);
            if(frq.get(temp) == 1){
                frq.remove(temp);
            }else{
                frq.put(temp,frq.get(temp) - 1);
            }
            return temp;
        }else{
            int temp = arr.remove(arr.size() - 1);
            if(frq.get(temp) == 1){
                frq.remove(temp);
            }else{
                frq.put(temp,frq.get(temp) - 1);
            }
            return temp;
        }
    }
}

/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack obj = new FreqStack();
 * obj.push(val);
 * int param_2 = obj.pop();
 */

7. 全O(1)结构

在这里插入图片描述

这个就是哈希表加上双向链表桶的思路, 哈希表中保存的是该字符串的所在桶的地址, 桶里面存放的是该词频的字符串的情况, 代码实现如下, 下面有一个LFU缓存也是这样的一个结构, 双向链表(桶), 桶里面再嵌套一个双向链表实现的堆结构…

class RandomizedCollection {

    //这个其实原来的HashMap中的value不再是整数, 而是一个HashSet集合
    ArrayList<Integer> arr = new ArrayList<>();
    HashMap<Integer, HashSet<Integer>> map = new HashMap<>();

    public RandomizedCollection() {

    }

    //insert方法是比较简单的, 就是直接放入元素即可
    public boolean insert(int val) {
        HashSet<Integer> set = map.get(val);
        if (set == null) {
            HashSet<Integer> tempSet = new HashSet<>();
            tempSet.add(arr.size());
            map.put(val, tempSet);
            arr.add(val);
            return true;
        } else {
            set.add(arr.size());
            arr.add(val);
            return false;
        }
    }

    public boolean remove(int val) {
        HashSet<Integer> set = map.get(val);
        if (set == null) {
            return false;
        }
        int indexValue = set.iterator().next();
        int swapValue = arr.get(arr.size() - 1);
        int swapIndex = arr.size() - 1;
        if (swapValue == val) {
            arr.remove(arr.size() - 1);
            set.remove(arr.size());

        } else {
            HashSet<Integer> end = map.get(swapValue);
            end.remove(swapIndex);
            end.add(indexValue);
            set.remove(indexValue);
            arr.set(indexValue, swapValue);
            arr.remove(swapIndex);
        }
        if (set.size() == 0) {
            map.remove(val);
        }
        return true;
    }

    public int getRandom() {
        return arr.get((int) (Math.random() * arr.size()));
    }

    public static void main(String[] args) {
        RandomizedCollection set = new RandomizedCollection();
        set.insert(1);
        set.remove(1);
        set.insert(1);

    }
}

8. LFU缓存结构


/**
 * LFU缓存 :
 * 1. 首先定义一个节点的对象, 里面保存的是该传入数据的key和value(ListNode)
 * 这个东西也是一个双向链表的结构(模拟队列使用) --> 不可以用LinkedList(源码的remove方法是遍历删除的结构)
 * 2. 其次我们定义一个桶结构, 里面有一个val用来保存操作的次数, 还有一个双向的链表(也就是我们的上面定义的队列结构)
 * 这个桶的结构也是类似于双端的链表(模拟双向链表使用)
 * 3. 然后我们需要定义两个哈希表
 * 第一个HashMap中的val保存的是该数字的所在桶的地址(方便定位桶的位置进行操作)
 * 第二个HashMap中的val保存的是该数字本身的ListNode的地址(方便进行删除操作)
 * 4. 返回使用量最小的元素就去最左边的桶里面拿队列中的first元素
 * 5. 桶的左右两侧我们定义了0操作次数, 以及Integer.MAX_VALUE桶, 减少了边界位置的判断
 */

class LFUCache {

    /**
     * 基础的节点的元素定义
     */
    static class Node {

        int key;
        int val;
        Node prev;
        Node next;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    /**
     * 模拟的队列的实现类
     */
    static class MQueue {
        //属性定义为这样是为了和下面的桶结构区分开
        Node head;
        Node tail;
        int size;

        //无参数的构造方法
        public MQueue() {
            this.head = null;
            this.tail = null;
        }

        //remove方法用来移除掉指定位置的节点
        public Node remove(Node node) {
            if (head == null || tail == null) {
                throw new RuntimeException("没有节点无法移动");
            }

            if (head == tail) {
                tail = null;
                head = null;
            } else if (node == this.head) {
                Node temp = this.head.next;
                this.head.next = null;
                this.head = temp;
                this.head.prev = null;
            } else if (node == tail) {
                Node temp = tail.prev;
                tail.prev = null;
                tail = temp;
                tail.next = null;
            } else {
                node.prev.next = node.next;
                node.next.prev = node.prev;
                node.next = null;
                node.prev = null;
            }
            size--;
            return node;
        }

        //下面是我们的pop方法
        public Node pop() {
            return remove(head);
        }

        //下面是我们的offer()方法
        public void offer(Node node) {
            if (head == null && tail == null) {
                head = node;
                tail = node;
                size++;
                return;
            }
            tail.next = node;
            node.prev = tail;
            tail = node;
            size++;
        }
    }

    /**
     * 下面我们要完成我们的桶结构的实现(桶结构的val是出现的次数, 桶结构里面有一个我们手写的一个队列结构)
     */
    static class Bucket {
        //出现的频率
        int time;
        //我们想要的队列结构
        MQueue mQueue;
        //模拟双向链表必须的实现
        Bucket prev;
        Bucket next;

        //构造方法
        public Bucket(int time) {
            this.time = time;
            mQueue = new MQueue();
        }
    }

    static class MBucket {

        //用作基石的两个桶
        Bucket min = new Bucket(0);
        Bucket max = new Bucket(Integer.MAX_VALUE);

        public MBucket(){
            min.next = max;
            max.prev = min;
        }

        //桶的插入插入操作(cur是参照结构)
        public void insert(Bucket cur, Bucket pos) {
            cur.next.prev = pos;
            pos.next = cur.next;
            cur.next = pos;
            pos.prev = cur;
        }

        //桶的删除操作
        public void remove(Bucket cur) {
            cur.prev.next = cur.next;
            cur.next.prev = cur.prev;
        }
    }


    //那个容量大小
    int capacity;

    //两个重要的HashMap结构
    HashMap<Integer, Node> nodeIndexMap = new HashMap<>();
    HashMap<Integer, Bucket> bucketIndexMap = new HashMap<>();

    MBucket mBucket = new MBucket();

    public LFUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        if (!nodeIndexMap.containsKey(key)) {
            return -1;
        } else {
            Bucket curBucket = bucketIndexMap.get(key);
            Node curNode = nodeIndexMap.get(key);
            if (curBucket.next.time > curBucket.time + 1) {
                Bucket newBucket = new Bucket(curBucket.time + 1);
                mBucket.insert(curBucket, newBucket);
                curBucket.mQueue.remove(curNode);
                newBucket.mQueue.offer(curNode);
            } else {
                curBucket.mQueue.remove(curNode);
                curBucket.next.mQueue.offer(curNode);
            }
            bucketIndexMap.put(key, curBucket.next);
            //判断先前的那个桶是不是空的(是空桶就删掉)
            if (curBucket.mQueue.size == 0) {
                mBucket.remove(curBucket);
            }
            return curNode.val;
        }
    }

    public void put(int key, int value) {
        if (nodeIndexMap.containsKey(key)) {
            Node curNode = nodeIndexMap.get(key);
            Bucket curBucket = bucketIndexMap.get(key);
            curNode.val = value;
            if (curBucket.next.time > curBucket.time + 1) {
                Bucket newBucket = new Bucket(curBucket.time + 1);
                mBucket.insert(curBucket, newBucket);
                curBucket.mQueue.remove(curNode);
                newBucket.mQueue.offer(curNode);
            } else {
                curBucket.mQueue.remove(curNode);
                curBucket.next.mQueue.offer(curNode);
            }
            bucketIndexMap.put(key, curBucket.next);
            //判断先前的那个桶是不是空的(是空桶就删掉)
            if (curBucket.mQueue.size == 0) {
                mBucket.remove(curBucket);
            }
        } else {
            Node newNode = new Node(key, value);
            if (nodeIndexMap.size() < capacity) {
                if (mBucket.min.next.time != 1) {
                    Bucket newBucket = new Bucket(1);
                    newBucket.mQueue.offer(newNode);
                    mBucket.insert(mBucket.min, newBucket);
                    nodeIndexMap.put(key, newNode);
                    bucketIndexMap.put(key, newBucket);
                } else {
                    Bucket oneBucket = mBucket.min.next;
                    oneBucket.mQueue.offer(newNode);
                    nodeIndexMap.put(key, newNode);
                    bucketIndexMap.put(key, oneBucket);
                }
            } else {
                Node removeNode = mBucket.min.next.mQueue.pop();
                nodeIndexMap.remove(removeNode.key);
                bucketIndexMap.remove(removeNode.key);
                if(mBucket.min.next.mQueue.size == 0){
                    mBucket.remove(mBucket.min.next);
                }
                if(mBucket.min.next.time != 1){
                    Bucket newBucket = new Bucket(1);
                    newBucket.mQueue.offer(newNode);
                    mBucket.insert(mBucket.min, newBucket);
                    nodeIndexMap.put(key, newNode);
                    bucketIndexMap.put(key, newBucket);
                }else{
                    Bucket oneBucket = mBucket.min.next;
                    oneBucket.mQueue.offer(newNode);
                    nodeIndexMap.put(key, newNode);
                    bucketIndexMap.put(key, oneBucket);
                }
            }
        }
    }

    public static void main(String[] args) {
        LFUCache lfu = new LFUCache(2);
        lfu.put(1,1);
        lfu.put(2,2);
        lfu.get(1);
        lfu.put(3,3);
        lfu.get(2);
        lfu.get(3);
        lfu.put(4,4);
        lfu.get(1);
        lfu.get(3);
        lfu.get(4);
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/781363.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QCustomPlot+ vs2022+ qt

零、printSupport 步骤一&#xff1a;下载QCustomPlot 访问QCustomPlot的官网 QCustomPlot 下载最新版本的源代码。 步骤二&#xff1a;配置项目 创建新的Qt项目&#xff1a; 打开VS2022&#xff0c;创建一个新的Qt Widgets Application项目。 将QCustomPlot源代码添加到项目…

集合复习(java)

文章目录 Collection 接口Collection结构图Collection接口中的方法Iterator 与 Iterable 接口Collection集合遍历方式迭代器遍历增强 for 遍历 List&#xff08;线性表&#xff09;List特有方法ArrayList&#xff08;可变数组&#xff09;ArrayList 底层原理ArrayList 底层原理…

【UML用户指南】-30-对体系结构建模-模式和框架

目录 1、机制 2、框架 3、常用建模技术 3.1、对设计模式建模 3.2、对体系结构模式建模 用模式来详述形成系统体系结构的机制和框架。通过清晰地标识模式的槽、标签、按钮和刻度盘 在UML中&#xff0c; 对设计模式&#xff08;也叫做机制&#xff09;建模&#xff0c;将它…

前端技术(三)—— javasctipt 介绍:jQuery方法和点击事件介绍(补充)

6. 常用方法 ● addClass() 为jQuery对象添加一个或多个class <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Efficient Contrastive Learning for Fast and Accurate Inference on Graphs

发表于:ICML24 推荐指数: #paper/⭐⭐⭐ 创新点一颗星,证明三颗星(证明的不错,值得借鉴,但是思路只能说还行吧) 如图, 本文采取的创新点就是MLP用原始节点,GCN用邻居节点的对比学习.这样,可以加快运算速度 L E C L − 1 ∣ V ∣ ∑ v ∈ V 1 ∣ N ( v ) ∣ ∑ u ∈ N ( v )…

汇聚荣拼多多电商的技巧有哪些?

在电商平台上&#xff0c;汇聚荣拼多多以其独特的商业模式和创新的营销策略吸引了大量消费者。那么&#xff0c;如何在这样一个竞争激烈的平台上脱颖而出&#xff0c;成为销售佼佼者呢?本文将深入探讨汇聚荣拼多多电商的成功技巧。 一、精准定位目标客户群体 首先&#xff0c;…

Android增量更新----java版

一、背景 开发过程中&#xff0c;随着apk包越来越大&#xff0c;全量更新会使得耗时&#xff0c;同时浪费流量&#xff0c;为了节省时间&#xff0c;使用增量更新解决。网上很多文章都不是很清楚&#xff0c;没有手把手教学&#xff0c;使得很多初学者&#xff0c;摸不着头脑&a…

【Threejs进阶教程-优化篇】4.Vue/React与threejs如何解决冲突和卡顿(续)

Vue/React与threejs如何解决冲突和卡顿-续 使用说明核心思路环境搭建(vuethree)vue运行机制分析业务分离使用threejs做背景 3D模块封装使用ES6的Class来让逻辑性更强Threejs尽量按需引入创建一个类扩展写法本次代码执行顺序 扩展内容添加orbitControls和辅助线解决事件覆盖 与V…

MUX VLAN实现二层流量的弹性管控

一、模拟场景&#xff0c;企业有一台服务器&#xff0c;部门A&#xff0c;部门B&#xff0c;访客 二、要求&#xff1a;三者都可以访问服务器&#xff0c;部门A和B可以进行部门内部通信&#xff0c;A和B不可以通信&#xff0c;访客只能访问服务器 三、拓扑如下图 四、配置流程…

UE5 05-利用 timeline 插值运动

理解成 unity Dotween DoMove 插值运动即可 AddTimeLine 节点 物体插值运动 物体插值缩放 一个timeline 可以K多个动画帧

【js基础巩固】深入理解作用域与作用域链

作用域链 先看一段代码&#xff0c;下面代码输出的结果是什么&#xff1f; function bar() {console.log(myName) } function foo() {var myName "极客邦"bar() } var myName "极客时间" foo()当执行到 console.log(myName) 这句代码的时候&#xff0c…

25_嵌入式系统总线接口

目录 串行接口基本原理 串行通信 串行数据传送模式 串行通信方式 RS-232串行接口 RS-422串行接口 RS-485串行接口 RS串行总线总结 RapidIO高速串行总线 ARINC429总线 并行接口基本原理 并行通信 IEEE488总线 SCSI总线 MXI总线 PCI接口基本原理 PCI总线原理 PC…

Qt | QPen 类(画笔)

01、画笔基础 1、需要使用到的 QPainter 类中的函数原型如下: void setPen(const QPen &pen); //设置画笔,void setPen(const QColor &color); //设置画笔,该笔样式为 Qt::SolidLine、宽度为 1,颜色由 color 指定void setPen(Qt::PenStyle style); //设置画笔,该…

【问题解决】 pyocd 报错 No USB backend found 的解决方法

pyocd 报错 No USB backend found 的解决方法 本文记录了我在Windows 10系统上遇到的pyocd命令执行报错——No USB backend found 的分析过程和解决方法。遇到类似问题的朋友可以直接参考最后的解决方法&#xff0c;向了解问题发送原因的可以查看原因分析部分。 文章目录 pyoc…

90元搭建渗透/攻防利器盒子!【硬件篇】

前言 以下内容请自行思考后进行实践。 使用场景 在某些情况下开软件进行IP代理很麻烦&#xff0c;并不能实现真正全局&#xff0c;而且还老容易忘记&#xff0c;那么为了在实景工作中&#xff0c;防止蓝队猴子封IP&#xff0c;此文正现。 正文 先说一下实验效果&#xff1…

Java 应用启动时出现编译错误进程会退出吗?

背景 开发的尽头是啥呢&#xff1f;超超级熟练工&#xff01; 总结最近遇到的一些简单问题&#xff1a; Java 应用的某个线程&#xff0c;如果运行时依赖的 jar 不满足&#xff0c;线程是否会退出&#xff1f;进程是否会退出&#xff1f;Netty 实现 TCP 功能时&#xff0c;换…

STL复习-序列式容器和容器适配器部分

STL复习 1. 常见的容器 如何介绍这些容器&#xff0c;分别从常见接口&#xff0c;迭代器类型&#xff0c;底层实现 序列式容器 string string严格来说不属于stl&#xff0c;它是属于C标准库 **底层实现&#xff1a;**string本质是char类型的顺序表&#xff0c;因为不同编译…

CC2530寄存器编程学习笔记_点灯

下面是我的CC2530的学习笔记之点灯部分。 第一步&#xff1a;分析原理图 找到需要对应操作的硬件 图 1 通过这个图1我们可以找到LED1和LED2连接的引脚&#xff0c;分别是P1_0和P1_1。 第二步 分析原理图 图 2 通过图2 确认P1_0和P1_1引脚连接到LED&#xff0c;并且这些引…

项目/代码规范与Apifox介绍使用

目录 目录 一、项目规范&#xff1a; &#xff08;一&#xff09;项目结构&#xff1a; &#xff08;二&#xff09;传送的数据对象体 二、代码规范&#xff1a; &#xff08;一&#xff09;数据库命名规范&#xff1a; &#xff08;二&#xff09;注释规范&#xff1a; …

【0基础学爬虫】爬虫框架之 feapder 的使用

前言 大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0…