数据结构之-队列实现

news2024/11/29 1:33:24

队列是一个先进先出的结构,可以用链表呀,数组来实现它,我们今天用数组实现个队列,以优先级队列方式,我们看看怎么实现,优先级队列以队列存储时规则会将即将过期的或较小的数据存储在前面,这样取出时则取头部数据即可。

优先级队列采用数组实现的完全二叉树,根据二叉树规则,在插入的时候对比,保证父节点要比子节点小就ok。

我们主要来看下入队出队的一个实现,需要定义接口,咱们来定义基本方法。

1.实现入队用add或offer,这里超出队列界限抛出异常什么的都不处理,这里add会调用offer方法

2.出队使用poll方法则是移除队列头部并返回队列头部数据,使用peek方法只得到头部数据不处理移除头部数据。

/**
 * @Author df
 * @Date 2022/11/28 16:48
 * @Version 1.0
 * java泛型中标记符含义
 * E element  (元素,集合中使用)
 * T type  (java类)
 * K key   (键)
 * V value (值)
 * N number(数值)
 * ? 表示不确定的java类型
 */
public interface Queue<E> {
    // 入队,超出队列界限抛出异常处理
    boolean add(E e);

    // 入队,超出队列界限直接返回false
    boolean offer(E e);

    // 将首个队列元素弹出,如果空为空
    E poll();
    // 查询首个队列,不移除首个队列,如果队列为空抛出异常
    E peek();
}

定义实现方法 PriorityQueue,准备一下,我们需要一个数组,这个数组我们就叫queue吧,还需要一个默认的容量就叫DEFAULT_INITIAL_CAPACITY=11,还需要个长度元素,记录数组长度,就叫size把,在实例化时,就会开辟默认数组空间

public class PriorityQueue<E> implements Queue<E> {    
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 用数组实现队列
    transient Object[] queue;
    private int size = 0;

    // 默认
    public PriorityQueue() {
        queue = new Object[DEFAULT_INITIAL_CAPACITY];
    }
}

准备工作做好就需要入队列了

1.入队

入队前我们先想想,我们怎么存储才能按顺序二叉树规则存储?

那么假如我存储一个3,假如在数组queue[0]直接插入3,我再存储个2,我直接在数组queue[1]存储2呢,我在插入个数据1,直接在queue[2]插入1,那么就形成了这样的二叉树  

                                                 3

                                          2            1

但是这不符合二叉树的规则,父节点要比任意子节点要小,现在父节点是3,它比其他子节点都要大,所以这样不对。我们要考虑在添加的时候需要对比,然后存储对应的位置就好了

如第一个元素3直接存储,第二个元素存储时则找到父节点值进行比对,3比2大则替换,将queue[0]变为2,queue[1]变为3,再存储1时则跟父节点queue[0]对比,1小于2则替换,

                                               1

                                       3            2

再新添加元素还是一样的方式,如果此时存储进来5对比以后比前3个位置都大,那么就存储queue[3],6就存储queue[4]而queue[1]就是它的父节点

                                              1

                                      3              2

                                  5      6     

你此时可能在想5和6是3的子节点,如果数据渐渐变大,怎么依次找他们自己的父节点呢,这就引申出来一个公式

父节点=  (n-1)/2     5的坐标为3,(3-1)/2=1  queue[1]就是queue[3]的父亲节点

添加操作,add()调用offer(),offer方法用来判断是否超出当前队列容量进行扩容调用grow(),以及调用siftUp()用来处理子父节点判断并替换操作。

offer():先判断是否扩容,默认进来size为0,queue队列长度是11个空值,所以是不用扩容,因为要往队列存储值了,所以设置size为i+1,然后判断是不是第一次存储值,第一次存储值就直接在queue[0]直接存储,如不是,则需要进入siftUp();

siftUp():参数传当前待插入的位置i,当前元素e,进入siftUpComparable();

siftUpComparable():先把当前的元素转换为Comparable,这样就可以进行值的比对,我们最重要的点就是通过当前元素位置找到父节点位置的值,然后进行比对,子节点小于父节点,就需要替换元素。

