当前位置: 首页 > news >正文

数据结构 - 3(链表12000字详解)

一:LinkedList的使用

1.1 ArrayList的缺陷

上篇文章我们已经基本熟悉了ArrayList的使用,并且进行了简单模拟实现。由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

1.2 链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
在这里插入图片描述
在这里插入图片描述
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  • 单向或者双向
    在这里插入图片描述
  • 带头或者不带头
    在这里插入图片描述
  • 循环或者非循环
    在这里插入图片描述

虽然有这么多的链表的结构,但是我们重点掌握两种:

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

在这里插入图片描述

  • 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

1.3 LinkedList的使用

LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
在这里插入图片描述
在集合框架中,LinkedList也实现了List接口,具体如下:
在这里插入图片描述
【说明】

  1. LinkedList实现了List接口
  2. LinkedList的底层使用了双向链表
  3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
  4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
  5. LinkedList比较适合任意位置插入的场景

1.4 LinkedList的构造方法

LinkedList的构造方法

方法解释
LinkedList()无参构造
public LinkedList(Collection<? extends E> c)使用其他集合容器中元素构造List

public LinkedList(Collection<? extends E> c) 是 Java 中 LinkedList 类的一个构造方法。它的作用是创建一个新的 LinkedList 对象,并将指定集合中的所有元素添加到该链表中。

下面是代码示例:

List<String> list1 = new ArrayList<>();
list1.add("Apple");
list1.add("Banana");
list1.add("Orange");LinkedList<String> linkedList = new LinkedList<>(list1);

在这个例子中,我们先创建了一个 ArrayList 类型的集合 list1,并向其中添加了三个字符串元素。然后我们使用 LinkedList 的构造方法将 list1 转换为一个新的 LinkedList 对象,并将 list1 中的所有元素添加到链表中。

1.5 LinkedList的常用方法

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list

下面是使用示例:

public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();// add(elem): 表示尾插list.add(1); list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());System.out.println(list);// add(index, elem): 在index位置插入元素elemlist.add(0, 0);  System.out.println(list);// remove(): 删除第一个元素,内部调用的是removeFirst()list.remove();     // removeFirst(): 删除第一个元素list.removeFirst();   // removeLast(): 删除最后元素list.removeLast();   // remove(index): 删除index位置的元素list.remove(1); System.out.println(list);// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回falseif(!list.contains(1)){list.add(0, 1);}
list.add(1);System.out.println(list);// indexOf(elem): 从前往后找到第一个elem的位置System.out.println(list.indexOf(1));  // lastIndexOf(elem): 从后往前找第一个1的位置System.out.println(list.lastIndexOf(1)); // get(index): 获取指定位置元素int elem = list.get(0);  // set(index, elem): 将index位置的元素设置为elemlist.set(0, 100);     System.out.println(list);// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回List<Integer> copy = list.subList(0, 3); System.out.println(list);System.out.println(copy);// 将list中元素清空list.clear();        System.out.println(list.size());
}

1.6ArrayList和LinkedList的区别

不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
插入空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

二:LinkedList模拟实现

2.1无头单向非循环链表

// 1、无头单向非循环链表实现
public class SingleLinkedList {//头插法public void addFirst(int data){}//尾插法public void addLast(int data){}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){return false;}//删除第一次出现关键字为key的节点public void remove(int key){}
//删除所有值为key的节点public void removeAllKey(int key){}//得到单链表的长度public int size(){return -1;}//清空链表public void clear() {}//打印链表public void display() {}
}

下面是模拟实现后的代码:


public class SingleLinkedList {private Node head; // 链表头节点// 节点类private class Node {int data;Node next;public Node(int data) {this.data = data;this.next = null;}}// 头插法,在链表头部插入新节点public void addFirst(int data) {Node newNode = new Node(data); // 创建一个新节点newNode.next = head; // 将新节点的next指向当前头节点head = newNode; // 将新节点设置为头节点}// 尾插法,在链表尾部插入新节点public void addLast(int data) {Node newNode = new Node(data); // 创建一个新节点if (head == null) { // 如果链表为空,将新节点设置为头节点并返回head = newNode;return;}Node curr = head; // 从头节点开始遍历链表,找到最后一个节点while (curr.next != null) {curr = curr.next;}curr.next = newNode; // 将新节点链接到最后一个节点的next上}// 任意位置插入,在指定位置插入新节点public void addIndex(int index, int data) {if (index < 0 || index > size()) { // 检查插入位置的合法性throw new IndexOutOfBoundsException("Index out of range"); // 抛出越界异常}if (index == 0) { // 如果插入位置是头部,调用addFirst方法addFirst(data);} else if (index == size()) { // 如果插入位置是尾部,调用addLast方法addLast(data);} else { // 在指定位置插入新节点Node newNode = new Node(data); // 创建新节点Node prev = getNode(index - 1); // 获取插入位置前一个节点newNode.next = prev.next; // 将新节点的next指向插入位置节点的nextprev.next = newNode; // 插入位置节点的next指向新节点}}// 查找是否包含关键字keypublic boolean contains(int key) {Node curr = head; // 从头节点开始遍历链表while (curr != null) {if (curr.data == key) { // 如果节点的data等于关键字key,返回truereturn true;}curr = curr.next; // 继续向下一个节点遍历}return false; // 遍历完链表都没有找到关键字key,返回false}// 删除第一次出现关键字为key的节点public void remove(int key) {if (head == null) { // 如果链表为空,直接返回return;}if (head.data == key) { // 如果头节点的data等于关键字key,将头节点指向下一个节点并返回head = head.next;return;}Node prev = head; // 设置prev指针指向头节点Node curr = head.next; // 设置curr指针指向头节点的下一个节点while (curr != null) { // 从头节点的下一个节点开始遍历链表if (curr.data == key) { // 如果节点的data等于关键字key,将prev节点的next指向当前节点的下一个节点并返回prev.next = curr.next;return;}prev = curr; // prev和curr指针向后移动curr = curr.next;}}// 删除所有值为key的节点public void removeAllKey(int key) {if (head == null) { // 如果链表为空,直接返回return;}// 处理头节点为key的情况while (head != null && head.data == key) { // 如果头节点的data等于关键字key,将头节点指向下一个节点head = head.next;}Node prev = head; // 设置prev指针指向头节点Node curr = head != null ? head.next : null; // 设置curr指针指向头节点的下一个节点while (curr != null) { // 从头节点的下一个节点开始遍历链表if (curr.data == key) { // 如果节点的data等于关键字key,将prev节点的next指向当前节点的下一个节点prev.next = curr.next;} else { // 否则prev指针和curr指针向后移动prev = curr;}curr = curr.next;}}// 得到单链表的长度public int size() {int count = 0; // 计数变量Node curr = head; // 从头节点开始遍历链表while (curr != null) {count++; // 每遍历一个节点,计数器加一curr = curr.next; // 移动到下一个节点}return count; // 返回计数器的值}// 清空链表public void clear() {head = null; // 将头节点置为null}// 获取指定位置上的节点private Node getNode(int index) {if (index < 0 || index >= size()) { // 检查索引的合法性throw new IndexOutOfBoundsException("Index out of range"); // 抛出越界异常}Node curr = head; // 从头节点开始遍历链表for (int i = 0; i < index; i++) { // 遍历到指定位置的节点curr = curr.next;}return curr; // 返回指定位置的节点}// 打印链表public void display() {Node curr = head; // 从头节点开始遍历链表while (curr != null) {System.out.print(curr.data + " "); // 打印当前节点的数据curr = curr.next; // 移动到下一个节点}System.out.println(); // 换行}public static void main(String[] args) {SingleLinkedList list = new SingleLinkedList();list.addFirst(1); // 在链表头部插入节点 1list.addLast(3);  // 在链表尾部插入节点 3list.addIndex(1, 2); // 在下标为 1 的位置插入节点 2list.display(); // 打印链表:1 2 3System.out.println("Contains 2: " + list.contains(2)); // 判断链表是否包含关键字 2,结果为 truelist.remove(2); // 删除首次出现的关键字为 2 的节点list.display(); // 打印链表:1 3list.addLast(1);list.addLast(2);list.addLast(3);list.display(); // 打印链表:1 3 1 2 3list.removeAllKey(1); // 删除所有值为 1 的节点list.display(); // 打印链表:3 2 3System.out.println("Size: " + list.size()); // 获取链表长度,结果为 3list.clear(); // 清空链表list.display(); // 打印链表:空}
}

