恋上数据结构与算法之二叉堆

文章目录

    • 需求分析
    • Top K 问题
    • 堆的基本接口设计
    • 二叉堆(Binary Heap)
    • 最大堆
      • 添加
        • 思路
        • 交换位置的优化
        • 实现
      • 删除
        • 思路
        • 流程图解
        • 实现
      • replace
      • 批量建堆
        • 自上而下的上滤
        • 自下而上的下滤
        • 效率对比
        • 复杂度计算
        • 实现
      • 完整代码
    • 最小堆
    • 比较器解析
    • Top K 问题
      • 问题分析
      • 代码实现
      • 内部方法分析
      • 问题 2
    • 堆排序
      • 概念
      • 代码示例
        • 第一种 -- 降序
        • 第二种 -- 升序
      • 空间复杂度能否下降至 O(1)?
        • 示例代码分析
        • 示例代码分析

需求分析

在这里插入图片描述

Top K 问题

什么是 Top K 问题?

从海量数据中找出前 K 个数据。

  • 比如:从 100 万个整数中找出最大的 100 个整数
  • Top K 问题的解法之一:可以用数据结构 “” 来解决。

堆是一种【完全二叉树】,可以分为【最大堆】和【最小堆】。只要是堆,里面的元素就会具备可比较性。

  • 在最大堆中,父节点的值大于等于(>=)其子节点的值;
  • 在最小堆中,父节点的值小于等于(<=)其子节点的值。

在这里插入图片描述

堆的基本接口设计

public interface Heap<E> {int size();	// 元素的数量boolean isEmpty();	// 是否为空void clear();	// 清空void add(E element);	 // 添加元素E get();	// 获得堆顶元素E remove(); // 删除堆顶元素E replace(E element); // 删除堆顶元素的同时插入一个新元素
}

二叉堆(Binary Heap)

着重注意索引的规律

在这里插入图片描述

floor(向下取整):只取前面的整数。

最大堆

添加

思路

一步步往上与父节点比较,并进行位置交换。

在这里插入图片描述

交换位置的优化

一般交换位置需要 3 行代码,可以进一步优化

  • 将新添加节点备份,确定最终位置才摆放上去
  • 循环比较,交换父节点位置 -> 循环比较,单纯父节点下移,最后确定位置了直接覆盖
  • 省去了每次都交换位置并且覆盖的操作