需用到公式找父节点,(k-1)/2 ,k>0  ,假如现在k为1,那就是(1-1)/2=0,先存储3,再存储2,那么2的父节点就是3,取出父节点的值进行比对,发现当前元素大于父节点,不退出循环,直接替换queue[k=1]=3,k=0,此时while条件无法满足,执行queue[k=0]=2,这样就可以保证父节点最小

    @Override
    public boolean add(E e) {
        return offer(e);
    }


    @Override
    public boolean offer(E e) {
        int i = size;
        // 队列不足,进行扩容
        if (i >= queue.length) {
            grow(i + 1);
        }
        size = i + 1;
        if (i == 0) {
            queue[0] = e;
        } else {
            siftUp(i, e);
        }
        return true;
    }

    // 扩容
    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // 开辟新的容量,如果小于64就队列长度+队列长度+2也就是说11+11+2=24,大于64就需要队列长度+队列长度/2
        int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
        // 判断是不是接近int值的最大值
        if (newCapacity - (Integer.MAX_VALUE - 8) > 0) {
            newCapacity = (minCapacity > Integer.MAX_VALUE - 8) ?
                    Integer.MAX_VALUE :
                    Integer.MAX_VALUE - 8;
        }
        // 开辟新的空间
        queue = Arrays.copyOf(queue, newCapacity);
    }


 // 对比并替换存储值
    private void siftUp(int k, E x) {
        siftUpComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        System.out.println("【入队】元素:" + key + " 当前队列:" + Arrays.toString(queue));
        while (k > 0) {
            //得到当前元素的父节点, >>>除2取整
            int parent = (k - 1) >>> 1;
            // 得到父节点的值
            Object e = queue[parent];
            // 当前元素大于父结点,不进行替换退出循环
            if (key.compareTo((E) e) > 0) {
                break;
            }
            // 当前元素比父节点大,则先给当前位置赋值为父结点的值
            System.out.println("【入队】替换过程,父子节点位置替换,继续循环。父节点值:" + e + " 存放到位置:" + k);
            queue[k] = e;
            // 把父节点的小标赋值给k
            k = parent;
        }
        queue[k] = key;
        System.out.println("【入队】完成 Idx:" + k + " Val:" + key + " \r\n当前队列: \r\n" + Arrays.toString(queue));
    }

main方法测试下存储

 public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.add(1);
        queue.add(3);
        queue.add(5);
        queue.add(11);
        queue.add(4);
        queue.add(6);
        queue.add(7);
        queue.add(12);
        queue.add(15);
        queue.add(10);
        queue.add(9);
        queue.add(8);
        queue.add(2);
  }

控制台输出

【入队】元素:3 当前队列:[1, null, null, null, null, null, null, null, null, null, null]
【入队】完成 Idx:1 Val:3 
当前队列: 
[1, 3, null, null, null, null, null, null, null, null, null]
【入队】元素:5 当前队列:[1, 3, null, null, null, null, null, null, null, null, null]
【入队】完成 Idx:2 Val:5 
当前队列: 
[1, 3, 5, null, null, null, null, null, null, null, null]
【入队】元素:11 当前队列:[1, 3, 5, null, null, null, null, null, null, null, null]
【入队】完成 Idx:3 Val:11 
当前队列: 
[1, 3, 5, 11, null, null, null, null, null, null, null]
【入队】元素:4 当前队列:[1, 3, 5, 11, null, null, null, null, null, null, null]
【入队】完成 Idx:4 Val:4 
当前队列: 
[1, 3, 5, 11, 4, null, null, null, null, null, null]
【入队】元素:6 当前队列:[1, 3, 5, 11, 4, null, null, null, null, null, null]
【入队】完成 Idx:5 Val:6 
当前队列: 
[1, 3, 5, 11, 4, 6, null, null, null, null, null]
【入队】元素:7 当前队列:[1, 3, 5, 11, 4, 6, null, null, null, null, null]
【入队】完成 Idx:6 Val:7 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, null, null, null, null]
【入队】元素:12 当前队列:[1, 3, 5, 11, 4, 6, 7, null, null, null, null]
【入队】完成 Idx:7 Val:12 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, 12, null, null, null]
【入队】元素:15 当前队列:[1, 3, 5, 11, 4, 6, 7, 12, null, null, null]
【入队】完成 Idx:8 Val:15 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, 12, 15, null, null]
【入队】元素:10 当前队列:[1, 3, 5, 11, 4, 6, 7, 12, 15, null, null]
【入队】完成 Idx:9 Val:10 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, null]
【入队】元素:9 当前队列:[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, null]
【入队】完成 Idx:10 Val:9 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, 9]
【入队】元素:8 当前队列:[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, 9, null, null, null, null, null, null, null, null, null, null, null, null, null]
【入队】完成 Idx:11 Val:8 
当前队列: 
[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, 9, 8, null, null, null, null, null, null, null, null, null, null, null, null]
【入队】元素:2 当前队列:[1, 3, 5, 11, 4, 6, 7, 12, 15, 10, 9, 8, null, null, null, null, null, null, null, null, null, null, null, null]
【入队】替换过程,父子节点位置替换,继续循环。父节点值:6 存放到位置:12
【入队】替换过程,父子节点位置替换,继续循环。父节点值:5 存放到位置:5
【入队】完成 Idx:2 Val:2 
当前队列: 
[1, 3, 2, 11, 4, 5, 7, 12, 15, 10, 9, 8, 6, null, null, null, null, null, null, null, null, null, null, null]

