对LinkedList和链表的理解

 一.ArrayList的缺陷
 二.链表
 三.链表部分相关oj面试题
     四.LinkedList的模拟实现
     五.LinkedList的使用
     六.ArrayList和LinkedList的区别
一.ArrayList的缺陷:
1. ArrayList底层使用 数组 来存储元素,如果不熟悉可以来再看看: ArrayList与顺序表-CSDN博客
由于其底层是一段连续空间,当 ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多的场景 。因此: java集合中又引入了 LinkedList,即链表结构
二.链表
1.链表的概念及结构:链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的引用链接次序实现的就像一个火车。
注意:1链表在逻辑上是连续的,在物理结构上不一定连续。
2.节点一般都是从堆上申请出来的
          3.从堆上申请出来空间,是有它的分配规律和策略的,两次申请出来的可能连续也可能不续
2.链表的分类
单向或者双向循环和非循环带头和不带头就可以组合出8种类型的链表
虽然有这么多的链表的结构,但是我们重点掌握两种:
(1) 无头单向非循环链表:结构简单, 一般不会单独用来存数据。实际中更多是 作为其他数据结构的子结构,哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
(2)无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表
 

3.无头单向非循环链表实现
自己定义的类和包:


这里可以把方法先写在一个接口中,再通过MyLinkList实现接口,这样写可能更好,代码好复用。
我们
MyLinkList类中:我们要先抽象出每一个节点,每一个节点就是一个对象,我们可以写一个产生节点对象的模板:(这里可以定义一个静态 内部类,来抽象出节点模板):
public class MyLinkList {public int data;public MyLinkList.Node next;//静态内部类public static class Node {public int data;//0public Node next;//引用类型默认值为NULLpublic Node(int data) {this.data = data;}}
}