2.2 无头双向非循环链表

// 2、无头双向链表实现
public class MyLinkedList {//头插法public void addFirst(int data){ }//尾插法public void addLast(int data){}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){}//删除第一次出现关键字为key的节点public void remove(int key){}//删除所有值为key的节点public void removeAllKey(int key){}//得到单链表的长度public int size(){}public void display(){}//清空public void clear(){}
}

下面是模拟实现后的代码:

// 2、无头双向链表实现
public class MyLinkedList {// 节点类private class Node {private int data; // 节点存储的数据private Node prev; // 前一个节点的引用private Node next; // 下一个节点的引用public Node(int data) {this.data = data; // 初始化节点的数据为传入的datathis.prev = null; // 初始化前一个节点的引用为nullthis.next = null; // 初始化下一个节点的引用为null}}private Node head; // 头节点private Node tail; // 尾节点private int size; // 链表长度// 头插法public void addFirst(int data) {Node newNode = new Node(data); // 创建一个新节点,存储传入的数据if (head == null) { // 如果头节点为null,说明链表为空head = newNode; // 将头节点指向新节点tail = newNode; // 将尾节点指向新节点} else { // 链表不为空newNode.next = head; // 将新节点的下一个节点指向当前头节点head.prev = newNode; // 将当前头节点的前一个节点指向新节点head = newNode; // 将头节点指向新节点}size++; // 链表长度加1}// 尾插法public void addLast(int data) {Node newNode = new Node(data); // 创建一个新节点,存储传入的数据if (tail == null) { // 如果尾节点为null,说明链表为空head = newNode; // 将头节点指向新节点tail = newNode; // 将尾节点指向新节点} else { // 链表不为空tail.next = newNode; // 将当前尾节点的下一个节点指向新节点newNode.prev = tail; // 将新节点的前一个节点指向当前尾节点tail = newNode; // 将尾节点指向新节点}size++; // 链表长度加1}// 任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data) {if (index < 0 || index > size) { // 如果索引小于0或者大于链表长度,抛出索引超出范围的异常throw new IndexOutOfBoundsException("Index out of bound");}if (index == 0) { // 如果要插入的位置是头节点前面,即插入为头节点addFirst(data); // 使用头插法} else if (index == size) { // 如果要插入的位置是尾节点后面,即插入为尾节点addLast(data); // 使用尾插法} else { // 在中间位置插入节点Node newNode = new Node(data); // 创建一个新节点,存储传入的数据Node currNode = goToIndex(index); // 找到要插入位置的前一个节点newNode.prev = currNode.prev; // 将新节点的前一个节点指向要插入位置的前一个节点newNode.next = currNode; // 将新节点的下一个节点指向要插入位置的节点currNode.prev.next = newNode; // 将要插入位置的前一个节点的下一个节点指向新节点currNode.prev = newNode; // 将要插入位置的节点的前一个节点指向新节点size++; // 链表长度加1}}// 查找是否包含关键字key是否在单链表当中public boolean contains(int key) {Node currNode = head; // 从头节点开始查找while (currNode != null) { // 当前节点不为nullif (currNode.data == key) { // 如果当前节点的数据等于关键字return true; // 返回true}currNode = currNode.next; // 继续下一个节点}return false; // 没找到关键字,返回false}// 删除第一次出现关键字为key的节点public void remove(int key) {Node currNode = head; // 从头节点开始查找while (currNode != null) { // 当前节点不为nullif (currNode.data == key) { // 如果当前节点的数据等于关键字if (currNode == head) { // 如果当前节点是头节点head = currNode.next; // 将头节点指向当前节点的下一个节点if (head != null) {head.prev = null; // 如果头节点不为null,将新头节点的前一个节点设置为null}if (currNode == tail) { // 如果当前节点是尾节点tail = null; // 将尾节点置为null}} else if (currNode == tail) { // 如果当前节点是尾节点tail = currNode.prev; // 将尾节点指向当前节点的前一个节点tail.next = null; // 将新尾节点的下一个节点设置为null} else { // 在中间位置删除节点currNode.prev.next = currNode.next; // 将要删除节点的前一个节点的下一个节点指向要删除节点的下一个节点currNode.next.prev = currNode.prev; // 将要删除节点的下一个节点的前一个节点指向要删除节点的前一个节点}size--; // 链表长度减1return; // 删除完成,结束方法}currNode = currNode.next; // 继续下一个节点}}// 删除所有值为key的节点public void removeAllKey(int key) {Node currNode = head; // 从头节点开始查找while (currNode != null) { // 当前节点不为nullif (currNode.data == key) { // 如果当前节点的数据等于关键字if (currNode == head) { // 如果当前节点是头节点head = currNode.next; // 将头节点指向当前节点的下一个节点if (head != null) {head.prev = null; // 如果头节点不为null,将新头节点的前一个节点设置为null}if (currNode == tail) { // 如果当前节点是尾节点tail = null; // 将尾节点置为null}currNode = head; // 将当前节点指向新的头节点} else if (currNode == tail) { // 如果当前节点是尾节点tail = currNode.prev; // 将尾节点指向当前节点的前一个节点tail.next = null; // 将新尾节点的下一个节点设置为nullcurrNode = null; // 将当前节点置为null,用于结束循环} else { // 在中间位置删除节点currNode.prev.next = currNode.next; // 将要删除节点的前一个节点的下一个节点指向要删除节点的下一个节点currNode.next.prev = currNode.prev; // 将要删除节点的下一个节点的前一个节点指向要删除节点的前一个节点Node tempNode = currNode.next; // 保存需要删除的节点的下一个节点的引用currNode.next = null; // 将要删除节点的下一个节点置为nullcurrNode.prev = null; // 将要删除节点的前一个节点置为nullcurrNode = tempNode; // 将当前节点指向保存的下一个节点}size--; // 链表长度减1} else {currNode = currNode.next; // 继续下一个节点}}}// 得到双向链表的长度public int size() {return size; // 返回链表的长度}// 辅助函数,用于定位到指定索引的节点private Node goToIndex(int index) {Node currNode = head; // 从头节点开始int count = 0; // 计数器,记录已经遍历的节点个数while (count < index) { // 当计数器小于索引时currNode = currNode.next; // 继续下一个节点count++; // 计数器加1}return currNode; // 返回找到的节点}// 清空双向链表public void clear() {head = null; // 将头节点置为nulltail = null; // 将尾节点置为nullsize = 0; // 链表长度置为0}// 打印双向链表public void display() {Node currNode = head; // 从头节点开始while (currNode != null) { // 当前节点不为nullSystem.out.print(currNode.data + " "); // 打印当前节点的数据currNode = currNode.next; // 继续下一个节点}System.out.println(); // 换行}public static void main(String[] args) {MyLinkedList list = new MyLinkedList(); // 创建一个新的双向链表对象list.addFirst(1); // 在链表头插入数据1list.addLast(2); // 在链表尾插入数据2list.addIndex(1, 3); // 在索引1处插入数据3list.display(); // 输出:1 3 2System.out.println(list.contains(2)); // 输出:truelist.remove(3); // 删除数据3list.display(); // 输出:1 2list.removeAllKey(2); // 删除所有值为2的节点list.display(); // 输出:1list.clear(); // 清空链表System.out.println(list.size()); // 输出:0}
}