入队处理完了,我们处理出队的逻辑,

2.出队

出队从队列的头部,也就是queue[0]取出数据就可以了,但是还有个问题,你取出来第一个你不能让queue[0]为空呀,下次再调用poll或peek不是取不出来剩余的队列数据了吗,所以还需要操作需要找到在左子树与右子树之间最小的值,放到queue[0],并调整子树的值与队列最后一个值对比(头部弹出相当于删除一个,取最后位置删除,所以把最后一个值拿出对比,放入树中合适的位置),可以看下下图所示步骤,

假如将1弹出,则需要找出它的左子树和右子树,

左子树公式为:n*2+1                             右子树:左子树的合+1;

那么找到了3和5,比对左子树的值和右子树的值3最小,所以将3替换到queue[0]位置上,然后继续找原来3(queue[1])的子节点,找到了左子树queue[3],右子树queue[4],对比4比11小,将4移动到queue[1],找到原来4(queue[4)的子节点,左子树queue[9],右子树queue[10],10和9对比9比较小,但是和最后一个值比对9比8大,需要把8移动到queue[4]的位置上,9就不动,把最后一个值置为空,就调整完毕了。还是蛮妙的把

                                           图片来源:小傅哥-图解数据结构

 代码实现

poll():因为要移除所以size就会减1操作,将队列第一个结果取出并返回,取出队列最后一个值,把最后一个值置为空,然后把0和最后一个值当目标值传给siftDown

siftDownComparable():这个方法的核心和上边是一样的,先找到节点的左右节点,比对大小,选中最小的,往上部放,再找选中的节点的子节点继续比对,直到都调整完毕。

 @Override
    public E poll() {
        if (size == 0) {
            return null;
        }
        int s = --size;
        E result = (E) queue[0];
        // 取出队列最后一个的值
        E x = (E) queue[s];
        // 将队列最后一个值置为空
        queue[s] = null;
        if (s != 0) {
            siftDown(0, x);
        }
        return result;
    }

    private void siftDown(int k, E x) {
        siftDownComparable(k, x);
    }


    /**
     * 移除节点,需要把队列头部数据移除,但是我们移除完毕还需要按照规则选择新的头部节点
     * 新的头部节点要满足比子节点小,怎么对比处理需要思考
     * <p>
     * 我们先需要以队头为首,找到它的树子节点(左右节点),找到最小的值认定为它是新的队头,此时
     * 再去找当前最小值的子节点(左右节点),继续对比大小,以小的为主,当队头的子节点,
     * 最后以最后一个值为目标值,找到的最小子节点跟目标值对比,大于目标值退出循环,小于目标值替换值
     * 直到达到目的。最主要目的是保持树的规则不破坏,
     * 由于插入时同节点大小没有比对,所以删除时,左右树节点进行比对取最小值当父节点,最终把目标值
     * 放入适当的树节点下。
     * 找当前的左子节点,公式  n*2+1         找右子节点  左子节点的合+1;
     *
     * 下行代码是对比替换的过程。
     */
    @SuppressWarnings("unchecked")
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        // 先找出中间节点
        int half = size >>> 1;
        while (k < half) {
            // 找到左子节点下标,当前节点*2+1
            int child = (k << 1) + 1;
            Object c = queue[child];
            // 找到右子节点下标,左子节点基础上+1就可
            int right = child + 1;
            // 左右节点的值进行比对,取最小的值赋值
            if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) {
                c = queue[child = right];
            }
            // 目标值与C值比较,目标值小于C值退出循环
            if (key.compareTo((E) c) <= 0) {
                break;
            }
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

    // 取出队列头部元素
    @Override
    public E peek() {
        return size == 0 ? null : (E) queue[0];
    }

我们测试删除两个队列查看变化:

public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.add(1);
        queue.add(3);
        queue.add(5);
        queue.add(11);
        queue.add(4);
        queue.add(6);
        queue.add(7);
        queue.add(12);
        queue.add(15);
        queue.add(10);
        queue.add(9);
        queue.add(8);
        queue.add(2);

        queue.poll();
        System.out.println("【出队1】当前队列:" + Arrays.toString(queue.queue));
        queue.poll();
        System.out.println("【出队3】当前队列:" + Arrays.toString(queue.queue));
    }

