链表?细啊!超详细的知识点总结!

链表

定义:链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用。

​ 其实链表就是有序的列表,它在内存中不一定是连续存储的。我们把它画成连起来是逻辑结构的,而不是内存中的实际情况。

链表是一种兼具递归和迭代性质的数据结构

在这里插入图片描述

特点:

  1. 链表是以节点的方式来存储,是链式存储

  2. 每个节点包含 data 域next 域(指向下一个节点)

  3. 链表的各个节点不一定是连续存储。

  4. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

——————

一些概念:

【头节点:在链表的第一个节点之前会额外增设一个节点,该节点的数据域一般不存放数据】

【首元节点:链表中第一个元素所在的节点,它是头节点后边的第一个节点】

(都首元了,元就是元素,第一个有元素的节点)

【头指针:永远指向链表中第一个节点的位置,有头节点就指向头节点】
(下图网上找的)
在这里插入图片描述

单链表的应用实例

构造链表:

​ 根据递归定义,一个Node类型的变量就可以表示一条链条。只需要保证它的值是null或者指向另外一个Node对象且该对象的next域指向了另外一条链条即可。

public class Node {

    //数据域 item(参数类型)
    public Item data; 
    
    //指针域,指向下一个节点
    public Node next;

    public Node() {
    }

    public Node(Item data) {
        this.data = data;
    }

    public Node(Item data, Node next) {
        this.data = data;
        this.next = next;
    }
}

	// 创建一些节点
	Node first = new Node();
	Node second = new Node();
	Node third = new Node();
	// 设置所需要的值
	first.data = "to";
	second.data = "be";
	third.data = "or";
//当然这样直接有参构造也行: Node newNode = new Node(value);

	// 设置next域来构造链表
	first.next = second;
	second.next = third;

注意:third.next是null,这个是创建时候被初始化的值。链表表示的是一列元素。first表示的序列是to,be,or。数组也可以表示:String[] s = {“to”,“be”,“or”}。

在这里插入图片描述

在表头插入元素:

向链表中插入一个新的节点,最简单的就是链表的开头。

实现思路:

​ 1.找到当前链表的尾节点

​ 2.将最后这个节点的 next 指向新的要加入的节点

public void addFirst(item value) {
    //初始化要加入的节点
    Node newNode = new Node(value);
	//因为 head 节点是不能动,因此我们需要一个辅助节点来遍历整个链表
	Node temp = head;
	//遍历链表,找到尾节点的位置
	while(temp.next == null) {
		//如果没有找到最后, 将将 temp 后移
		temp = temp.next;
	}
	//当退出 while 循环时,temp 就指向了链表的最后
	//将最后这个节点的 next 指向 新的节点
	temp.next = newNode;
}             

插入节点:

1.因为要插入到指定位置,所以需要有位置相关的判断属性。然后进行相关判断,找到插入的位置

2.设置一个辅助指针遍历链表,去找要插入位置的上一个节点。因为这样节点有next域,这样子才可以将插入的节点前后链接起来。

(比如2个节点,要插在中间,newNode.next—>后一个节点,让前一个节点next—>newNode即可。注意顺序,因为先前一个节点next—>newNode,这样子后面newNode.next—>后一个节点是通过等于前一个节点.next。这样子会出错)

这里可以一开始在节点添加一个判断属性,或者额外定义一个值,记录遍历的位置

// index 要插入的位置
public void insert(item val, int index) {
        Node newNode = new Node(val);
        if (index == 0) {
            newNode.next = head;
            head = newNode;
        } else {
            Node temp = head;
            // 通过遍历,找到插入位置的上一个节点
            // 当i = index - 1 时,也代表temp就是要插入点的上一个节点的位置了
            for (int i = 0; i < index - 1 && temp != null; i++) {
                temp = temp.next;
            }
            // 当 temp 指向了空节点,意味着已经到达了链表的末尾,仍没有找到位置
            if (temp == null) {
                System.out.println("Index out of bound!");
                return;
            }
            // 插入节点的指向等于了上一个节点的指向
            newNode.next = temp.next;
            // 然后
            temp.next = newNode;
        }
    }

也可以创建一个变量记录当前遍历位置 location,用来判断到达的位置:

int location = 0;
while (temp.next != null) {
	// 找到要插入位置的前一个节点
	if ((index - 1) == location) {
    	newNode.next = temp.next;
        temp.next = newNode;
    	return;
    }
	location++;
	temp = temp.next;
}

删除节点

从表头删除节点:即删除首节点,直接让头指针指向原本的下一个节点即可。

first = first.next;

指定删除具体某个节点:

和指定位置插入节点原理相似,只是删除是让前一个节点的next域指向删除节点的后一个节点,即:temp.next = temp.next.next

public void delete(Node head, int index) {
    // 临时节点
    Node temp = head;
    for (int i = 0; i < (index - 1) && temp != null; i++) {
        temp = temp.next;
    }
    // 删除
    // 如果 temp.next 为 null,则说明当前节点是链表的最后一个节点,此时再执行 temp.next.next 就会导致空指针异常
    if (temp != null && temp.next != null) {
        temp.next = temp.next.next;
    }
}

也可以创建一个变量记录当前遍历位置 location,用来判断到达的位置:

int location = 0;
while(true) {
	if(temp.next == null) { //已经到链表的最后
		break;
	}
	if ((index - 1) == location) {
	//找到的待删除节点的前一个节点 temp
		flag = true;
		break;
	}
	temp = temp.next; //temp 后移,遍历
}
//判断 flag
if(flag) { //找到
//可以删除
	temp.next = temp.next.next;
}else {
	System.out.printf("要删除的 %d 节点不存在\n", no);
	}
}

遍历链表

遍历数组的时候,可以用for循环遍历 a[ ] 中所有元素

for (int i = 0; i < N; i++) {
	// 处理a[i]
}

访问链表中是所有元素也有方法:索引变量x初始化为链表初节点,然后通过x.data访问与x相关联的元素,并将x设为x.next来访问链表下一个节点,直到x为null为止。

for (Node x = first; x != null; x = x.next) {
	// 处理x.data
}

其实也就是用一个辅助节点去依次走完整个链表,然后输出它的每次节点。

Node temp = head.next;   // 辅助节点,从首节点开始
//判断是否到链表最后
while (temp != null) {
    // 输出节点信息
    System.out.println(temp.data);
    // 后移,遍历下一个节点
    temp = temp.next;
}

修改节点的信息

这个不需要找到前一个节点,直接找到你要修改的那个节点,然后修改它的data即可。因为它对链表的结构链接没有影响

HeroNode temp = head.next; // 从第一个有效节点开始遍历

for (int i = 0; temp != null && i < index; i++) {
    temp = temp.next;
}

if (temp != null) {
    // 找到目标节点,进行修改
    temp.data = newData;
} else {
    // 目标节点不存在
    System.out.println("目标节点不存在");
}

单链表的相关操作:

1.求单链表中有效节点的个数

定义一个计数器,再借助辅助节点从首节点开始遍历链表即可。

public static int getLength(Node head) {
    
	if(head.next == null) {
    	// 首节点为null,代表是空链表
    	return 0;
	}
	int length = 0;
	Node temp = head.next;
	while(temp != null) { 
		length++;
    	temp = temp.next; // 后移 
	}
	return length;
}

2.查找单链表中的倒数第 k 个结点:

​ 因为单链表只能从前往后遍历,所有先遍历整个链表;确定这个链表的长度。然后通过总长度减去k就能知道它正数是第几个节点了;也就是它的正数索引值。

public static HeroNode findLastIndexNode(Node head, int index) {
	//判断如果链表为空,返回 null
	if(head.next == null) {
		return null;//没有找到
}
    //上一个问题已经求出了链表中的有效节点个数,也就是链表的长度了。
    int size = getLength(head);
    // 对 index 的值进行校验
    if(index <=0 || index > size) {
		return null;
	}
    // 从正向数的话,位置为 size-index
    // 定义辅助变量,让其找到倒数第 index 节点
    Node cur = head.next; 
	for(int i =0; i< size - index; i++) {
		cur = cur.next;
	}
	return cur;
}

3.单链表的反转(面试常出):