2.3LinkedList的遍历

当我们使用Java中的LinkedList类时,有几种方式可以进行遍历。我们可以使用foreach循环,也可以使用迭代器对链表进行正向和反向遍历。下面是代码示例:

首先,让我们创建一个LinkedList对象并向其添加一些元素

import java.util.LinkedList;public class LinkedListTraversal {public static void main(String[] args) {LinkedList<String> linkedList = new LinkedList<>();linkedList.add("Apple");linkedList.add("Banana");linkedList.add("Cherry");linkedList.add("Durian");}
}
  1. 使用foreach循环遍历

使用foreach循环可以直接遍历LinkedList中的元素。在每次迭代中,我们可以通过一个变量来访问当前元素。下面是使用foreach循环遍历LinkedList的示例代码:

for (String fruit : linkedList) {System.out.println(fruit);
}

输出结果为:

Apple
Banana
Cherry
Durian
  1. 使用迭代器进行正向遍历

LinkedList实现了Iterable接口,因此我们可以使用迭代器在链表上进行正向遍历。通过调用iterator()方法,我们可以获取一个Iterator对象,然后使用hasNext()next()方法来迭代遍历链表中的元素。下面是使用迭代器进行正向遍历的示例代码:

Iterator<String> iterator = linkedList.iterator();
while (iterator.hasNext()) {String fruit = iterator.next();System.out.println(fruit);
}