打印的结果 

 这个数组结构的二叉树设计还是蛮妙的,代码又很简洁,很佩服java代码的大神,需要细细品味,如果不太懂,可以打断点走流程看,相信你一定能有所收获。

完整代码:


/**
 * @Author df
 * @Date 2022/11/28 16:56
 * @Version 1.0
 * <p>
 * 假如一个3进来就会存储根节点就是queue[0]的位置,在进来个2,按照二叉树性质
 * 需要知道根节点并根据跟节点的值进行比对,发现2比3小,就会交换值,将2存储queue[0],将3存储到queue[1],
 * 那么就形成   2
 *          3
 * 在存储一个5,跟父节点对比不大于根节点存储queue[2]位置,那么就形成
 *            2
 *          3   5
 * 再存储一个6,跟父节点3比它大,存储queue[3],所以就形成了
 *            2
 *          3   5
 *        6
 * 再存储一个7,存储queue[4],所以就形成了
 *           2
 *         3   5
 *       6   7
 * 从这里你也能看到规律,找父节点先n-1除以2就可以得到自己的父节点
 * 查找父节点--------------------------------------------
 * 如找下标1的父节点,则利用公式  1-1/2得到0,所以下标0的值就是下标1父亲,
 * 如找下标2的父节点,则利用公式  2-1/2得到0,所以下标0的值就是下标2父亲,
 * 如找下标3的父节点,则利用公式  3-1/2得到1,所以下标1的值就是下标3父亲,
 * 以此类推。。。都能找到自身的父亲,除以2得到的数需要取整
 * 第二个规律是父节点的值小于等于任意孩子节点,同节点之间节点不予维护
 * <p>
 * 优先队列采用完全二叉树,在存储时就会进行数据的对比小的存储左边,大的存储右边
 * 以数组形式存储
 */
public class PriorityQueue<E> implements Queue<E> {
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 用数组实现队列
    transient Object[] queue;
    private int size = 0;

    // 默认
    public PriorityQueue() {
        queue = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    //private Logger logger= LoggerFactory.getLogger(PriorityQueue.class);
    @Override
    public boolean add(E e) {
        return offer(e);
    }

    // 扩容
    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // 开辟新的容量,如果小于64就队列长度+队列长度+2也就是说11+11+2=24,大于64就需要队列长度+队列长度/2
        int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
        // 判断是不是接近int值的最大值
        if (newCapacity - (Integer.MAX_VALUE - 8) > 0) {
            newCapacity = (minCapacity > Integer.MAX_VALUE - 8) ?
                    Integer.MAX_VALUE :
                    Integer.MAX_VALUE - 8;
        }
        // 开辟新的空间
        queue = Arrays.copyOf(queue, newCapacity);
    }