​ 单链表的反转,可以通过很多种方法实现。包括迭代法,递归法,

  • 迭代法:

    1. 定义三个指针:prev、current和next,它们分别表示前一个节点、当前节点和下一个节点。

    2. 初始化时,prev为null,current为头节点。

    3. 在循环中,不断将current的next指针指向prev,然后依次向后移动prev、current和next指针。

    4. 当next为空时,说明已经到达链表末尾,此时的prev指向就是反转后的头节点。

    其实就像是先将第一个节点指向null,就像最后一个节点它也是next = null的;然后一直这样子重复,转一次就后退,让下一个节点去转方向指向前面的。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    public ListNode reverse(Node head) {
            Node prev = null;
            Node current = head;
    
            while (current != null) {
                Node nextTemp = current.next; // 暂存当前节点的下一个节点
                current.next = prev; // 将当前节点指向前一个节点
                prev = current; // 更新prev为当前节点
                current = nextTemp; // 更新current为原先的下一个节点
            }
    
            return prev; // 返回反转后的头节点
        }
    

    // 补充一个力扣第92题,加深理解(让你把单链表部分元素反转,其他部分不变)

  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 这里依旧可以使用迭代的方法,就是要先通过遍历去找到反转开始的位置和结束的位置;用辅助节点记录反转区间的前置节点和反转区间的尾节点。然后反转区间的链表反转即可,反转后接上前面的部分。

在这里插入图片描述

public ListNode reverseBetween(ListNode head, int left, int right) {
    ListNode current = head;
    ListNode prev = null;
    for (int i = 1; i < left; i++) {
        prev = current;
        current = current.next;
    }
    // 反转区间的前置节点
    ListNode tailPrev = prev;
    // 反转区间的尾节点
    ListNode tail = current;
    // 同样的迭代原理,只是范围要自定义。
    for (int j = left; j <= right; j++) {
        ListNode newTemp = current.next;
        current.next = prev;
        prev = current;
        current = newTemp;
    }
    // 反转区间的头节点
    ListNode headReverse = prev;
    tail.next = current;
    if (tailPrev != null) {
        tailPrev.next = headReverse;
        return head;
    } else {
        return headReverse;
    }
}

递归法:(也是力扣第206题)

​ 递归的关键是看整体,找出整体的大块东西。可以先写一个伪代码过一遍思路。你就写基础情况,然后要的操作,再看子问题(递归调用)放的位置即可。

实操技巧:第一步先找到可以看作整体的,就是原理一样的。去设计递归调用(超级操作),第二步去设计微操作,去看一份整体的怎么实现即可。后面就去找到题目中的基础情况,它相当于最里面的超级操作了,也基本就是剩下一个的情况那种。

相关概念:

​ 1.首先先明确函数的意义,还要原问题和子问题。在这里原问题是给整个链表反转,子问题是给每一个字节进行反转。

​ 2.基础情况,也就是要找到结束的条件。就是当数据规模很小的时候,能结束递归,返回答案。

​ 3.递归调用(超级操作),怎么搞定中间的递归操作。但是!唉!人脑进去递归出不来的。所以得完把递归的过程看成一个整体,不要去想里面怎么运行的。

​ 4.微操作。也就是操作的方法。

​ 比如汉诺塔问题,你有2个叠在一起的时候,就是先把小的放中间,大的放右边,再把小的放大的上面。那这个时候,假如他有一堆,你就是把小的一堆给放到中间,让最大的去到最右边垫底。然后小的一堆整体放到大的上面。而那一堆小的移动其实就是整个问题的子问题,它其实就是可以用递归重复一个原理完成。

此题思路如下:

想想只有2个怎么反转,那现在就把那一堆后面的看作一块。然后那一堆里面自己再反转就行,这就是它自己的超级操作咯,不理他。

在这里插入图片描述

ListNode reverse(ListNode head) {
    // 基础情况,也就是结束的代码。
    // 链表为空或者只有一个节点时,直接返回头节点即可。
    if (head == null || head.next == null) {
        return head;
    }
    // 递归调用(超级操作)
    ListNode last = reverse(head.next);
    // 而其实当你写一个伪代码时候,你也可以发现。下面的这个其实就是反转需要的的操作,可以写一个伪代码,微操作。具体操作方法: operate(head.next); 
    head.next.next = head;
    head.next = null;
    return last;
}

再看看上面的力扣92题(反转部分链表)用递归法怎么做。