输出结果为:

Apple
Banana
Cherry
Durian
  1. 使用迭代器进行反向遍历

LinkedList还提供了一个descendingIterator()方法,返回一个逆向迭代器,使我们可以反向遍历链表中的元素。通过调用hasNext()next()方法,我们可以从尾部开始向前迭代遍历LinkedList。下面是使用迭代器进行反向遍历的示例代码:

Iterator<String> descendingIterator = linkedList.descendingIterator();
while (descendingIterator.hasNext()) {String fruit = descendingIterator.next();System.out.println(fruit);
}

输出结果为:

Durian
Cherry
Banana
Apple

以上就是LinkedList遍历的三种方式!

相关文章:

数据结构 - 3(链表12000字详解)

一&#xff1a;LinkedList的使用 1.1 ArrayList的缺陷 上篇文章我们已经基本熟悉了ArrayList的使用&#xff0c;并且进行了简单模拟实现。由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&am…...

Jmeter性能测试插件jpgc的安装

一、获取插件包 1.访问官网获取 官网地址&#xff1a; 2.百度网盘下载 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;blmn 二、安装路径 将下载到的plugins-manager.jar插件存放到%JMETER_HOME%/lib/ext目录下 ​ 三、安装插件 1.重启Jmeter 如果已启动了…...

关于safari浏览器浏览html video标签无法正常播放的问题

问题&#xff1a; 前端demo使用一个video标签包含一个非静态资源的mp4文件。在chrome浏览器下可以正常展示&#xff0c;但是safari却不可以。 原因&#xff1a; 1. mp4文件必须用ffmpeg合成的&#xff0c;其他压缩的mp4文件是不可能展示的。请确定mp4文件并用正常的ffmpeg进…...

【C++代码】最大二叉树,合并二叉树,二叉搜索树中的搜索,验证二叉搜索树--代码随想录

题目&#xff1a;最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 …...

母婴用品会员商城小程序的作用是什么

随着政策放松&#xff0c;母婴行业相比以前迎来了更高的发展空间&#xff0c;由于可以与多个行业连接&#xff0c;因此市场规模也是连年上升&#xff0c;母婴用品是行业重要的分支&#xff0c;近些年从业商家连年增加&#xff0c;但在实际经营中&#xff0c;商家所遇经营痛点也…...

c++初阶--内存管理

目录 c/c 内存分布c内存管理方式new/delete操作内置类型new和delete操作自定义类型 operator new与operator delete函数new和delete的实现原理内置类型自定义类型 malloc/free和new/delete的区别内存泄露什么是内存泄漏&#xff0c;内存泄露的危害如何避免内存泄漏 在c语言中我…...

Burstormer论文阅读笔记

这是CVPR2023的一篇连拍图像修复和增强的论文&#xff0c;一作是阿联酋的默罕默德 本 扎耶得人工智能大学&#xff0c;二作是旷视科技。这些作者和CVPR2022的一篇BIPNet&#xff0c;同样是做连拍图像修复和增强的&#xff0c;是同一批。也就是说同一个方向&#xff0c;22年中了…...

Apifox 学习笔记 - 前置操作之:动态更新请求体中的时间戳

Apifox 学习笔记 - 前置操作之&#xff1a;动态更新请求体中的时间戳 1. 在前置操作中添加一个&#xff1a;自定义脚本或公共脚本2. 定义我们所需的环境变量。3. 在请求参数中使用【时间戳】4. 检验参考资料 1. 在前置操作中添加一个&#xff1a;自定义脚本或公共脚本 2. 定义我…...

2023年9月 青少年软件编程等级考试Scratch二级真题

