数据结构(Java实现):链表与LinkedList

文章目录

  • 1. 单向链表
    • 1.1 链表的概念及结构
    • 1.2 链表的实现
      • 1.2.1 单向链表类和节点
      • 1.2.2 打印每个节点的值
      • 1.2.3 计算链表长度
      • 1.2.4 头插节点
      • 1.2.5 尾插节点
      • 1.2.6 在指定下标插入新节点
      • 1.2.7 判断是否存在某个节点
      • 1.2.8 移除某个节点
      • 1.2.9 移除所有指定节点
      • 1.2.10 清空链表
      • 1.2.11 测试代码
    • 1.3 链表相关习题及题解
  • 2. LinkedList(双向链表)
    • 2.1 LinkedList的模拟实现(MyLinkedList)
      • 2.1.1 双向链表类和节点
      • 2.1.2 前插节点
      • 2.1.3 尾插节点
      • 2.1.4 指定位置插入节点
      • 2.1.5 判断是否有目标节点
      • 2.1.6 删除第一次出现的关键字的节点
      • 2.1.7 删除所有目标节点
      • 2.1.8 链表长度
      • 2.1.9 打印链表
      • 2.1.10 清空链表
      • 2.1.11 测试代码
  • 3. LinkedList的使用
    • 3.1 什么是LinkedList
    • 3.2 LinkedList的使用
  • 4. ArrayList和LinkedList的区别

1. 单向链表

1.1 链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
在这里插入图片描述
在这里插入图片描述
注意:

  1. 从上图可以看出,链式结构在逻辑上是连续的,但物理上不一定连续。
  2. 现实中的节点一般都是从堆上申请出来的。
  3. 从堆上申请的空间,是按照一定的策略来分配的,再次申请的空间可能连续,也可能不连续。

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

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

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

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。在这里插入图片描述
  • 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

在这里插入图片描述

1.2 链表的实现

1.2.1 单向链表类和节点

链表的节点我们通过内部类的方式来实现,这个类包含一个值val和下一节点的地址。外部类需要再定义一个头结点。