思路:其实他就是在整体反转改为一个范围内的反转,然后反转后,你要连接剩下的部分。所以你需要记录反转区间的两个点,即m-1和n+1这两个点。这个后面补

4.从尾到头打印单链表

有两种方法:1.先进行反转,然后再遍历 (会破坏链表的原本顺序结构,不好)

​ 2.利用的特性来实现。(先进后出)

public static void reversePrint(HeroNode head) {
	if(head.next == null) {
		return;//空链表,不能打印
	}
    //创建要给一个栈,将各个节点压入栈
	Stack<HeroNode> stack = new Stack<HeroNode>();
	HeroNode cur = head.next;
	//将链表的所有节点压入栈
	while(cur != null) {
		stack.push(cur);  // push 入栈
		cur = cur.next; //cur 后移,这样就可以压入下一个节点
	}
	//将栈中的节点进行打印,pop 出栈
	while (stack.size() > 0) {
		System.out.println(stack.pop()); 
	}
}

5.合并两个有序的单链表,合并之后的链表依然有序:

这里可以用到迭代的思路。迭代多用在:需要遍历;重复执行一段代码;直到满足要求。

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    // 创建一个值为 -1 的虚拟节点
	ListNode dummy = new ListNode(-1);
	ListNode current = dummy;

    // 依次遍历两个链表的节点,进行大小比较。
    // 此时两个链表都没有遍历完,进行大小比较入链表
	while (l1 != null && l2 != null) {
		if (l1.val <= l2.val) {
			current.next = l1;
            l1 = l1.next;
		} else {
            current.next = l2;
			l2 = l2.next;
		}
		current = current.next;
	}
    
	// 当某一个链表遍历完后,剩下的一条链表直接全部链接过去就行了
	if (l1 != null) {
		current.next = l1;
	} else {
        current.next = l2;
	}
    // 返回链表的头节点,因为dummy是虚拟节点。
	return dummy.next;
}

​ 在 mergeTwoLists 方法中,我们使用 dummy 节点作为合并后链表的头节点。然后,我们使用 current 节点迭代合并两个链表。在循环中,比较两个链表当前节点的值,选择较小的节点连接到新链表上,并将对应链表的指针向后移动。最后,将剩余未处理完的链表连接到新链表的末尾。返回 dummy.next 即可得到合并后的有序链表。

双向链表

单链表的缺点:

  • 查找只能单方向,双向链表有两个方向指向,所以可以从前往后或者从后往前。
  • 删除和插入需要辅助节点,双向链表因为存在prev和next域,可以实现自我删除
遍历:

​ 和单链表方法一样,因为头节点不能动,使用辅助节点来遍历。正向的已经写过了,这里展示一个从后往前遍历的。先到尾节点,然后用prev域指向前一个节点依次遍历。

public void List(Node head) {
    Node current = head; // 当前节点
    
    // 将current指针移动到链表尾部
    while (current.next != null) {
        current = current.next;
    }
    
    // 从链表尾部向前遍历
    while (current != null) {
        System.out.println(current); // 打印当前节点的值
        current = current.prev; // 移动current指针到前一个节点
    }
}

添加节点:

​ 指定位置的添加节点,你得知道要加在哪里。所以得用一个辅助节点去遍历找到位置,然后因为现在是双向链表了,添加逻辑就要修改一下了。

// 添加节点到链表尾部
public void add(Node node) {
	Node temp = head;
	while(true){
        // 到了链表的最后的话,就可以停下来了
        if(temp.next == null){
            break;
        }
        // 如果没有找到最后, 就将 temp 继续后移。让temp等于尾节点的位置
        temp = temp.next;
    }
    //添加逻辑: 尾节点.next = 要添加的; 要添加的.pre = temp;
    temp.next = node;
    node.pre = temp;
}