实现
	@Overridepublic void add(E element) {elementNotNullCheck(element);ensureCapacity(size + 1);elements[size++] = element;siftUp(size - 1);}private void elementNotNullCheck(E element) {if (element == null) {throw new IllegalArgumentException("element must not be null");}}private void ensureCapacity(int capacity) {int oldCapacity = elements.length;if (oldCapacity >= capacity) {return;}// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);E[] newElements = (E[]) new Object[newCapacity];for (int i = 0; i < size; i++) {newElements[i] = elements[i];}elements = newElements;}/*** 让index位置的元素上滤* @param index*/private void siftUp(int index) {
//		E e = elements[index];
//		while (index > 0) {
//			int pindex = (index - 1) >> 1;
//			E p = elements[pindex];
//			if (compare(e, p) <= 0) return;
//			
//			// 交换index、pindex位置的内容
//			E tmp = elements[index];
//			elements[index] = elements[pindex];
//			elements[pindex] = tmp;
//			
//			// 重新赋值index
//			index = pindex;
//		}E element = elements[index];while (index > 0) {int parentIndex = (index - 1) >> 1;E parent = elements[parentIndex];if (compare(element, parent) <= 0) {break;}// 将父元素存储在index位置elements[index] = parent;// 重新赋值indexindex = parentIndex;}elements[index] = element;}

删除

思路

一般来说,如果我们要删除某个元素的话,我们通常会拿到最后一个元素先覆盖它的位置,然后再把最后一个元素删掉,相当于同学们直接将 43 的值覆盖掉 0 这个位置的值,要再把这个值清空。

为什么?

因为这个操作是 O(1) 级别的,删除最后一个元素。

具体流程如下图所示:
在这里插入图片描述

流程图解

在这里插入图片描述

实现
	@Overridepublic E remove() {emptyCheck();int lastIndex = --size;E root = elements[0];elements[0] = elements[lastIndex];elements[lastIndex] = null;siftDown(0);return root;}/*** 让index位置的元素下滤* @param index*/private void siftDown(int index) {E element = elements[index];int half = size >> 1;// 第一个叶子节点的索引 == 非叶子节点的数量// index < 第一个叶子节点的索引// 必须保证index位置是非叶子节点while (index < half) { // index的节点有2种情况// 1.只有左子节点// 2.同时有左右子节点// 默认为左子节点跟它进行比较int childIndex = (index << 1) + 1;E child = elements[childIndex];// 右子节点int rightIndex = childIndex + 1;// 选出左右子节点最大的那个if (rightIndex < size && compare(elements[rightIndex], child) > 0) {child = elements[childIndex = rightIndex];}if (compare(element, child) >= 0) {break;}// 将子节点存放到index位置elements[index] = child;// 重新设置indexindex = childIndex;}elements[index] = element;}

replace

接口:删除堆顶元素的同时插入一个新元素

	@Overridepublic E replace(E element) {elementNotNullCheck(element);E root = null;if (size == 0) {elements[0] = element;size++;} else {root = elements[0];elements[0] = element;siftDown(0);}return root;}

批量建堆

批量建堆,有 2 种做法

  1. 自上而下的上滤 – 本质是添加
  2. 自下而上的下滤 – 本质是删除

注意:【自上而下的滤】和【自下而上的滤】不可以批量建堆,因为执行起来对整体来说没有什么贡献,依然还是乱的。

自上而下的上滤

在这里插入图片描述

自下而上的下滤

在这里插入图片描述

效率对比
  • 如下图所示,显然是【自下而上的下滤】效率更高。
  • 可把图中蓝色部分看作是节点数量,箭头直线看作是工作量。
  • 最下层的节点最多,这一部分在【自下而上的下滤】中的工作量较小。

在这里插入图片描述

复杂度计算

深度之和 vs 高度之和

在这里插入图片描述

公式推导

在这里插入图片描述

实现

1、修改构造函数

	public BinaryHeap(E[] elements, Comparator<E> comparator)  {super(comparator);if (elements == null || elements.length == 0) {this.elements = (E[]) new Object[DEFAULT_CAPACITY];} else {size = elements.length;// this.elements = elements // 不能这么写,因为不安全int capacity = Math.max(elements.length, DEFAULT_CAPACITY);this.elements = (E[]) new Object[capacity];for (int i = 0; i < elements.length; i++) {this.elements[i] = elements[i];}// 批量建堆heapify();}}

解释:

this.elements = elements 会导致外部传进来的数组和堆内的数组挂钩,如果后续修改了外包数组的元素值,会影响批量建堆的输出。

2、批量建堆方法编写

	/*** 批量建堆*/private void heapify() {// 自上而下的上滤
//		for (int i = 1; i < size; i++) {
//			siftUp(i);
//		}// 自下而上的下滤for (int i = (size >> 1) - 1; i >= 0; i--) {siftDown(i);}}

完整代码

/*** 二叉堆(最大堆)*/
@SuppressWarnings("unchecked")
public class BinaryHeap<E> extends AbstractHeap<E> implements BinaryTreeInfo {private E[] elements;private static final int DEFAULT_CAPACITY = 10;public BinaryHeap(E[] elements, Comparator<E> comparator)  {super(comparator);if (elements == null || elements.length == 0) {this.elements = (E[]) new Object[DEFAULT_CAPACITY];} else {size = elements.length;int capacity = Math.max(elements.length, DEFAULT_CAPACITY);this.elements = (E[]) new Object[capacity];for (int i = 0; i < elements.length; i++) {this.elements[i] = elements[i];}heapify();}}public BinaryHeap(E[] elements)  {this(elements, null);}public BinaryHeap(Comparator<E> comparator) {this(null, comparator);}public BinaryHeap() {this(null, null);}@Overridepublic void clear() {for (int i = 0; i < size; i++) {elements[i] = null;}size = 0;}@Overridepublic void add(E element) {elementNotNullCheck(element);ensureCapacity(size + 1);elements[size++] = element;siftUp(size - 1);}@Overridepublic E get() {emptyCheck();return elements[0];}@Overridepublic E remove() {emptyCheck();int lastIndex = --size;E root = elements[0];elements[0] = elements[lastIndex];elements[lastIndex] = null;siftDown(0);return root;}@Overridepublic E replace(E element) {elementNotNullCheck(element);E root = null;if (size == 0) {elements[0] = element;size++;} else {root = elements[0];elements[0] = element;siftDown(0);}return root;}/*** 批量建堆*/private void heapify() {// 自上而下的上滤
//		for (int i = 1; i < size; i++) {
//			siftUp(i);
//		}// 自下而上的下滤for (int i = (size >> 1) - 1; i >= 0; i--) {siftDown(i);}}/*** 让index位置的元素下滤* @param index*/private void siftDown(int index) {E element = elements[index];int half = size >> 1;// 第一个叶子节点的索引 == 非叶子节点的数量// index < 第一个叶子节点的索引// 必须保证index位置是非叶子节点while (index < half) { // index的节点有2种情况// 1.只有左子节点// 2.同时有左右子节点// 默认为左子节点跟它进行比较int childIndex = (index << 1) + 1;E child = elements[childIndex];// 右子节点int rightIndex = childIndex + 1;// 选出左右子节点最大的那个if (rightIndex < size && compare(elements[rightIndex], child) > 0) {child = elements[childIndex = rightIndex];}if (compare(element, child) >= 0) {break;}// 将子节点存放到index位置elements[index] = child;// 重新设置indexindex = childIndex;}elements[index] = element;}/*** 让index位置的元素上滤* @param index*/private void siftUp(int index) {
//		E e = elements[index];
//		while (index > 0) {
//			int pindex = (index - 1) >> 1;
//			E p = elements[pindex];
//			if (compare(e, p) <= 0) return;
//			
//			// 交换index、pindex位置的内容
//			E tmp = elements[index];
//			elements[index] = elements[pindex];
//			elements[pindex] = tmp;
//			
//			// 重新赋值index
//			index = pindex;
//		}E element = elements[index];while (index > 0) {int parentIndex = (index - 1) >> 1;E parent = elements[parentIndex];if (compare(element, parent) <= 0) {break;}// 将父元素存储在index位置elements[index] = parent;// 重新赋值indexindex = parentIndex;}elements[index] = element;}private void ensureCapacity(int capacity) {int oldCapacity = elements.length;if (oldCapacity >= capacity) {return;}// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);E[] newElements = (E[]) new Object[newCapacity];for (int i = 0; i < size; i++) {newElements[i] = elements[i];}elements = newElements;}private void emptyCheck() {if (size == 0) {throw new IndexOutOfBoundsException("Heap is empty");}}private void elementNotNullCheck(E element) {if (element == null) {throw new IllegalArgumentException("element must not be null");}}@Overridepublic Object root() {return 0;}@Overridepublic Object left(Object node) {int index = ((int)node << 1) + 1;return index >= size ? null : index;}@Overridepublic Object right(Object node) {int index = ((int)node << 1) + 2;return index >= size ? null : index;}@Overridepublic Object string(Object node) {return elements[(int)node];}
}

最小堆

同样使用最大堆的代码,只需要设置一个倒序比较器即可,将小的数认为比较大放在数组前面。

代码如下:

	static void test3() {Integer[] data = {88, 44, 53, 41, 16, 6, 70, 18, 85, 98, 81, 23, 36, 43, 37};BinaryHeap<Integer> heap = new BinaryHeap<>(data, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {// 将原本【最大堆】中较小的值放前面,就实现了【最小堆】return o2 - o1;}});BinaryTrees.println(heap);}public static void main(String[] args) {test3();}

比较器解析

无论是 o1 - o2 还是 o2 - o1

  1. 只要返回正整数,就表示 o1 应该在 o2 的右边。
  2. 而返回负整数则表示 o1 应该在 o2 的左边。

示例说明:

1、向数组中加入 20,Integer[] data = {10, 20}

o1 - o2 = -10 – 10, 20

o2 - o1 = 10 – 20, 10

2、再向数组中加入 30,Integer[] data ={10, 20, 30}

o1 - o2

  • 10 - 30 = -20 – 10, 30, 20
  • 20 - 30 = -10 – 【10, 20, 30】

o2 - o1

  • 30 - 10 = 20 – 30, 10
  • 20 - 10 = 10 – 【30, 20, 10】

总结

无论是升序还是降序,只要返回正整数,就表示第一个元素应该在第二个元素的右边。

Top K 问题

问题分析

题目:从 n 个整数中,找出最大的前 k 个数 (k 远远小于 n)

  1. 如果使用【排序算法】进行全排序,需要 O(nlogn) 的时间复杂度。

  2. 如果使用【二叉堆】来解决,可以使用 O(nlogk) 的时间复杂度来解决

    • 新建一个小顶堆
    • 扫描 n 个整数

具体细节:

  1. 先将遍历到的前 k 个数放入堆中;
  2. 从第 k + 1 个数开始,如果大于堆顶元素,就使用 replace 操作(删除堆顶元素,将第 k + 1 个数添加到堆中)

代码实现

	static void test4() {// 新建一个小顶堆BinaryHeap<Integer> heap = new BinaryHeap<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});// 找出最大的前k个数int k = 3;Integer[] data = {51, 30, 39, 92, 74, 25, 16, 93, 91, 19, 54, 47, 73, 62, 76, 63, 35, 18, 90, 6, 65, 49, 3, 26, 61, 21, 48};for (int i = 0; i < data.length; i++) {if (heap.size() < k) { // 前k个数添加到小顶堆heap.add(data[i]); // logk} else if (data[i] > heap.get()) { // 如果是第k + 1个数,并且大于堆顶元素heap.replace(data[i]); // logk}}// O(nlogk)BinaryTrees.println(heap);}public static void main(String[] args) {test4();}

内部方法分析

1、heap.get() 获取堆顶元素

	@Overridepublic E get() {emptyCheck();return elements[0];}

2、heap.replace(data[i]);

删除堆顶元素的同时插入一个新元素,即将大于堆顶的数组元素加进去。

3、这是个最小堆

堆顶元素一直是最小的。

问题 2

如果是找出最小的前 k 个数呢?

  1. 顶堆
  2. 如果小于堆顶元素,就使用 replace 操作

堆排序

概念

堆排序(Heap Sort)是一种基于堆数据结构的排序算法,它利用了堆的特性来进行排序。

堆排序的基本思想如下:

  1. 构建最大堆(或最小堆):将待排序的数组构建成一个最大堆(或最小堆)。
  2. 交换堆顶元素:将堆顶元素与当前未排序部分的最后一个元素交换位置。
  3. 调整堆:对交换后的堆进行调整,使其满足最大堆(或最小堆)的性质。
  4. 重复步骤 2 和 3,直到整个数组排序完成。

代码示例

以下是两个简单的堆排序示例代码:

第一种 – 降序
public class Main2 {public static void main(String[] args) {Integer[] arr = { 12, 11, 13, 5, 6, 7 };BinaryHeap<Integer> heap = new BinaryHeap<>(arr);heapSort(heap);}public static <E> void heapSort(BinaryHeap<E> heap) {int size = heap.size();for (int i = 0; i < size; i++) {// 删除后会再调整堆结构E max = heap.remove();System.out.print(max + " ");}}
}

输出结果为:

13 12 11 7 6 5 
第二种 – 升序
public class HeapSort {public static void heapSort(Integer[] arr) {int n = arr.length;// 构建最大堆for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 交换堆顶元素和未排序部分的最后一个元素,并调整堆for (int i = n - 1; i > 0; i--) {// 将堆顶元素与当前未排序部分的最后一个元素交换位置int temp = arr[0];arr[0] = arr[i];arr[i] = temp;// 调整堆heapify(arr, i, 0);}}// 调整堆,使其满足最大堆的性质public static void heapify(Integer[] arr, int n, int i) {int largest = i; // 初始化堆顶元素为最大值int left = 2 * i + 1; // 左子节点的索引int right = 2 * i + 2; // 右子节点的索引// 判断左子节点是否大于堆顶元素if (left < n && arr[left] > arr[largest]) {largest = left;}// 判断右子节点是否大于堆顶元素if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果堆顶元素不是最大值,则交换堆顶元素和最大值,并继续调整堆if (largest != i) {int temp = arr[i];arr[i] = arr[largest];arr[largest] = temp;heapify(arr, n, largest);}}public static void main(String[] args) {Integer[] arr = { 12, 11, 13, 5, 6, 7 };System.out.println("原始数组:");for (int num : arr) {System.out.print(num + " ");}System.out.println("\n------------------------");BinaryHeap<Integer> heap = new BinaryHeap<>(arr);BinaryTrees.println(heap);System.out.println("==========================");heapSort(arr);System.out.println("\n排序后的数组:");for (int num : arr) {System.out.print(num + " ");}System.out.println("\n------------------------");BinaryHeap<Integer> heap2 = new BinaryHeap<>(arr);BinaryTrees.println(heap2);}
}

输出结果为:

原始数组:
12 11 13 5 6 7 
------------------------┌──13─┐│     │
┌─11─┐ ┌─12
│    │ │
5    6 7
==========================排序后的数组:
5 6 7 11 12 13 
------------------------┌──13─┐│     │┌─12─┐ ┌─7│    │ │
11    6 5

注意:

以下这个方法是会对【原数组】的值改变的,heapSort 方法会直接修改原始数组。这意味着在排序之后,原始数组的顺序会被改变。

如果你希望保持原始数组的不变,并在排序后得到一个新的已排序副本,可以使用以下方法:


// 在进行堆排序之前,创建一个原始数组的副本。
Integer[] arr = { 12, 11, 13, 5, 6, 7 };
Integer[] arrCopy = Arrays.copyOf(arr, arr.length);

空间复杂度能否下降至 O(1)?

在当前的实现中,二叉堆的空间复杂度是 O(n),其中 n 是元素的数量。这是因为我们使用一个数组来存储堆的元素。

  • 要将空间复杂度降低到 O(1),我们需要修改数据结构的实现方式

  • 目前的实现方式(BinaryHeap<E>)是使用一个动态数组来存储元素,但这会占用 O(n) 的额外空间。

如果要将空间复杂度降低到 O(1),我们可以考虑使用原始的输入数组来表示堆,而不是创建一个额外的数组。这意味着我们需要在原始数组上进行堆操作,而不是将元素复制到新的数组中。(比如上面写的第二种代码示例)

但是,这样做会对原始数组进行修改,并且在堆操作期间可能会打乱原始数组的顺序。因此,在实际应用中,这种修改可能会有限制,并且需要权衡空间和时间的复杂度。

总结起来,要将空间复杂度降低到 O(1),需要在原始数组上进行操作,但这可能会对原始数组造成修改,并可能会有限制和权衡。具体的实现方式取决于具体的应用场景和需求。

示例代码分析

第一种示例方法复杂度解析

空间复杂度

如果输入的元素个数为 n,且 n 大于 10,那么空间复杂度为 O(n);否则,空间复杂度为 O(1)。

所以最坏空间复杂度为 O(n)

时间复杂度

  • 构建二叉堆的过程具有 O(n) 的时间复杂度,其中 n 是输入数组的长度。
  • 接下来,进行 n 次删除操作,每次删除操作的时间复杂度为 O(logn)。由于删除操作会调整堆的结构,保持最大堆的性质,因此每次删除操作的时间复杂度为O(logn)。
  • 因此,总体上,堆排序的时间复杂度为 O(nlogn)

第二种示例方法复杂度解析

空间复杂度

  • heapSort 方法中,除了输入数组之外,没有使用额外的空间。因此,空间复杂度为 O(1),即常数级别的空间复杂度。

时间复杂度:

  • 构建最大堆的过程具有 O(n) 的时间复杂度,其中 n 是输入数组的长度。

  • 接下来,进行 n-1 次堆调整和交换操作,每次操作的时间复杂度为 O(logn)。

  • 因此,总体上,堆排序的时间复杂度为 O(nlogn)
    *。

  • 目前的实现方式(BinaryHeap<E>)是使用一个动态数组来存储元素,但这会占用 O(n) 的额外空间。

如果要将空间复杂度降低到 O(1),我们可以考虑使用原始的输入数组来表示堆,而不是创建一个额外的数组。这意味着我们需要在原始数组上进行堆操作,而不是将元素复制到新的数组中。(比如上面写的第二种代码示例)

但是,这样做会对原始数组进行修改,并且在堆操作期间可能会打乱原始数组的顺序。因此,在实际应用中,这种修改可能会有限制,并且需要权衡空间和时间的复杂度。

总结起来,要将空间复杂度降低到 O(1),需要在原始数组上进行操作,但这可能会对原始数组造成修改,并可能会有限制和权衡。具体的实现方式取决于具体的应用场景和需求。

示例代码分析

第一种示例方法复杂度解析

空间复杂度

如果输入的元素个数为 n,且 n 大于 10,那么空间复杂度为 O(n);否则,空间复杂度为 O(1)。

所以最坏空间复杂度为 O(n)

时间复杂度

  • 构建二叉堆的过程具有 O(n) 的时间复杂度,其中 n 是输入数组的长度。
  • 接下来,进行 n 次删除操作,每次删除操作的时间复杂度为 O(logn)。由于删除操作会调整堆的结构,保持最大堆的性质,因此每次删除操作的时间复杂度为O(logn)。
  • 因此,总体上,堆排序的时间复杂度为 O(nlogn)

第二种示例方法复杂度解析

空间复杂度

  • heapSort 方法中,除了输入数组之外,没有使用额外的空间。因此,空间复杂度为 O(1),即常数级别的空间复杂度。

时间复杂度:

  • 构建最大堆的过程具有 O(n) 的时间复杂度,其中 n 是输入数组的长度。
  • 接下来,进行 n-1 次堆调整和交换操作,每次操作的时间复杂度为 O(logn)。
  • 因此,总体上,堆排序的时间复杂度为 O(nlogn)

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

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

相关文章

【程序员养生心得】—— 编程之路,健康同行

身为程序员&#xff0c;我们似乎总和亚健康、熬夜、颈椎病等标签紧密相连。但工作虽重要&#xff0c;健康价更高。在此&#xff0c;我想与大家分享一些在编程之路上的养生心得&#xff0c;希望我们都能在职业发展的同时&#xff0c;照顾好自己。 定时休息&#xff0c;活动身体&…

小程序云开发中引入vant

首先看一下云开发中的小程序的目录结构 安装 vant 上面是官方的方法 具体到我们的项目是这样子的 最后&#xff0c;构建一下就可以了

rv1126-rv1109-rk809

是这样的,新来板子走的是rk809部分 然后我的编译方式里面没有,走的是别的方式,打印到log如下,然后就卡死 DDR V1.09 8fef64cfb9 wesley.yao 22/10/25-20:03:00 DDR4, 328MHz BW=16 Col=10 Bk=4 BG=2 CS0 Row=15 CS=1 Die BW=16 Size=512MB change to: 328MHz change to: 528MHz…

微信小程序踩坑记录

一、引言 作者在开发微信小程序《目的地到了》的过程中遇到过许多问题&#xff0c;这里讲讲一些技术和经验问题。 基本目录机构&#xff1a; 二、问题 1、定位使用 获取定位一定要在app.json里面申明&#xff0c;不然是没办法获取定位信息的 "requiredPrivateInfos"…

Linux | Ubuntu设置 netstat(网络状态)

netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据&#xff0c;一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序&#xff0c;它能提供TCP连接&#xff0c;TCP和UDP监听&#xff0c;进程内存管理的相关报告。 1.netstat的安装 搜…

JVM执行引擎以及调优

1.JVM内部的优化逻辑 1.1JVM的执行引擎 javac编译器将Person.java源码文件编译成class文件[我们把这里的编译称为前期编译]&#xff0c;交给JVM运行&#xff0c;因为JVM只能认识class字节码文件。同时在不同的操作系统上安装对应版本的JDK&#xff0c;里面包含了各自屏蔽操作…

网络通信与TCP.IP协议

网络通信与TCP.IP协议 URI 用字符串标识某一互联网资源&#xff0c;而 URL 表示资源的地点&#xff08;互联网上所处的位置&#xff09;。可见 URL 是 URI 的子集 URL (Uniform Resource Locator)&#xff0c;统一资源定位符 &#xff0c;用于描述一个网络上的资源 DNS: &#…

element-plus 使用密码输入框的自定义图标

<el-inputv-model"ruleFormPassword.newPassword"placeholder"请输入新密码":type"showPassword ? text : password":style"{ width: 360px }"><template #suffix><span class"input_icon" click"swit…

linux环境下编译安装OpenCV For Java(CentOS 7)

最近在业余时间学习了一些有关图像处理的代码&#xff0c;但是只能本地处理&#xff0c;满足不了将来开放远程服务的需求。 因此&#xff0c;查找并参考了一些资料&#xff0c;成功在centos7环境安装上了opencv 460。 下面上具体安装步骤&#xff0c;希望能帮到有需要的同学。 …

FP5207 DC-DC 电源升压模块/12V升24V(5A) 升压板/升压电路/直流稳压/直流升压-应用蓝牙音箱、快充、应急电源、车载设备等

目录 概述 特征 应用 概述 FP5207是异步升压控制IC&#xff0c;透过EXT Pin控制外部NMOS&#xff0c;输入低启动电压2.8V与宽工作电压5V~24V&#xff0c;单节锂电池3V~4.2V应用&#xff0c;将Vout接到HVDD Pin&#xff1b;精准的反馈电压1.2V&#xff0c;内置软启动&#x…

Flutter App混淆加固、保护与优化原理

​ 引言 在移动应用程序开发中&#xff0c;保护应用程序的代码和数据安全至关重要。本文将探讨如何对Flutter应用程序进行混淆、优化和保护&#xff0c;以提高应用程序的安全性和隐私。 一、混淆原理 混淆是一种代码保护技术&#xff0c;通过修改源代码或编译后的代码&#…

c/c++概念辨析-指针常量常量指针、指针函数函数指针、指针数组数组指针

概念澄清&#xff1a; 统一规则&#xff1a; 不管是XX指针&#xff0c;还是指针XX&#xff0c;后者是本体&#xff0c;前者只是个定语&#xff0c;前者也可以替换为其他同类&#xff08;例如字符串&#xff09;&#xff0c;帮助理解。 XX指针&#xff1a; 可简单理解为&#…

Image Segmentation Using Deep Learning: A Survey

论文标题&#xff1a;Image Segmentation Using Deep Learning:A Survey作者&#xff1a;发表日期&#xff1a;阅读日期 &#xff1a;研究背景&#xff1a;scene understanding,medical image analysis, robotic perception, video surveillance, augmented reality, and image…

安卓+charles实现抓包(主要解决证书网站无法打开问题)

安装 官网下载 https://www.charlesproxy.com/latest-release/download.do 使用介绍 Charles介绍 上面链接看一至三即可 初步代理配置 如何获取代理服务器IP和手机端IP 代理服务器IP 点击help&#xff0c;选中ssl 代理&#xff0c;点击在移动设备或远程浏览器上安装Cha…

如何练好太极拳?

太极拳是一种需要细心和耐心的武术&#xff0c;要练好太极拳&#xff0c;需要从以下几个方面入手&#xff1a; 找到好的师傅&#xff1a;找到一位经验丰富、技艺高超的师傅是学习太极拳的关键。师傅应该具备正确的太极拳理论、技术和经验&#xff0c;能够正确地指导学生学习太极…

M1安装RabbitMQ

1.查看centos内核版本 uname -a uname -r2.安装之前的准备工作 安装RabbitMQ必装Erlang(RabbitMQ官网添加链接描述) 2.1.Erlang简介 Erlang是一种通用的面向并发的编程语言&#xff0c;它由瑞典电信设备制造商爱立信所辖的CS-Lab开发&#xff0c;目的是创造一种可以应对…

漏洞复现--安恒明御安全网关 aaa_local_web_preview 任意文件上传

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

爬虫学习 逆向爬虫(六)

多任务异步协程 协程:更高效的利用CPU import timedef func():print("黎明")time.sleep(3)print("还是黎明")func() 等待时机长 sleep时CPU不再工作 IO操作(费时不费力)->阻塞 线程运行阻塞后 移出主线程 移动到下一个 4个任务一个线程 …

一套后台管理系统的入门级的增删改查(vue3组合式api+elemment-plus)

一、页面示意&#xff1a; 图一 图二 二、组件结构 列表组件 &#xff1a;index.vue,对应图一添加组件&#xff1a;add.vue&#xff0c;对应图二&#xff0c;用抽屉效果编辑组件&#xff1a;edit.vue&#xff0c;和添加组件的效果一个。 三、代码 1、列表组件: index.vue …

我在Vscode学OpenCV 图像处理一(阈值处理、形态学操作【连通性,腐蚀和膨胀,开闭运算,礼帽和黑帽,内核】)

文章目录 一、阈值处理1.1 OpenCV 提供了函数 cv2.threshold()和函数 cv2.adaptiveThreshold()&#xff0c;用于实现阈值处理1.1.1. cv2.threshold()&#xff1a;(1)在函数cv2.threshold()中&#xff0c;参数threshold_type用于指定阈值处理的方式。它有以下几种可选的阈值类型…