    // 对比并替换存储值
    private void siftUp(int k, E x) {
        siftUpComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        System.out.println("【入队】元素:" + key + " 当前队列:" + Arrays.toString(queue));
        while (k > 0) {
            //得到当前元素的父节点, >>>除2取整
            int parent = (k - 1) >>> 1;
            // 得到父节点的值
            Object e = queue[parent];
            // 当前元素大于父结点,不进行替换退出循环
            if (key.compareTo((E) e) > 0) {
                break;
            }
            // 当前元素比父节点大,则先给当前位置赋值为父结点的值
            System.out.println("【入队】替换过程,父子节点位置替换,继续循环。父节点值:" + e + " 存放到位置:" + k);
            queue[k] = e;
            // 把父节点的小标赋值给k
            k = parent;
        }
        queue[k] = key;
        System.out.println("【入队】完成 Idx:" + k + " Val:" + key + " \r\n当前队列: \r\n" + Arrays.toString(queue));
    }

    @Override
    public boolean offer(E e) {
        int i = size;
        // 队列不足,进行扩容
        if (i >= queue.length) {
            grow(i + 1);
        }
        size = i + 1;
        if (i == 0) {
            queue[0] = e;
        } else {
            siftUp(i, e);
        }
        return true;
    }

    @Override
    public E poll() {
        if (size == 0) {
            return null;
        }
        int s = --size;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0) {
            siftDown(0, x);
        }
        return result;
    }

    private void siftDown(int k, E x) {
        siftDownComparable(k, x);
    }


    /**
     * 移除节点,需要把队列头部数据移除,但是我们移除完毕还需要按照规则选择新的头部节点
     * 新的头部节点要满足比子节点小,怎么对比处理需要思考
     * <p>
     * 我们先需要以队头为首,找到它的树子节点(左右节点),找到最小的值认定为它是新的队头,此时
     * 再去找当前最小值的子节点(左右节点),继续对比大小,以小的为主,当队头的子节点,
     * 最后以最后一个值为目标值,找到的最小子节点跟目标值对比,大于目标值退出循环,小于目标值替换值
     * 直到达到目的。最主要目的是保持树的规则不破坏,
     * 由于插入时同节点大小没有比对,所以删除时,左右树节点进行比对取最小值当父节点,最终把目标值
     * 放入适当的树节点下。
     * 找当前的左子节点,公式  n*2+1         找右子节点  左子节点的合+1;
     *
     * 下行代码是对比替换的过程。
     */
    @SuppressWarnings("unchecked")
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        // 先找出中间节点
        int half = size >>> 1;
        while (k < half) {
            // 找到左子节点下标,当前节点*2+1
            int child = (k << 1) + 1;
            Object c = queue[child];
            // 找到右子节点下标,左子节点基础上+1就可
            int right = child + 1;
            // 左右节点的值进行比对,取最小的值赋值
            if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) {
                c = queue[child = right];
            }
            // 目标值与C值比较,目标值小于C值退出循环
            if (key.compareTo((E) c) <= 0) {
                break;
            }
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

    // 取出队列头部元素
    @Override
    public E peek() {
        return size == 0 ? null : (E) queue[0];
    }

    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.add(1);
        queue.add(3);
        queue.add(5);
        queue.add(11);
        queue.add(4);
        queue.add(6);
        queue.add(7);
        queue.add(12);
        queue.add(15);
        queue.add(10);
        queue.add(9);
        queue.add(8);
        queue.add(2);

        queue.poll();
        System.out.println("【出队1】当前队列:" + Arrays.toString(queue.queue));
        queue.poll();
        System.out.println("【出队3】当前队列:" + Arrays.toString(queue.queue));
    }

    /**
     *                   1
     *              3            2
     *         11       4     5     7
     *      12   15  10  9  8  6
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *                    2
     *          3                5
     *      11      4         6     7
     *    12  15  10  9     8
     *
     *
     *     * 参数:0,8  去掉下标11==null
     *      * 1.队列长度   12/2=6
     *      * 2.找左子节下标  0*2+1=1   child=1   value=3
     *      * 3.找右子节下标  1+1=2     right=2   value=5
     *      * 4.左右节点对比找最小的值 3
     *      * 5.目标值跟3比对,目标值不小于3,开始替换
     *      * 6.queue[0]=3;  k=1
     *      *
     *      * 7.循环到上边第2个  1*2+1=3  child=3  value=11;
     *      * 8.循环到上边第3个  3+1=4    child=4  value=4;
     *      * 9.循环到上边第4个  4   child=4  c=4
     *      * 10.queue[1]=4  k=4
     *      *
     *      * 11.循环到上边第7个 4*2+1=9   child=9    value=10;
     *      * 12.循环到上边第8个 9+1=10;   child=10   value=9;
     *      * 13.循环到上边第9个  9   child=10  c=9
     *      * 14.9大于目标值退出循环
     *      * 15.queue[4]=8;
     *
     *                   3
     *          4               5
     *     11      8   6  7
     *   12  15  10 9
     *
     * 11/2=5
     * 0+1=1  4   2  5
     * 9>4 no
     * queue[0]=4
     * k=1
     *
     * 1*2+1=3,11   4,8
     * child=4  c=8
     * queue[1]=8;
     * k=4;
     *
     * 4*2+1=9,10     10,9
     * child=10  c=9
     * 退出循环
     * queue[4]=9
     *
     *                  4
     *         8                5
     *     11      9       6         7
     *  12   15  10
     *
     *
     * */
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【生成式网络】入门篇(四):CycleGAN 的 代码和结果记录