​ 注意要先搞定新节点的指向问题!因为假如你先调整current节点的指向,那么等一下新节点就借助不了current来实现添加了!

	// 在指定索引位置插入节点
    public void insertAtIndex(int index, int data) {
        Node newNode = new Node(data);
        if (index < 0) {
            return;
        }
        // 新节点成为头节点
        if (index == 0) {
            newNode.next = head;
            // 链表为空的话,newNode.next将被赋值为null
            if (head != null) {
                head.prev = newNode;
            }
            // 让新节点成为头节点
            head = newNode;
            return;
        }
        // 让current到达插入点的位置
        Node current = head;
        int count = 0;
        while (count < index - 1 && current != null) {
            current = current.next;
            count++;
        }
        
        if (current == null) {
            System.out.println("大于链表的大小");
            return;
        }
	// 注意要先搞定新节点的指向!
    //因为假如你先调整current节点的指向,那么等一下新节点就借助不了current来添加。
       	// 新的节点指向下一个节点
        newNode.next = current.next;
        // 新的节点prev指回当前节点
        newNode.prev = current;
        // 代表已经是最后一个节点了,也就不需要下一个节点prev指回这个新节点的操作
        if (current.next != null) {
            // 下一个节点prev指回这个新节点
            current.next.prev = newNode;
        }
        // 当前节点指向新节点
        current.next = newNode;
    }

删除节点:

​ 让temp去找到要删除的节点,这次不用找前一个节点了。有前后域,可以直接删除了。注意要先搞定新节点的指向问题!因为假如你先调整current节点的指向,那么等一下新节点就借助不了current来添加。

	// 在指定索引位置删除节点
    public void deleteAtIndex(int index) {
        if (head == null || index < 0) {
            System.out.println("空链表或者删除的位置不存在");
            return;
        }

        if (index == 0) {
            head = head.next;
            if (head != null) {
                head.prev = null;
            }
            return;
        }

        Node current = head;
        int count = 0;
        while (count < index && current != null) {
            current = current.next;
            count++;
        }
		// 这个index已经超过了链表范围了
        if (current == null) {
            return;
        }
		// 判断是否到最后一个节点了,下面两个代码顺序对于删除操作是没有影响的
        if (current.next != null) {
            current.next.prev = current.prev;
        }
        current.prev.next = current.next;
    }

修改节点元素实现和单链表这个一样的,也利用不到双向链表的性质。

单向环形链表(约瑟夫环问题)

​ Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此 产生一个出队编号的序列。

分析与思路:

  1. 先构建一个n个节点的单向环形链表
  2. 从k节点开始,从1计到m,就输出和删除那个m的节点。
  3. 然后下一个节点又从1开始这样循环下去,全部删除完就结束。

创建环形链表

​ 尾节点指回头节点就行了,一开始的节点也要指向自身。

public class CircularLinkedList {
    private Node head;

    // 节点类
    private static class Node {
        int data;
        Node next;

        public Node(int data) {
            this.data = data;
            this.next = null;
        }
    }

	// 在链表末尾插入节点
    public void insert(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
            head.next = head; // 将头节点的 next 指针指向自身,形成环
        } else {
            Node current = head;
            // 判断当前节点的下一个节点是否等于头节点
            // 循环链表,最后一个节点是指向头节点的
            while (current.next != head) {
                current = current.next;
            }
            // 将最后一个节点指向新节点
            current.next = newNode;
            // 新节点的 next 指针指向头节点,形成环
            newNode.next = head; 
        }
    }
    
    // 遍历链表
    public void printList() {
        if (head == null) {
            return;
        }
        Node current = head;
        while (true) {
            // 要先打印首节点,因为可能链表就一个节点。
            // 假如先判断,这样子首节点指向自己,停止循环。则不打印首节点了
            System.out.printf(current.data);
			if (current.next == head) { // 说明已经遍历完毕
				break;
			}
			current = current.next; 
        }
    }

出圈思路如图分析:

在这里插入图片描述

在这里插入图片描述

  • 优化:这里可以把prev设计成null,则省了遍历整个链表去找最后一个节点的过程,将当前节点移动到开始出圈的位置,只用把prev = current即可。

其实这个就是一个双指针问题,双指针问题解决链表问题很常见。后续会出相关的专题

代码:

public void josephus(int k, int m, int n) {
        if (head == null || k <= 0 || m <= 0 || n <= 0) {
            return;
        }

        Node current = head;
    	// 这里设计成null,则省了遍历整个链表去找最后一个节点的过程
        Node prev = null;

        // 将当前节点移动到开始出圈的位置
        for (int i = 0; i < k - 1; i++) {
            prev = current;
            current = current.next;
        }

        // 开始模拟出圈过程
        while (current.next != current) {
            for (int i = 0; i < m - 1; i++) {
                // 继续移动到要删除的节点
                prev = current;
                current = current.next;
            }
            prev.next = current.next;
            System.out.print(current.data + " ");
            current = prev.next;
        }
    	// 最后留在里面的节点
        System.out.println(current.data);
    }
}

链表有一些力扣题目,这个后面再结合力扣刷题来写。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux面试题(二)

目录 17、怎么使一个命令在后台运行? 18、利用 ps 怎么显示所有的进程? 怎么利用 ps 查看指定进程的信息&#xff1f; 19、哪个命令专门用来查看后台任务? 20、把后台任务调到前台执行使用什么命令?把停下的后台任务在后台执行起来用什么命令? 21、终止进程用什么命令…

C++学习之路(五)C++ 实现简单的文件管理系统命令行应用 - 示例代码拆分讲解

简单的文件管理系统示例介绍: 这个文件管理系统示例是一个简单的命令行程序&#xff0c;允许用户进行文件的创建、读取、追加内容和删除操作。这个示例涉及了一些基本的文件操作和用户交互。 功能概述&#xff1a; 创建文件 (createFile())&#xff1a; 用户可以输入文件名和内…

11【保姆级】-GO语言的struct

11【保姆级】-GO语言的struct 一、Go的面向对象1.1 说明 二、结构体2.1 结构体和结构体变量(实例)的区别和联系2.2 声明结构体 和 细节说明2.3 结构体在内存中的布局2.4 创建结构体和访问结构体的四种方式 在学习GO语言时&#xff1a; 先建立整体框架&#xff0c;然后再去抠细节…

职场快速赢得信任

俗话说的好&#xff0c;有人的地方就有江湖。 国内不管是外企、私企、国企&#xff0c;职场环境都是变换莫测。 这里主要分享下怎么在职场中快速赢取信任。 1、找到让自己全面发展的方法 要知道&#xff0c;职场中话题是与他人交流的纽带&#xff0c;为了找到共同的话题&am…

elastic -job和springboot集成实现分布式调度5

一 案例介绍说明 1.1 案例介绍 基于 Spring boot 集成方式的而产出的工程代码&#xff0c;完成对作业分片的实现&#xff0c;文件数据备份采取更接近真实项目的数 据库存取方式。 1.分片设置 2.每个线程获取给自的类型 1.2 作业配置 zk的配置 二 操作说明 2.1 数据表的初始…

【单片机学习笔记】STC8H1K08参考手册学习笔记

STC8H1K08参考手册学习笔记 STC8H系列芯片STC8H1K08开发环境串口烧录 STC8H系列芯片 STC8H 系列单片机是不需要外部晶振和外部复位的单片机&#xff0c;是以超强抗干扰/超低价/高速/低功耗为目标的 8051 单片机,在相同的工作频率下,STC8H 系列单片机比传统的 8051约快12 倍速度…

讯飞星火知识库文档问答Web API的使用(二)

上一篇提到过星火spark大模型&#xff0c;现在有更新到3.0&#xff1a; 给ChuanhuChatGPT 配上讯飞星火spark大模型V2.0&#xff08;一&#xff09; 同时又看到有知识库问答的web api&#xff0c;于是就测试了一下。 下一篇是在ChuanhuChatGPT 中单独写一个基于星火知识库的内容…

机器学习库:pandas

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 文章目录 写在开头 基本数据格式 DataFrame 数据选取 iloc 数据描述 head describe 数据合并 merge 数据删除 drop drop删除多列 处理缺失…

LeetCode Hot100 236.二叉树的最近公共祖先

题目&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节…

基于U-Net的视网膜血管分割(Pytorch完整版)

基于 U-Net 的视网膜血管分割是一种应用深度学习的方法&#xff0c;特别是 U-Net 结构&#xff0c;用于从眼底图像中分割出视网膜血管。U-Net 是一种全卷积神经网络&#xff08;FCN&#xff09;&#xff0c;通常用于图像分割任务。以下是基于 U-Net 的视网膜血管分割的内容&…

什么是分布式锁?Redis实现分布式锁详解