202309 青少年软件编程等级考试Scratch二级真题&#xff08;电子学会等级考试&#xff09; 试卷总分数&#xff1a;100分 试卷及格分&#xff1a;60 分 考试时长&#xff1a;60 分钟 第 1 题 点击绿旗&#xff0c;运行程序后&#xff0c;舞台上的图形是?( ) A&#xff1a;画…...

12.验证码以及付费代理

文章目录 一、验证码的处理1、验证码概述1、2 什么是图片验证码&#xff1f;1、2 验证码的作用1、3 图片验证码使用场景1、4 图片验证码的处理方案 2、图片在网页页面中的形式2、1 如何进行图片形式的转化 3、打码平台 二、代理的使用2、1 付费代理2、1、1 找付费代理服务站点2…...

使用Plotly可视化

显示项目受欢迎程度 改进图表 设置颜色&#xff0c;字体...

【C语言】结构体、位段、枚举、联合(共用体)

结构体 结构&#xff1a;一些值的集合&#xff0c;这些值称为成员变量。结构体的每个成员可以是不同类型的变量&#xff1b; 结构体声明&#xff1a;struct是结构体关键字&#xff0c;结构体声明不能省略struct&#xff1b; 匿名结构体&#xff1a;只能在声明结构体的时候声…...

“Python+”集成技术高光谱遥感数据处理与机器学习深度应用

涵盖高光谱遥感数据处理的基础、python开发基础、机器学习和应用实践。重点解释高光谱数据处理所涉及的基本概念和理论&#xff0c;旨在帮助学员深入理解科学原理。结合Python编程工具&#xff0c;专注于解决高光谱数据读取、数据预处理、高光谱数据机器学习等技术难题&#xf…...

Excel 转为 PDF,PNG,HTML等文件

1.安装 Spire.XLS for Java,下载jar包 下载地址 2.引入方式一&#xff08;我这里这种方式一直无法引入&#xff0c;都是失败&#xff0c;所以用的方式二&#xff09; <repositories><repository><id>com.e-iceblue</id><name>e-iceblue</na…...

docker中使用GPU+rocksdb

配置环境 delldell-Precision-3630-Tower  ~  lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focaldelldell-Precision-3630-Tower  ~  nvcc --version nvcc: NVIDIA (R) Cuda comp…...

好用的跨平台同步笔记工具,手机和电脑可同步的笔记工具

在这个快节奏的工作环境中&#xff0c;每个人都在寻找一种方便又高效的方式来记录工作笔记。记录工作笔记可以帮助大家统计工作进展&#xff0c;了解工作进程&#xff0c;而如果工作中常在一个地方办公&#xff0c;直接选择电脑或者手机中笔记工具来记录即可&#xff0c;但是对…...

【Python 千题 —— 基础篇】浮点数转换为整数

题目描述 题目描述 整数转换为浮点数。 输入描述 输入一个整数。 输出描述 程序将整数转换为浮点数并输出。 示例 示例 ① 2输出&#xff1a; 2.0代码讲解 下面是本题的代码&#xff1a; # 描述: 整数转换为浮点数。 # 输入: 输入一个整数。 # 输出: 程序将整数转换…...

金融科技论文D部分

总结 以每周为例&#xff0c; 动量因子定义每种货币为前一周的回报率 价值因子定义为当前市值与其区块链中过去 7 天平均链上交易价值 利差因子定义为前 7 天硬币发行总量的负数除以在7天期限开始时未偿还的硬币量。 因素定义 为了避免过拟合&#xff0c;我们试图定义每一…...

Apache Tomcat下载安装配置使用超详细

下载安装 tomcat官网 在此我们以Tomcat 9.0.81为例&#xff0c;点击下载压缩包&#xff0c;解压到自己的文件夹。 tar.gz是linux操作系统下的安装版本。zip是windows系统下的压缩版本。Windows Service Installer是windows操作系统下的exe安装版本。 检查是否配置JDK 1.…...

基于Seata的分布式事务方案

在Seata中&#xff0c;有4种分布式事务实现方案 XA、AT、TCC、Saga 其中XA利用了数据库的分布式事务特性&#xff0c;AT相当于框架去控制事务回滚。TCC手写三个方法&#xff0c;saga手写两个方法。 AT的性能和编写比较折中&#xff0c;是最常用的一种。TCC一些视频教程中介绍…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...