CycleGAN是一个里程碑式的工作&#xff0c;开启了unpaired的风格迁移的先河&#xff0c;斑马转马的效果还是很震惊。 具体原理可以参考 https://zhuanlan.zhihu.com/p/402819206 老习惯&#xff0c;直接上code&#xff0c;然后按照code进行一些解释 代码参考自 https://github.…

自托管书签管理器LinkAce

本文完成于 9 月下旬&#xff0c;当时的版本是 v1.10.4&#xff0c;发稿时最新版本为 v1.10.5 什么是 LinkAce &#xff1f; LinkAce 是一个自托管档案&#xff0c;用于收集您喜爱的网站的链接&#xff0c;并保存文章以供日后阅读。LinkAce 提供了一个长期存档来存储指向网站、…

NVIDIA 7th SkyHackathon(二)开发套件的安装与测试

1.NeMo 开源工具包 1.1 关于 NeMo NeMo&#xff08;Neural Modules&#xff09;是 NVIDIA 发布的基于 PyTorch 的开源工具包&#xff0c;它允许开发者快速构建、训练和微调会话式人工智能模型 NeMo 由 NeMo Core 和 NeMo Collection 组成&#xff0c;NeMo Core 为所有模型和…

[附源码]Python计算机毕业设计Django的中点游戏分享网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

内容理解之情感计算

导语 概念定义&#xff1a;情感计算是自然语言处理领域的重要研究方向之一&#xff0c;其目标是赋予计算机类似于人一样的观察、理解和生成各种情感表达的能力&#xff0c;它是一个高度综合化的跨学科领域&#xff0c;涉及计算机科学、心理学、社会学和认知科学等。通过多学科…

物联网卡有哪些类型

伴随着科学技术的不断升级&#xff0c;不断发展&#xff0c;5G和物联网必定成为未来生活的主角&#xff0c;而现在5G已经慢慢在我们生活越来越常见&#xff0c;这是我们肉眼可以看见的&#xff0c;其实物联网如同5G一样&#xff0c;在我们生活中也随处可见&#xff0c;如我们平…

谈谈Go语言中函数的本质

在很多编程语言中&#xff0c;都会有函数一说&#xff0c;今天我们来聊聊Go语言中的函数。 废话不多说&#xff0c;咱们直接上代码~ 代码 package mainimport "fmt"func main() {fmt.Printf("%T\n", function1)fmt.Printf("%T\n", function2)…

跨越速运如何构建实时统一的运单分析

作者&#xff1a;张杰&#xff0c;跨越速运大数据架构师&#xff08;本文为作者在 StarRocks Summit Asia 2022 上的分享&#xff09; 作为大型现代化综合速运企业&#xff0c;跨越速运拥有 3000 多家服务网点 &#xff0c;日均处理 30 多万票运单。海量运单数据涌来&#xff…

博科交换机使用

博科交换机使用 ip查询 博科交换机的默认IP地址是10.77.77.77&#xff0c;用户名admin&#xff0c;密码&#xff1a;password。 ipaddrshowip修改 ipaddrset端口查询 交换机的端口表示为&#xff08;A,B&#xff09;或者&#xff08;A,B;C,D&#xff09;。 A,C表示交换机的…