目录 前言&#xff1a; 分布式系统买票示例 引入redis做分布式锁 引入过期时间 引入校验id 引入lua脚本 过期时间续约问题 redlock算法 小结&#xff1a; 前言&#xff1a; 在分布式系统中&#xff0c;涉及多个主机访问同一块资源&#xff0c;此时就需要锁来做互斥控制…

[论文阅读]CBAM——代码实现和讲解

CBAM 论文网址&#xff1a;CBAM 论文代码&#xff1a;CBAM 本文提出了一种卷积块注意力模块&#xff08;CBAM&#xff09;&#xff0c;它是卷积神经网络&#xff08;CNN&#xff09;的一种轻量级、高效的注意力模块。该模块沿着通道和空间两个独立维度依次推导注意力图&#x…

ehr人力资源管理系统(实际项目源码)

eHR人力资源管理系统&#xff1a;功能强大的人力资源管理工具 随着企业规模的不断扩大和业务需求的多样化&#xff0c;传统的人力资源管理模式已无法满足现代企业的需求。eHR人力资源管理系统作为一种先进的管理工具&#xff0c;能够为企业提供高效、准确、实时的人力资源管理…

GPT实战系列-GPT训练的Pretraining,SFT,Reward Modeling,RLHF

GPT实战系列-GPT训练的Pretraining&#xff0c;SFT&#xff0c;Reward Modeling&#xff0c;RLHF 文章目录 GPT实战系列-GPT训练的Pretraining&#xff0c;SFT&#xff0c;Reward Modeling&#xff0c;RLHFPretraining 预训练阶段Supervised FineTuning &#xff08;SFT&#x…

基于51单片机交通灯夜间模式+紧急模式_易懂版_(仿真+代码_报告_讲解)

J029 51单片机交通灯_易懂版__夜间紧急(仿真代码_报告_讲解&#xff09; 51单片机交通灯_易懂版_ 1 **讲解视频&#xff1a;**2 **功能要求**3 **仿真图&#xff1a;**4 **程序设计&#xff1a;**5 **设计报告**6 **资料清单&&下载链接&#xff1a;****资料下载链接&am…

超好玩C++控制台打飞机小游戏,附源码

我终于决定还是把这个放出来。 视频在这&#xff1a;https://v.youku.com/v_show/id_XNDQxMTQwNDA3Mg.html 具体信息主界面上都有写。 按空格暂停&#xff0c;建议暂停后再升级属性。 记录最高分的文件进行了加密。 有boss&#xff08;上面视频2分47秒&#xff09;。 挺好…

2023年【N1叉车司机】新版试题及N1叉车司机作业考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 N1叉车司机新版试题参考答案及N1叉车司机考试试题解析是安全生产模拟考试一点通题库老师及N1叉车司机操作证已考过的学员汇总&#xff0c;相对有效帮助N1叉车司机作业考试题库学员顺利通过考试。 1、【多选题】《中华…

Linux系统编程 day05 进程控制

Linux系统编程 day05 进程控制 1. 进程相关概念2. 创建进程3. exec函数族4. 进程回收 1. 进程相关概念 程序就是编译好的二进制文件&#xff0c;在磁盘上&#xff0c;占用磁盘空间。程序是一个静态的概念。进程即使启动了的程序&#xff0c;进程会占用系统资源&#xff0c;如内…

【服务器能干什么】二十分钟搭建一个属于自己的 RSS 服务

如果大家不想自己捣鼓,只是想尝尝鲜,可以在下面留言,我后台帮大家开几个账号玩一玩。 哔哩哔哩【高清版本可以点击去吐槽到 B 站观看】:【VPS服务器到底能干啥】信息爆炸的年代,如何甄别出优质的内容?你可能需要自建一个RSS服务!_哔哩哔哩_bilibili 前言 RSS 服务 市…

MySQL表连接

文章目录 MySQL内外连接1.内连接2.外连接&#xff08;1&#xff09;左外连接&#xff08;2)右外连接 3.简单案例 MySQL内外连接 1.内连接 内连接的SQL如下&#xff1a; SELECT ... FROM t1 INNER JOIN t2 ON 连接条件 [INNER JOIN t3 ON 连接条件] ... AND 其他条件;说明一下…