public class MySingleLinkedList {static class ListNode{//节点public int val;public ListNode next;//内部类构造方法,给节点赋值public ListNode(int val){this.val=val;this.next=null;}}//链表的头节点public ListNode head = null;
}

下面我们来一步一步分析并实现一些自定义链表的功能,包含在MySingleLinkedList类里面。

1.2.2 打印每个节点的值

我们通过遍历链表来实现每个节点值的打印,注意:我们不能使用头节点去遍历我们的链表,这可能会使我们的链表结构发生改变,所以我们重新创建一个节点cur来遍历链表,下面遍历也是同样的方法。

public void display(){ListNode cur=head;while (cur!=null){//cur走到最后的next为null,停止循环System.out.print(cur.val+" ");cur=cur.next;//遍历}System.out.println();}

1.2.3 计算链表长度

跟打印相同的方式进行遍历,然后使用count计数,每遍历一次就加1。然后返回count。

public int size(){int count=0;//计数ListNode cur=head;while (cur!=null){count++;cur=cur.next;}return count;}

1.2.4 头插节点

头插节点仅需要将新增节点的next指向头节点,然后让新增节点化为头节点。

public void addFirst(int val){ListNode node = new ListNode(val);//新增节点node.next=head;//新增节点的next指向头节点head=node;//新增节点化为头节点}

1.2.5 尾插节点

尾差节点需要将整个链表遍历完,然后让尾节点的next指向新节点。此时要注意,如果头节点为空就要使新增节点划定位头节点。

public void addLast(int val){ListNode node = new ListNode(val);//创建新节点//头节点为空就要使新增节点划定位头节点if(head == null){head = node;return;}//遍历链表ListNode cur = head;while (cur.next!=null){cur=cur.next;}//尾结点指向新节点cur.next=node;}

1.2.6 在指定下标插入新节点

这里要进行一个判断,判断输入的下标是否合法。因此我们自定义了一个受查异常。

public class IndexLegal extends RuntimeException{public IndexLegal(){}public IndexLegal(String arg){super(arg);}
}

然后我们通过遍历定位到指定位置的前一个节点,然后使用新节点的next指向定位节点的下一个节点,定位节点指向新增节点。此处两个步骤不能相反。

public void addIndex(int val, int index){//判断位置是否合法if(index<0||index>this.size()-1){throw new IndexLegal("传入位置不合法!");}//新节点ListNode node=new ListNode(val);ListNode cur=head;//定位到指定节点的前一个节点for (int i = 0; i < index-1; i++) {cur=cur.next;}//新节点的next指向定位节点的下一个节点node.next=cur.next;//定位节点指向新增节点cur.next=node;}

1.2.7 判断是否存在某个节点

此处只需要遍历节点,如果某个节点的值等于传入值就返回true,否则返回false。

public boolean contains(int val){ListNode cur=head;//遍历链表while (cur!=null){if(cur.val==val){return true;//判断为真返回true}cur=cur.next;}return false;//遍历完没有返回就不存在指定节点}

1.2.8 移除某个节点

如果要移除的节点在头节点就将头结点设置为头结点的next。如果为其他节点,先进行遍历,定位到指定节点的前一个节点,然后让定位节点指向定位节点的next的next。

public void remove(int val){//判断指定节点是否为头节点if(head.val==val){//头结点设置为头结点的nexthead=head.next;return;}ListNode cur=head;//遍历链表while (cur.next!=null){//定位到指定节点的前一个节点if(cur.next.val==val){//定位节点指向定位节点的next的next。cur.next=cur.next.next;return;}}}

1.2.9 移除所有指定节点

此时我们可以有两种方法:

  1. 创建一个虚拟头节点,当遍历到的节点不是指定节点就连接上虚拟头结点的那条链表,然后虚拟头结点的next就是所需节点。这里就不进行写入演示了。
  2. 通过使用一个前驱节点和遍历节点,前驱节点指向头节点,遍历节点指向头结点的next。然后遍历链表,当遍历到的节点不是目标节点,就让前驱节点指向遍历节点。如果是目标节点,遍历节点就一直遍历下去,前驱节点next指向遍历节点,由此来达到删除目标节点的功能。最后要判断头结点是否为目标节点,如果是就将头结点转换为头结点的next。
public void removeAll(int val){ListNode cur=head.next;//遍历节点ListNode prev=head;//前驱节点//遍历链表while (cur!=null){//当遍历到的节点不是目标节点,就让前驱节点指向遍历节点if (cur.val != val) {prev = cur;}//如果是目标节点,遍历节点就一直遍历下去cur=cur.next;//前驱节点next指向遍历节点,由此来达到删除目标节点的功能prev.next=cur;}if(head.val==val){//判断头结点是否为目标节点//如果为真,就将头结点转换为头结点的nexthead=head.next;}}

1.2.10 清空链表

如果进使用head=null,其他节点并没有得到释放。所以我们需要通过遍历来一个一个释放节点,最后再释放头结点。

public void clear(){ListNode cur=head;while (cur!=null){ListNode curN=head.next;
//如果使用cur=null并不能释放当前节点,因为cur只是当前节点的一个副本。
//对cur的操作并不能影响到当前节点。cur.next=null;//一个一个释放节点cur=curN;}head=null;//最后释放头结点}

1.2.11 测试代码

	public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();//尾插sl.addLast(12);sl.addLast(23);sl.addLast(34);sl.addLast(45);sl.addLast(56);//打印sl.display();System.out.println("==============");//头插sl.addFirst(11);sl.display();System.out.println("==============");//指定位置插sl.addIndex(99,3);sl.display();//大小System.out.println(sl.size());//判断System.out.println(sl.contains(12));}public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();sl.addLast(11);sl.addLast(12);sl.addLast(11);sl.addLast(14);sl.addLast(11);sl.addLast(11);sl.addLast(15);sl.display();System.out.println("===========");//删除指定节点sl.remove(12);sl.display();System.out.println("===========");//删除所有节点sl.removeAll(11);sl.display();}public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();//当所有节点都为要删除的节点的情况sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.removeAll(11);sl.display();}public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.addLast(11);sl.display();//清空链表sl.clear();sl.display();}public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();}  public static void main(String[] args) {MySingleLinkedList sl=new MySingleLinkedList();}

在这里插入图片描述
在这里插入图片描述

1.3 链表相关习题及题解

链表相关习题及题解
写完这些习题可以更好帮助你迅速掌握和理解链表。(建议去做一下)

2. LinkedList(双向链表)

特点:每一个节点都包含了前驱和后继。

2.1 LinkedList的模拟实现(MyLinkedList)

2.1.1 双向链表类和节点

public class MyLinkedList {static class ListNode{//节点public int val;//节点值public ListNode next;//后继public ListNode prev;//前驱public ListNode(int val){//构造方法this.val=val;this.next=null;this.prev=null;}}public ListNode head;//头节点public ListNode last;//尾节点}

特点:每一个节点都包含了前驱和后继。下面这些方法在这个自定义双向链表实现的。

2.1.2 前插节点

前插节点之前先判断头节点是否为null,如果为空将插入节点设置为头结点和尾结点。不为空就使头节点的前驱指向新节点,新节点的后继指向头结点,然后将新节点设置为头结点。
在这里插入图片描述

public void addFirst(int data){ListNode node=new ListNode(data);if(head==null){//先判断头节点是否为null//为空将插入节点设置为头结点和尾结点head=last=node;}else {//不为空head.prev=node;//头节点的前驱指向新节点node.next=head;//新节点的后继指向头结点head=node;//将新节点设置为头结点}}

2.1.3 尾插节点

尾插节点之前先判断尾节点是否为null,如果为空将插入节点设置为头结点和尾结点。如果不为空,尾结点的后继指向新节点,新节点的前驱指向尾节点,将新节点设置为尾节点。
在这里插入图片描述

public void addLast(int data){ListNode node=new ListNode(data);//先判断尾节点是否为nullif(last==null){//如果为空将插入节点设置为头结点和尾结点head=last=node;}else {//如果不为空last.next=node;//尾结点的后继指向新节点node.prev=last;//新节点的前驱指向尾节点last=node;//将新节点设置为尾节点}}

2.1.4 指定位置插入节点

在指定位置插入节点需要先判断传入的位置是否合法。如果合法,判断位置是否为头或者尾,如果为头就调用头插法,如果为尾就调用尾插法。如果为中间,需要改变的指向的位置如下。
在这里插入图片描述

public void addIndex(int index,int data){//判断位置是否合法if(index<0||index>size()){return;}ListNode node=new ListNode(data);//头插if(index==0) {addFirst(data);}else if(index==size()){//尾插addLast(data);}//找到对应位置的节点ListNode cur = findIndexNode(index);//指针指向转换node.next=cur;cur.prev.next=node;node.prev=cur.prev;cur.prev=node;}//寻找目标节点private ListNode findIndexNode(int index){ListNode cur = head;for (int i = 0; i < index; i++) {cur=cur.next;}return cur;}

2.1.5 判断是否有目标节点

从头遍历链表,如果节点的目标值等于传入值,返回true。遍历完链表没有返回true,就不存在目标节点,返回false。

public boolean contains(int key){ListNode cur=head;while (cur!=null){if(cur.val==key){return true;}cur=cur.next;}return false;}

2.1.6 删除第一次出现的关键字的节点

首先从头遍历整条链表,如果找到关键字的节点,先判断此节点是否为头节点,如果为头节点让头结点设置为头节点的next。然后判断头结点是否为null,如果为null此链表就只存在一个节点,此时将头和尾设置为null就行,否则将头结点的前驱设置为null。如果不是头结点就需要让目标节点的前驱节点的后置节点指向目标节点的后置节点。如果目标节点的后置节点为null(目标节点为尾结点)就将尾结点设置为尾结点的前驱节点。否则让目标节点的后置节点的前驱设置为目标节点的前驱。删除后返回。
在这里插入图片描述

public void remove(int key){ListNode cur=head;while (cur!=null){//首先从头遍历整条链表if(cur.val==key){//如果找到关键字的节点if(cur==head){//判断此节点是否为头节点//如果为头节点让头结点设置为头节点的nexthead=head.next;if(head!=null){//判断头结点是否为null//将头结点的前驱设置为nullhead.prev=null;}else {//此链表就只存在一个节点,此时将头和尾设置为nulllast=null;}}else {//不为头节点//让目标节点的前驱节点的后置节点指向目标节点的后置节点cur.prev.next=cur.next;
//目标节点的后置节点为null(目标节点为尾结点)就将尾结点设置为尾结点的前驱节点if(cur.next==null){last=last.prev;}else {//目标节点的后置节点的前驱设置为目标节点的前驱cur.next.prev=cur.prev;}}return;}cur=cur.next;}}

2.1.7 删除所有目标节点

此时只要将上面的返回去掉能达到删除所有目标节点的效果。

public void remove(int key){ListNode cur=head;while (cur!=null){//首先从头遍历整条链表if(cur.val==key){//如果找到关键字的节点if(cur==head){//判断此节点是否为头节点//如果为头节点让头结点设置为头节点的nexthead=head.next;if(head!=null){//判断头结点是否为null//将头结点的前驱设置为nullhead.prev=null;}else {//此链表就只存在一个节点,此时将头和尾设置为nulllast=null;}}else {//不为头节点//让目标节点的前驱节点的后置节点指向目标节点的后置节点cur.prev.next=cur.next;
//目标节点的后置节点为null(目标节点为尾结点)就将尾结点设置为尾结点的前驱节点if(cur.next==null){last=last.prev;}else {//目标节点的后置节点的前驱设置为目标节点的前驱cur.next.prev=cur.prev;}}}cur=cur.next;}}

2.1.8 链表长度

定义一个count,然后遍历链表,每遍历一次count就++。最后返回count就是链表的长度。

public int size(){ListNode cur=head;int count=0;//链表长度while (cur!=null){//遍历链表count++;cur=cur.next;}return count;}

2.1.9 打印链表

遍历链表,然后打印每个节点的值。

public void display(){ListNode cur=head;while (cur!=null){//遍历链表System.out.print(cur.val+" ");//打印节点的值cur=cur.next;}System.out.println();}

2.1.10 清空链表

遍历节点,然后将节点的前驱和后置置为空。

public void clear(){ListNode cur=head;while (cur!=null){//遍历链表ListNode curN=cur.next;cur.prev=null;//前驱置空cur.next=null;//后继置空cur=curN;}head=last=null;}

2.1.11 测试代码

//清空链表测试public static void main(String[] args) {MyLinkedList ll=new MyLinkedList();ll.addFirst(1);ll.addFirst(2);ll.addFirst(3);ll.addFirst(4);ll.addFirst(5);ll.clear();ll.display();}//移除所有目标节点测试public static void main(String[] args) {MyLinkedList ll=new MyLinkedList();ll.addLast(1);ll.addLast(1);ll.addLast(1);ll.addLast(1);ll.addLast(1);ll.addLast(1);ll.display();ll.removeAllKey(1);System.out.println("=========");ll.display();}//移除目标值和判断目标节点测试public static void main(String[] args) {MyLinkedList ll=new MyLinkedList();ll.addLast(1);ll.addLast(2);ll.addLast(3);ll.addLast(4);ll.addLast(5);ll.remove(1);ll.display();ll.remove(3);ll.display();System.out.println(ll.contains(2));System.out.println(ll.contains(3));}//尾插和指定位置插测试public static void main(String[] args) {MyLinkedList ll=new MyLinkedList();ll.addLast(1);ll.addLast(2);ll.addLast(3);ll.addLast(4);ll.addLast(5);ll.display();System.out.println("================");ll.addIndex(2,99);ll.display();}//前插测试public static void main(String[] args) {MyLinkedList ll=new MyLinkedList();ll.addFirst(1);ll.addFirst(2);ll.addFirst(3);ll.addFirst(4);ll.addFirst(5);ll.display();}

3. LinkedList的使用

3.1 什么是LinkedList

LinkedList官方文档
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。LinkedList还被当做双向链表来使用。
在这里插入图片描述
在这里插入图片描述
【说明】

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

知识补充:RandomAccess接口简介
RandomAccess接口是一个标记接口,用以标记实现的List集合具备快速随机访问的能力。
当一个List拥有快速访问功能时,其遍历方法采用for循环最快速。而没有快速访问功能的List,遍历的时候采用Iterator迭代器最快速。

if(list instanceof RandomAccess) {//判断链表是否实现RandomAccess接口// for循环System.out.println("采用for循环遍历");for (int i = 0;i< list.size();i++) {System.out.println(list.get(i));}
} else {// 迭代器System.out.println("采用迭代器遍历");Iterator it = list.iterator();while(it.hasNext()){System.out.println(it.next());}
}

3.2 LinkedList的使用

  1. LinkedList的构造方法在这里插入图片描述
    可以看到第二个构造方法有一个参数,解析如下。
    在这里插入图片描述
    有了第二种构造方法,我们可以复制相同类型或相同类型的子类的链表或者顺序表。
    public static void main(String[] args) {ArrayList<Integer> list1=new ArrayList<>();list1.add(1);list1.add(2);list1.add(3);LinkedList<Integer> list2=new LinkedList<>(list1);//复制顺序表System.out.println(list2);LinkedList<Integer> list3=new LinkedList<>();list3.add(4);list3.add(5);list3.add(6);LinkedList<Integer> list4=new LinkedList<>(list3);//复制链表System.out.println(list4);}
  1. LinkedList的其他常用方法介绍在这里插入图片描述
    上面的一些常用的方法跟我们第二模块模拟实现LinkedList用法和实现方法相似,可以通过源码查看。
    public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1); // add(elem): 表示尾插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);// 在起始位置插入0list.add(0, 0); // add(index, elem): 在index位置插入元素elemSystem.out.println(list);list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()list.removeFirst(); // removeFirst(): 删除第一个元素list.removeLast(); // removeLast(): 删除最后元素list.remove(1); // remove(index): 删除index位置的元素System.out.println(list);// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回falseif (!list.contains(1)) {list.add(0, 1);}list.add(1);System.out.println(list);System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置int elem = list.get(0); // get(index): 获取指定位置元素list.set(0, 100); // set(index, elem): 将index位置的元素设置为elemSystem.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.clear(); // 将list中元素清空System.out.println(list.size());}
  1. LinkedList的遍历
public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1);list.add(2);list.add(3);System.out.println("===for循环===");for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i)+" ");}System.out.println();System.out.println("===for-each===");for (Integer x:list) {System.out.print(x+" ");}System.out.println();System.out.println("===Iterator迭代器===");Iterator<Integer> it1= list.iterator();while (it1.hasNext()){System.out.print(it1.next()+" ");}System.out.println();System.out.println("===ListIterator迭代器===");ListIterator<Integer> it2=list.listIterator();//ListIterator实现Iterator接口,专属于list的迭代器while (it2.hasNext()){System.out.print(it2.next()+" ");}System.out.println();System.out.println("===ListIterator迭代器反向遍历===");ListIterator<Integer> it3=list.listIterator(list.size());//ListIterator的反向遍历(区别于普通Iterator迭代器)while (it3.hasPrevious()){System.out.print(it3.previous()+" ");}System.out.println();}

运行结果
在这里插入图片描述

4. ArrayList和LinkedList的区别

在这里插入图片描述

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

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

相关文章

redis | 认识非关系型数据库Redis的哈希数据类型

Redis 非关 kv型 哈希通用命令python 操作hash应用场景 数据类型 数据类型丰富&#xff0c;字符串strings,散列hashes,列表lists&#xff0c;集合sets,有序集合sorted sets等等 哈希 定义 1、由field和关联的value组成的键值对 类似于python的键值对 2、field和value.是字符…

一文学会Shell中case语句和函数

大家好呀&#xff01;今天简单聊一聊Shell中的case语句与函数。在多选择情况下使用case语句将非常方便&#xff0c;同时&#xff0c;函数的学习和使用对于学好一门编程语言也是非常重要的。 一、case语句 case语句为多选择语句。可以用case语句匹配一个值与一个模式&#xff0c…

OpenCV绘图函数详解及其用法示例

MFC类库中的CDC类有划线,画矩形,画椭圆,画多边形,文字等绘图函数,OpenCV也有类似的绘图函数。二者的区别在于MFC画图是在一定的区域内绘制图形,而OpenCV则是在图像上绘制,主要用于图像标注。 OpenCV的常用绘图函数有arrowedLine,circle ,drawContours, drawMarker, dra…

AI数字时代客户体验白皮书5G云算力网络云网终端AIGC人工智能宽带政企物联网专线 IDC智慧城市专家学者教授培训讲师分享

客户体验的时代已然来临 在过去的几十年里&#xff0c;中国企业逐步从产品驱动转向市场驱动&#xff0c;从规模竞争走向创新竞争。然而&#xff0c;随着市场竞争的白热化和产品、服务的高度同质化&#xff0c;企业之间的差异化逐渐被削弱&#xff0c;传统的价格战、渠道战已经…

layui table表单 checkbox选中一个其它也要选中

当我们选中其中一个商品的时候同类型的商品状态也要跟着改变 所以要在表单加载完成后去监听checkbox ,done:function (res) {console.log(详情表格数据,res)tableDetailList res.data;// 监听表格复选框选择table.on(checkbox( INST_SELECTORS.instLayFilters.unpaidTableDe…

Python优化算法13——飞蛾扑火优化算法(MFO)

科研里面优化算法都用的多&#xff0c;尤其是各种动物园里面的智能仿生优化算法&#xff0c;但是目前都是MATLAB的代码多&#xff0c;python几乎没有什么包&#xff0c;这次把优化算法系列的代码都从底层手写开始。 需要看以前的优化算法文章可以参考&#xff1a;Python优化算…

用4种不同视角理解矩阵乘法

目录 1. 背景 2. 线性方程组视角&#xff08;向量点积视角&#xff09; 3. 列向量观点视角 4. 向量变换视角&#xff08;矩阵函数&#xff09; 5. 坐标变换视角 1. 背景 矩阵诞生于线性方程组的求解&#xff0c;最基本的运算方法来自于高斯消元法&#xff0c;所以矩阵整个…

Linux 离线安装docker和docker-compose

前言 公司有 docker 和 docker-compose 离线包安装部署的需求&#xff0c;本文应运而生撰写时间&#xff1a;2024-06-07&#xff08;初稿&#xff09; 1 应用版本 docker&#xff1a;20.10.7, build f0df350docker-compose&#xff1a;1.25.1 2 物料准备 服务器账号/密码d…

[数据集][目标检测]电力场景输电线防震锤检测数据集VOC+YOLO格式2721张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2721 标注数量(xml文件个数)&#xff1a;2721 标注数量(txt文件个数)&#xff1a;2721 标注…

《javaEE篇》--线程池

线程池是什么 线程的诞生是因为进程创建和销毁的成本太大&#xff0c;但是也是相对而言&#xff0c;如果频繁的创建和销毁线程那么这个成本就不能忽略了。 一般有两种方法来进一步提高效率&#xff0c;一种是协程(这里不多做讨论),另一种就是线程池 假如说有一个学校食堂窗口…

智能控制,高效节能。ZLG致远电子能源智慧管理解决方案

面对楼宇及建筑群能源管理与设备控制的复杂需求&#xff0c;ZLG致远电子推出了一套能源智慧管理解决方案。该方案集设备管理、任务调度和数据可视化于一体&#xff0c;不仅实现数据的实时监控与分析&#xff0c;还助力系统节能降耗。 ZLG致远电子能源智慧管理解决方案 在ZLG致…

ShareSDK 企业微信

本篇文档主要讲解如何使用企业微信并进行分享和授权。 创建应用 登录企业微信并通过企业认证。选择应用管理 > 应用 >创建应用。编辑应用信息。配置授权登录信息。 以下为创建过程示例&#xff0c;图中信息仅为示例&#xff0c;创建时请按照真实信息填写&#xff0c;否…

如何查看ubuntu版本

在当前的技术环境中&#xff0c;了解操作系统的具体版本对于用户来说至关重要。这不仅能确保软件兼容性&#xff0c;还有助于进行系统管理和故障排查。对于使用Ubuntu系统的用户来说&#xff0c;有几种不同的方法可以查看当前系统的版本。下面将详细介绍如何查看您的Ubuntu系统…

Spring Boot(快速上手)

Spring Boot 零、环境配置 1. 创建项目 2. 热部署 添加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional> </dependency&…

polarctf靶场[WEB]cookie欺骗、upload、签到

[web]cookie欺骗 考点&#xff1a;cookie值 工具&#xff1a;Burp Suite抓包 根据题目提示&#xff0c;cookie欺骗&#xff0c;所以要在cookie值寻找关键 进入网页之后&#xff0c;说只有admin用户才能得到flag&#xff0c;而我们此时只属于普通访客 我们查看cookie值&…

「Python程序设计」基本数据类型:字符串

​在python的程序设计过程中&#xff0c;字符串是需要经常处理的变量类型。字符串在程序中的存储方式&#xff0c;类似于一维数组&#xff0c;每个字符占据数组中的一个单元格。 字符串可以存储字符类型的变量&#xff0c;即使是数字类型&#xff0c;也可以通过字符串来进行存…

vue3+vite配置环境变量实现开发、测试、生产的区分

文章目录 一、为什么需要区分 (dev)、测试 (test) 和生产 (prod) 环境二、vue3的项目如何通过配置方式区分不同的环境1、创建不同环境的.env文件2、在不同的.env文件中配置相应的环境变量1&#xff09;.env.develoment2&#xff09;.env.test3&#xff09;.env.production 3、在…

Git之git stash高级用法(五十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

Tomcat 部署与优化

目录 tomcat 简介 tomcat 构件 1. Web 容器 2. Servlet 容器 3.Jsp容器 tomcat 核心组件 Connector Container Service 子容器 总结 tomcat 部署 tomcat请求过程 tomcat部署 tomcat 优化 tomcat配置文件参数优化 JVM优化 tomcat 简介 提到Tomcat 就想到 java&a…

RISC-V vector(1) --- vector的引入与register说明

Vector相较于SIMD的优势 这两种实现方案&#xff0c;都是为了实现数据级并行性&#xff08;存在大量的数据可供程序同时计算&#xff09;&#xff1b; SIMD&#xff08;Single Instruction Multiple Data&#xff09; SIMD是将数据宽度和操作类型&#xff0c;都放在了指令中&a…