使用JLINK给GD32下载程序

使用JLINK给GD32下载程序关于GD32单片机需要的工具和软件包①Jlink仿真器一个②相关软件包下载准备①选择好芯片②在DEBUG中选择JLINK下载现象总结关于GD32单片机 GD32是兆易创新基于Arm Cortex-M内核和RISC-V内核&#xff0c;推出的32位通用微控制器&#xff0c;对比了下两者…

[附源码]Python计算机毕业设计SSM流浪宠物申领信息平台(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java中进制基础知识与算法题

本篇文章旨在给大家普及下计算机内部数据的机器级表示方式&#xff0c;即&#xff1a;二进制、八进制、十进制、十六进制… 对于进制&#xff0c;我们从小最先接触的是十进制&#xff0c;这个也是我们日常生活中应用最多的数值统计方式。然而&#xff0c;现实中我们感觉到的媒体…

python的opencv操作记录(十)——图像融合

文章目录前言opencv中的一个方法泊松融合图像梯度图像散度融合图像散度通过散度场进行图像重建泊松融合的一般逻辑前言 最近碰到一个项目上的难题&#xff0c;是要从电动显微镜对焦的多张图像进行融合。因为&#xff0c;显微镜物镜的景深范围较小&#xff0c;可能在同一视野中…

地理空间数据共享资源大汇总

1.全国12.5米分辨率ALOS高程数据 全国12.5米分辨率DEM数据&#xff0c;该数据由锐多宝的地理空间提供并进行分省裁剪与镶嵌&#xff0c;由测绘营地进行影像坐标系转换。 ALOS-12.5m高程数据参数&#xff1a; 覆盖范围&#xff1a;全国&#xff08;仅有小部分区域存在数据空白…

本机使用python操作hdfs搭建及常见问题

一.虚拟机安装CentOS7并配置共享文件夹 二.CentOS 7 上hadoop伪分布式搭建全流程完整教程 三.本机使用python操作hdfs搭建及常见问题 四.mapreduce搭建 五.mapper-reducer编程搭建 本机使用python操作hdfs搭建及常见问题一、环境搭建1.打开虚拟机系统&#xff0c;打开hadoop2.修…

【JavaScript 逆向】极验四代滑块验证码逆向分析

前言 相较于三代滑块&#xff0c;四代的逻辑流程更简短&#xff0c;底图没混淆&#xff0c;某些点校验不严格 声明 本文章中所有内容仅供学习交流&#xff0c;相关链接做了脱敏处理&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 案例目标 滑动验证码&…

[NAS] QNAP/威联通 常用设置和操作

&#x1f341;简介 QNap 产品是一种可扩展的数据存储解决方案。它们包括具有 1 到 30 个驱动器托架的设备&#xff0c;并提供 HDMI、Thunderbolt 2 和 USB 3.1 等连接选项&#xff0c;以及 802.11ac/a/n Wi-Fi 和高达每秒 40 Gb 的以太网。内置软件提供基本服务&#xff0c;例如…

WeetCode2滑动窗口系列

一丶[无重复字符的最长子串](3. 无重复字符的最长子串 - 力扣&#xff08;Leetcode&#xff09;)# 思路:# 维护一个窗口&#xff0c;窗口中不存在重复的字符&#xff0c;窗口右边界从第一个字符移动到最后&#xff0c;使用一个变量记录窗口大小的最大值 那么问题就变成了&…

浅谈h264和h265的区别

相比h264&#xff0c;压缩同样的视频获得同样的质量的情况下&#xff0c;h265可以做到压缩后的大小为前者的一半&#xff0c;但压缩时间复杂度增加。h264编码单元为宏块(MB)&#xff0c;最大划分为16x16&#xff0c;而h265编码单元为编码树单元(CTU)&#xff0c;最大划分为64x6…

C++ 不知算法系列之深入动态规划算法思想

1. 前言 前面写过一篇博文&#xff0c;介绍了什么是动态规划算法。动态规划算法的最大特点&#xff0c;原始问题可以通过分解成规模更小的子问题来解决&#xff0c;子问题之间互成依赖关系&#xff0c;先计算出来的子问题的结果会影响到后续子问题的结果。 有点类似于武侠片中…