(1)头插方法:
public void addFirst(int data) {//第一次插入节点(链表为空)if (this.head == null) {Node node = new Node(data);//链表头为空时(head == null),整了链表的头引用为 nodethis.head = node;return;}//链表不为空,单链表插入要先绑后面Node node = new Node(data);node.next = this.head;head = node;//把node的引用给head,然head变成新的头}

(2)尾插法:

public void addList(int data) {//第一次插入时if (this.head == null) {Node node = new Node(data);head = node;return;}Node node = new Node(data);Node cur = this.head;//cur从头开始/*这里注意cur不可以先走到空,如果cur走到null,那么cur的next就是cull*/while (cur.next != null) {cur = cur.next;}//出来时cur==null,就尾插cur.next = node;}

(3)打印单链表:这里我们可以写一个,重载方法display2,可以让链表从返回的某个节点开始打印;

    //打印单链表public void display2(Node nodeH) {Node cur = this.head;//cur从头开始cur = nodeH;while (cur != null) {System.out.print(cur.data + " ");cur = cur.next;}System.out.println();}public void display() {Node cur = this.head;//cur从头开始while (cur != null) {System.out.print(cur.data + " ");cur = cur.next;}System.out.println();}

(4)查找链表中是否包含某一数据节点:

 //查找是否包含关键字Key,是否在链表中public boolean contains(int key) {Node cur = this.head;while (cur != null) {if (cur.data == key) {return true;}cur = cur.next;}return false;}

 
(5)清空链表:
public void clear() {Node cur = head;while (cur != null) {//注意定义一个,变量记住置为空的,后驱节点Node curN = cur.next;cur.next =null;//引用类型必须制空cur = curN;}//最后把头节点手动置为nullhead = null;}

(6).返回链表的长度:

public int size() {Node cur = this.head;int count = 0;//count不能为1,如果是空链表,count=1返回就,寄了while (cur != null) {cur = cur.next;count++;}return count;}

(7)任意位置插入:这里我画了个图来理解:

//任意位置插入(第一个数据节点为0号下标)public void addIndex(int index, int data) {//相当于头插if (index == 0) {addFirst(data);return;}//相当于尾插if (index == this.size()) {addList(data);return;}//正常插入方法:/***    1. 先找到index前一个节点的地址->定义一个cur走index-1步*    2.画图插入*///先找到index前一个节点的地址Node cur = searchIndex(index);//插入Node node = new Node(data);/*** 这里注意,先绑后面(node = cur.next;),因为单链表前一个节点负责,单独的维护后一个节点,前一个节点的引用被覆盖(cur节点)* 那么原本和cur节点连接的节点就找不到了*/node.next = cur.next;cur.next = node;}//找到index前一个节点的地址的方法private Node searchIndex(int index) {//index下标位置检验if (index < 0 || index > this.size()) {throw new RuntimeException("下标位置不合法");}Node cur = this.head;while (index-1 != 0/*走index-1步*/) {cur = cur.next;index--;}return cur;//返回走index-1步后的,cur类型地址}

(8)删除指定位置节点:
 //找key节点的前驱private Node searchPrev(int key) {Node prev = this.head;while(prev.next != null) {if (prev.next.data == key) {return prev;}else {prev = prev.next;//继续往后走}}return null;}//删除第一次出现关键字为key的节点public void remove(int key) {/** 1. 找到,要删除节点del的前驱*  2. 找到要删除的节点del*  3. 删除节点*///空节点直接返回if (this.head == null) {return;}//头节点直接删除if (this.head.data == key) {head = head.next;return;//这里注意别忘记了}//1. 找到,要删除节点del的前驱Node prev = searchPrev(key);if (prev == null) {throw new RuntimeException("没有你要删除的节点,请考量要删除的节点");}//2. 找到要删除的节点delNode del = prev.next;//3. 删除节点prev.next = del.next;}

(9)只遍历一遍链表,删除所有指定的节点:这里我画了一个图可以帮助理解:定义一个一直往后走快指针,和一个,不需要时往后走判断是否要删除慢指针

 //遍历单链表一遍,删除所有值为key的节点public void removeAllKey(int key) {/** 1.定义一个快指针 cur : cur指针一直往后走;*  2.定义一个慢指针 prev: prev指针,只有cur遇到要删除的数据时,prev指针才往后走,不然保持不动*  3.注意最后不要漏了,head头节点*/// 1.定义一个 cur指针 : cur指针一直往后走//  2.定义一个 prev指针: prev指针,只有cur遇到要删除的数据时,prev指针才往后走,不然保持不动Node cur = this.head.next;//Node prev = this.head;while (cur != null) {if (cur.data == key) {//cur.data == key,时只有cur指针都在走,因为要遍历删除数据prev.next = cur.next;cur = cur.next;}else {//cur.data != key,两个指针都在动,prev指针,指向cur指针prev = cur;cur = cur.next;}}// 3.注意最后不要漏了,head头节点if (this.head.data == key) {this.head = this.head.next;}}

 三.链表部分相关oj面试题:(分享一些我认为比较重要的)
1.  反转一个单链表:我录了视频方便理解: 反转一个链表-CSDN直播

反转一个链表

class Solution {public ListNode reverseList(ListNode head) {ListNode cur = head;if(head == null) {return null;}head = null;while(cur != null) {ListNode curN = cur.next;cur.next = head;head = cur;cur = curN;}return head;}
}


2.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点:

理解视频:找到链表中间节点-CSDN直播

找到链表中间节点

class Solution {public ListNode middleNode(ListNode head) {if(head == null) {return null;}ListNode fast = head;//快指针一次走2步ListNode slow = head;//慢指针一次走一步
//条件不可以交换:(fast != null && slow.next != null),fast可能开始就为nullwhile (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}return slow;}
}

3.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的:

理解视频:合并两个有序链表-CSDN直播

合并两个有序链表


 

class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode headH = new ListNode(-1);ListNode tmp = headH;//tmp用来遍历两个链表while(list1 != null && list2 != null) {//哪个节点数据小,就接在tmp后面if(list1.val < list2.val) {tmp.next = list1;list1 = list1.next;tmp = tmp.next;}else {tmp.next = list2;list2 = list2.next;tmp = tmp.next;}}//当其中一个链表遍历完,就直接接上另一个链表的后半部分if(list1 != null) {tmp.next = list1;}if(list2 != null) {tmp.next = list2;}return headH.next;}
}

4.链表的回文结构:

这里有两个点要注意:1.从后往前用slow走,因为偶数节点,fast指针会走到null,无法往前走

 2.回文时偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回ture

理解视频:链表的回文结构-CSDN直播

链表的回文结构

public class PalindromeList {public boolean chkPalindrome(ListNode A) {// write code hereif (A == null) {return true;}// write code hereListNode fast = A;ListNode slow = A;//1.找到中间节点while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}//2.翻转链表ListNode cur = slow.next;while (cur != null) {ListNode curN = cur.next;cur.next = slow;slow = cur;cur = curN;}//3.判断回文//让A往后走,slow往前走直到;A.val==slow.val//注意:回文时会有偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回turewhile (A != slow) {if (A.val != slow.val) {return false;}//到这里A.val == slow.val//A.val == slow.val前提下,偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回tureif (A.next == slow) {return true;}A = A.next;slow = slow.next;}return true;}
}


 

5.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前:

注意:这里我的方法是,改完后,链表数据从小到大的,而做题在牛客网是,要求反过来(但是方法都一样)

理解视频:链表分割-CSDN直播

链表分割

//链表的分割public Node partition(Node pHead, int x) {Node as = null;Node ae = null;Node bs = null;Node be = null;Node cur = pHead;while (cur != null) {if (cur.data > x) {//第一次插入if (as == null) {as = ae = cur;}else {//第N次插入ae.next = cur;ae = ae.next;}} else {//第一次插入if (bs == null) {bs = be = cur;}else{//第N次插入be.next = cur;be = be.next;}}cur = cur.next;}//当一个链表为空时,返回if(as == null) {return bs;}//如果到这里as!= null//连接两部分ae.next = bs;//注意,第二部分结尾不为空时,要手动把第二部分最后一个节点,手动制空if(bs != null) {be.next = null;}//最后返回asreturn bs;}

6.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环返回空 :

方法是:第一次相遇点,到入口点的距离,等于起始点到入口点的距离

这里我画了这个图的推到:

public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast = head;ListNode slow = head;//  方法:第一次相遇点,到入口点的距离,等于起始点到入口点的距离while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(fast == slow) {break;}}/**1.走到这里,要么不满足{(fast != null && fast.next != null)}就是没有环;2. 要么就是有环*///没有环if(fast == null || fast.next == null) {return null;}/**有环:让slow以和fast以相同的速度,从起始点到入口点,fast从第一次相遇的成环点走到入口点*/slow = head;//把slow返回起始点while(slow != fast) {slow = slow.next;fast = fast.next;}return slow;}
}


7.输入两个链表,找出它们的第一个公共结点:

方法:先找到哪个链表长,再让长的链表他们的差值步最后两个链表一起走,直到他们第一次相遇。

public class Solution {public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {//1.先分别求出两个链表的长度ListNode pl = pHead1;ListNode ps = pHead2;int lenA = 0;int lenB = 0;while (pl != null) {lenA++;pl = pl.next;}while (ps != null) {lenB++;ps = ps.next;}//注意pl和ps,指向了null,要赋值回来pl = pHead1;ps = pHead2;//2.求差值int len = lenA - lenB;if (len < 0) {pl = pHead2;ps = pHead1;len = lenB - lenA;//len变为为正数}//现在知道pl指向长的链表,ps指向短的链表//3.操作两个链表pl和ps,长的链表(pl)先走链表的差值,然后再一起走直到相交while (len != 0) {pl = pl.next;len--;}//两个链表分别都走,直到他们相遇while (pl != ps) {pl = pl.next;ps = ps.next;}if (pl == null) {//pl,ps为空,也不可能相交return null;}return pl;}
}


 

 四.LinkedList的模拟实现:无头双向链表实现
 
1.写的类和包:
其实 无头双向链表,就比单链表多了一个,可以指向前一个节点的引用域,并且尾节点也被一个引用记录着。这样任意位置插入就不用记录节点了。
2.实现:
这里注意一下删除双链表指定位置Remove的节点 :可以优化一下代码,先删除头节点,之后尾节点和中间任意位置节点,有重复代码,(cur.prev.next = cur.next)可以共用;
public class MyLinkList implements  IList{static class ListNode{public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;//头节点public ListNode last;//尾节点@Overridepublic void addFirst(int data) {ListNode node = new ListNode(data);if (head == null) {head = last = node;}else {//所有的插入优先绑定后面node.next = head;head.prev = node;head = node;}}@Overridepublic void addLast(int data) {ListNode node = new ListNode(data);if (head == null) {head = last = node;}else {last.next = node;node.prev = last;last = last.next;}}@Overridepublic void addIndex(int index, int data) {int len = size();if (index > len || index < 0) {return;}if (index == len) {addLast(data);}if (index == 0) {addFirst(data);}ListNode cur = findIndex(index);ListNode node = new ListNode(data);node.next = cur;cur.prev.next = node;node.prev = cur.prev;cur.prev = node;}private ListNode findIndex(int index) {ListNode cur = head;while (index != 0) {cur = cur.next;index--;}return cur;}@Overridepublic boolean contains(int key) {ListNode cur = head;while (cur != null) {if (cur.val == key) {return true;}cur = cur.next;}return false;}@Overridepublic void remove(int key) {ListNode cur = head;while (cur != null) {if (cur.val == key) {if (cur == head) {//当只有一个节点要删除时if (head == null) {cur.next.prev = null;}head = head.next;//删除就走人return;}else {cur.prev.next = cur.next;//优化后,删除中间和尾巴的代码if (cur == last) {last = cur.prev;}else {cur.next.prev = cur.prev;}//删除就走人return;}}cur = cur.next;}}@Overridepublic void removeAllKey(int key) {ListNode cur = head;while (cur != null) {if (cur.val == key) {if (cur == head) {//当只有一个节点要删除时,cur.next.prev = null会为空,所以加上if判断if (head == null) {cur.next.prev = null;}head = head.next;//删除不能走人,接着删除后面。}else {cur.prev.next = cur.next;//优化后,删除中间和尾巴的代码if (cur == last) {last = cur.prev;}else {cur.next.prev = cur.prev;}//删除不能走人,接着删除后面。}}cur = cur.next;}}@Overridepublic int size() {ListNode cur = head;int len = 0;while (cur != null) {len++;cur = cur.next;}return len;}@Overridepublic void display() {ListNode cur = head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}@Overridepublic void clear() {ListNode cur = head;while (cur != null) {ListNode curN = cur.next;cur.next = null;cur.prev = null;cur = curN;}//注意head和last节点在链表中还被引用着head = last = null;}
}

五.LinkedList的使用:
1.什么是LinkedList:
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
2.  在集合框架中,LinkedList也实现了List接口,具体如下:

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

 
3. LinkedList也有有参数和二无参数的构造方法:
4.方法的使用表参考:
public class Test {public static void main(String[] args) {List<Integer> list = new LinkedList<>();list.add(1);list.add(2);list.add(3);list.add(4);System.out.println(list);ArrayList<Integer> list1 = new ArrayList<>();list1.add(11);list1.add(12);list1.add(13);System.out.println("==============");list.addAll(list1);System.out.println(list);}
}

输出:

5.LinkedList的遍历:ListIteratorIterator的一个子类,可以专门用来打印链表

代码如下:

public class Test {public static void main(String[] args) {List<Integer> list = new LinkedList<>();list.add(1);list.add(2);list.add(3);list.add(4);System.out.println(list);ArrayList<Integer> list1 = new ArrayList<>();list1.add(11);list1.add(12);list1.add(13);System.out.println("foreach遍历");for (Integer x:list) {System.out.print(x + " ");}System.out.println();System.out.println("迭代器遍历历");Iterator<Integer> it = list.iterator();while (it.hasNext()) {System.out.print(it.next() + " ");}/*** ListIterator是Iterator的一个子类,可以专门用来打印链表*/System.out.println();System.out.println("使用迭代器遍历---正向遍历");ListIterator<Integer> it1 = list.listIterator();while (it1.hasNext()) {System.out.print(it1.next() + " ");}System.out.println();System.out.println("使用反向迭代器---反向遍历");ListIterator<Integer> it2 = list.listIterator(/*这里要传链表的长度*/ list.size());while (it2.hasPrevious()) {System.out.print(it2.previous() + " ");}}
}


 

 六.ArrayList和LinkedList的区别:

                                                                                 

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

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

相关文章

2024年7月13日全国青少年信息素养大赛Python复赛小学高年级组真题

第一题 题目描述 握情况。他决定让每个人输入一个正整数 N (0≤N≤1000)&#xff0c;然后计算并输出(5*N)的值。请用 在一个神秘的王国里&#xff0c;国王希望通过一个简单的测试来评估他的子民对基 础数学运算的掌 Python 编写程序&#xff0c;程序执行后要求用户输入一个正…

Hash表(C++)

本篇将会开始介绍有关于 unordered_map 和 unordered_set 的底层原理&#xff0c;其中底层实现其实就是我们的 Hash 表&#xff0c;本篇将会讲解两种 Hash 表&#xff0c;其中一种为开放定址法&#xff0c;另一种为 hash 桶&#xff0c;在unordered_map 和 unordered_set 的底层…

智驭未来:人工智能与目标检测的深度交融

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;如同一股不可阻挡的浪潮&#xff0c;正以前所未有的速度重塑着我们的世界。在众多AI应用领域中&#xff0c;目标检测以其独特的魅力和广泛的应用前景&#xff0c;成为了连接现实与智能世界的桥梁。本文旨在…

20240715 每日AI必读资讯

&#x1f310; 代号“ 草莓 ”&#xff0c;OpenAI 被曝研发新项目&#xff1a;将 AI 推理能力提至新高度 - OpenAI 公司被曝正在研发代号为“ 草莓 ”的全新项目&#xff0c;进一步延伸去年 11 月宣布的 Q* 项目&#xff0c;不断提高 AI 推理能力&#xff0c;让其更接近人类的…

基于Java的休闲娱乐代理售票系统

你好&#xff0c;我是专注于Java开发的码农小野&#xff01;如果你对系统开发感兴趣&#xff0c;欢迎私信交流。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术、SpringBoot框架、B/S架构 工具&#xff1a;Eclipse IDE、MySQL数据库管理工具…

牛客小白月赛98 (个人题解)(补全)

前言&#xff1a; 昨天晚上自己一个人打的小白月赛&#xff08;因为准备数学期末已经写烦了&#xff09;&#xff0c;题目难度感觉越来越简单了&#xff08;不在像以前一样根本写不了一点&#xff0c;现在看题解已经能看懂一点了&#xff09;&#xff0c;能感受到自己在不断进步…

2024年是不是闰年?

闰年的由来 闰年的概念最早可以追溯到古罗马时期的朱利叶斯凯撒。当时的罗马历法是根据太阳年来制定的&#xff0c;每年大约有365.25天。为了使日历与季节保持同步&#xff0c;人们需要定期插入一个额外的日子。朱利叶斯凯撒在公元前46年颁布了一项法令&#xff0c;规定每四年增…

SAP PP学习笔记26 - User Status(用户状态)的实例,订单分割中的重要概念 成本收集器,Confirmation(报工)的概述

上面两章讲了生产订单的创建以及生产订单的相关内容。 SAP PP学习笔记24 - 生产订单&#xff08;制造指图&#xff09;的创建_sap 工程外注-CSDN博客 SAP PP学习笔记25 - 生产订单的状态管理(System Status(系统状态)/User Status(用户状态)),物料的可用性检查&#xff0c;生…

最长下降序列

如何理解这个题目呢,我们可以每个人的分数放到排名上&#xff0c;然后求解最长下降序列即可 #include<bits/stdc.h> using namespace std;int n; const int N (int)1e5 5; int a[N]; int b[N]; int d[N]; int dp[N]; int t;int main() {cin >> t;while (t--) {…

Apache Hadoop之历史服务器日志聚集配置

上篇介绍绍了Apache Hadoop的分布式集群环境搭建&#xff0c;并测试了MapReduce分布式计算案例。但集群历史做了哪些任务&#xff0c;任务执行日志等信息还需要配置历史服务器和日志聚集才能更好的查看。 配置历史服务器 在Yarn中运行的任务产生的日志数据不能查看&#xff0…

【计算机毕业设计】013新闻资讯微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Python数据分析案例51——基于K均值的客户聚类分析可视化

案例背景 本次案例带来的是最经典的K均值聚类&#xff0c;对客户进行划分类别的分析&#xff0c;其特点是丰富的可视化过程。这个经典的小案例用来学习或者课程作业在合适不过了。 数据介绍 数据集如下: 客户的编码&#xff0c;性别&#xff0c;年龄&#xff0c;年收入&#…

Vue2-集成路由Vue Router介绍与使用

文章目录 路由&#xff08;Vue2&#xff09;1. SPA 与前端路由2. vue-router基本使用创建路由组件声明路由链接和占位标签创建路由模块挂载路由模块 3. vue-router进阶路由重定向嵌套路由动态路由编程式导航导航守卫 本篇小结 更多相关内容可查看 路由&#xff08;Vue2&#xf…

安全防御----防火墙综合实验2

安全防御----防火墙综合实验2 一、题目 二、实验要求&#xff1a; 1&#xff0c;DMZ区内的服务器&#xff0c;办公区仅能在办公时间内&#xff08;9&#xff1a;00 - 18&#xff1a;00&#xff09;可以访问&#xff0c;生产区的设备全天可以访问. 2&#xff0c;生产区不允许访…

雷赛运动控制卡编程(1)

一、运动控制卡选择 电气常用知识-CSDN博客 如下旋转控制卡 DMC3800八轴高性能点位卡 - 东莞市雅恰达机电有限公司 轴少的时候选择脉冲系列卡 轴多的话就选总线型系列控制卡 样品 架构&#xff1a; 二、 添加文件 dll 添加接口文件 【最全&#xff0c;带注释版】雷赛运动…

OpenCV中使用Canny算法在图像中查找边缘

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 算法描述 Canny算法是一种广泛应用于计算机视觉和图像处理领域中的边缘检测算法。它由John F. Canny在1986年提出&#xff0c;旨在寻找给定噪声条件下的最佳边…

Python+wxauto=微信自动化?

Pythonwxauto微信自动化&#xff1f; 一、wxauto库简介 1.什么是wxauto库 wxauto是一个基于UIAutomation的开源Python微信自动化库。它旨在帮助用户通过编写Python脚本&#xff0c;轻松实现对微信客户端的自动化操作&#xff0c;从而提升效率并满足个性化需求。这一工具的出现&…

详细分析Sql Server中的declare基本知识

目录 前言1. 基本知识2. Demo3. 拓展Mysql4. 彩蛋 前言 实战探讨主要来源于触发器的Demo 1. 基本知识 DECLARE 语句用于声明变量 声明的变量可以用于存储临时数据&#xff0c;并在 SQL 查询中多次引用 声明变量&#xff1a;使用 DECLARE 语句声明一个或多个变量变量命名&a…

SpringBoot整合JAX-RS接口

目录 二、创建RESTful资源 三、注册JAX-RS资源 四、修改配置等信息 五、启动SpringBoot程序、访问服务 六、遇到的问题 七、与feign进行配合使用 1、接口定义 2、接口实现 3、注册资源 4、调用方web服务实现&#xff0c;跟注入普通服务一样 5、启动两个服务&#xff…

html5——表单

目录 表单基本结构 表单标签 常用表单元素 文本框 密码框 邮箱 单选按钮 复选框 文件域 隐藏域 列表框 多行文本域 lable标签 表单按钮 常用表单属性 只读与禁用 placeholder required pattern autofocus autocomplete 用于指定表单是否有自动完…