Java之链表(不带头结点,带头结点,迭代实现,递归实现)

目录

一.链表

1.什么是链表

2.链表的分类

 二.不带头结点单向链表的非递归实现

1.接口的定义

2. 不带头结点单向链表的结构

3.链表的添加操作(头插法和尾插法)

1.头插法

2.尾插法

4. 链表的插入操作

5.链表的删除操作

1.删除指定索引的结点

2.删除指定值的第一个结点

3.删除指定值的所有结点

6.链表的其他操作

1.将指定索引的结点的值更换为指定值

2.获得指定索引结点的值

3.判断链表是否包含指定值

4.获得指定值的第一个索引

5.toString方法

三.带头结点单向链表的非递归实现

1.带头结点单向链表的结构

2.链表的添加操作(头插法和尾插法)

1.头插法

2.尾插法

3. 链表的插入操作

4.链表的删除操作

1.删除指定索引的结点

2.删除指定值的第一个结点

3.删除指定值的所有结点

5.链表的其他操作

四.不带头结单向点链表的递归实现

1.基本介绍

2. 链表的插入操作

3.递归打印链表结点的值

1.正序打印

2.逆序打印

4.链表的删除操作

1.删除指定值的所有结点

2.删除链表中的重复值(保留)

3.删除链表中的重复值(不保留)

5.链表的其他操作

五.不带头结点双向链表的非递归实现

1.不带头结点双向链表的结构

2.链表的添加操作(头插法和尾插法)

1.头插法

2.尾插法

3. 链表的插入操作

4.链表的删除操作

1.删除指定索引的结点

2.删除指定值的第一个结点

3.删除指定值的所有结点


一.链表

1.什么是链表

链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的 引用链接 次序实现的 。

 

2.链表的分类

链表的类型多种多样,具体可以分为:

1.带头结点的链表和不带头结点的链表

2.单向链表和双向链表

3.循环的和非循环的

 二.不带头结点单向链表的非递归实现

1.接口的定义

在实现具体的方法之前,我们先定义一个接口,之后的这些链表都将实现这些接口,实现这些接口内的方法,主要包含了一些常用的CRUD方法.这个接口我们使用了泛型,之后的链表类也将继承这个接口的泛型,元素的类型就是泛型的类型

public interface SeqList<E> {
    // 在线性表中的最后插入值为element的新元素
    void addLast(E element);

    // 在线性表的前边插入新元素,插入后的元素下标为index
    void addFirst(E element);

    //在索引为index处插入值为element的结点
    void add(int index, E element);

    // 删除当前线性表中索引为index的元素,返回删除的元素值
    E removeByIndex(int index);

    // 删除当前线性表中第一个值为element的元素
    void removeByValue(E element);

    // 删除当前线性表中所有值为element的元素
    void removeAllValue(E element);

    // 将当前线性表中index位置的元素替换为element,返回替换前的元素值
    E set(int index, E element);

    // 得到下标为index结点的值
    E get(int index);

    // 查询链表内是否包含element的结点
    boolean contains(E element);

    // 查询当前线性表中第一个值为element的下标
    int indexOf(E element);

}

2. 不带头结点单向链表的结构

我们这里定义了一个内部类Node,为单向链表的结点类,同时单向链表的属性包含了头结点的引用信息和链表结点个数的定义

public class SingleLinkedList<E> implements SeqList<E> {
    private Node head;  //第一个结点的位置
    private int size;  //链表结点的个数

    private class Node {
        E val;
        Node next;

        public Node(E val) {
            this.val = val;
        }
    }

    @Override
    public void addLast(E element) {

    }

    @Override
    public void addFirst(E element) {

    }

    @Override
    public void add(int index, E element) {

    }

    @Override
    public E removeByIndex(int index) {
        return null;
    }

    @Override
    public void removeByValue(E element) {

    }

    @Override
    public void removeAllValue(E element) {

    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public E get(int index) {
        return null;
    }

    @Override
    public boolean contains(E element) {
        return false;
    }

    @Override
    public int indexOf(E element) {
        return 0;
    }
}

3.链表的添加操作(头插法和尾插法)

1.头插法

    @Override
    public void addFirst(E element) {
        //产生一个值为element的结点
        Node newNode = new Node(element);
        //当前结点的指向头结点
        newNode.next = head;
        //更新head的引用,将其指向新节点
        head = newNode;
        size++;

    }

2.尾插法

    @Override
    public void addLast(E element) {
        Node temp = head;
        if (head == null) {
            head = new Node(element);
            size++;
            return;
        }
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = new Node(element);
        size++;

    }

4. 链表的插入操作

记住:插入和删除的关键是找到插入和删除位置的前驱节点

    @Override
    public void add(int index, E element) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        //头插
        if (index == 0) {
            addFirst(element);
            return;
        }
        //尾插
        if (index == size) {
            addLast(element);
            return;
        }
        Node temp = head;
        //找到插入位置的前驱节点
        for (int i = 0; i < index - 1; ++i) {
            temp = temp.next;
        }
        Node newNode = new Node(element);
        newNode.next = temp.next;
        temp.next = newNode;
        size++;

    }

    //index的检查
    private boolean rangeCheck(int index) {
        //如果插入的位置索引不合理,返回false
        if (index < 0 || index > size) {
            return false;
        }
        return true;
    }

5.链表的删除操作

1.删除指定索引的结点

    @Override
    public E removeByIndex(int index) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        //判断头结点的删除
        if (index == 0) {
            Node node = head;
            head = head.next;
            node.next = null;
            size--;
            return node.val;

        }
        //除头结点的输出,找到要删除位置的前驱节点
        Node temp = head;
        for (int i = 0; i < index - 1; ++i) {
            temp = temp.next;
        }
        Node node = temp.next;
        temp.next = temp.next.next;
        node.next = null;
        size--;
        return node.val;
    }

2.删除指定值的第一个结点

    @Override
    public void removeByValue(E element) {
        if (head == null)
            return;
        //删除的恰好是头结点
        if (head.val.equals(element)) {
            head = head.next;
            size--;
            return;
        }
        //删除的是其他节点
        Node temp = head;
        while (temp.next != null) {
            if (temp.next.val.equals(element)) {
                temp.next = temp.next.next;
                size--;
                return;
            }
            temp = temp.next;
        }
        //没有要删除值的结点
        System.out.println("当前链表中不存在值为:" + element + "的结点");


    }

3.删除指定值的所有结点

    @Override
    public void removeAllValue(E element) {
        if (head == null) {
            return;
        }
        //删除的恰好是头结点
        while (head != null && head.val.equals(element)) {
            head = head.next;
            size--;
        }
        //链表全部删完了
        if (head == null) {
            return;
        }
        //删除的是其他节点
        Node temp = head;
        while (temp.next != null) {
            if (temp.next.val.equals(element)) {
                temp.next = temp.next.next;
                size--;
            } else {
                //只有后继节点不是element的时候才能移动
                temp = temp.next;
            }

        }


    }

6.链表的其他操作

1.将指定索引的结点的值更换为指定值

    @Override
    public E set(int index, E element) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        Node temp = head;
        //找到索引为index的结点
        for (int i = 0; i < index; ++i) {
            temp = temp.next;
        }
        //保存结点的旧值
        E oldValue = temp.val;
        //将索引为index结点的值更换为element
        temp.val = element;
        return oldValue;
    }

2.获得指定索引结点的值

    @Override
    public E get(int index) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        Node temp = head;
        //找到索引为index的结点
        for (int i = 0; i < index; ++i) {
            temp = temp.next;
        }
        return temp.val;
    }

3.判断链表是否包含指定值

    @Override
    public boolean contains(E element) {
        Node temp = head;
        while (temp != null) {
            if (temp.val.equals(element)) {
                return true;
            }
            temp = temp.next;
        }
        return false;
    }

4.获得指定值的第一个索引

    @Override
    public int indexOf(E element) {
        //头结点为空
        if (head == null)
            return -1;
        Node temp = head;
        for (int i = 0; i < size; ++i) {
            if (temp.val.equals(element)) {
                return i;
            }
            temp = temp.next;
        }
        //表示没有找到值为element的结点的索引
        return -1;
    }

5.toString方法

    @Override
    public String toString() {
        Node temp = head;
        StringBuffer sb = new StringBuffer();
        while (temp != null) {
            sb.append(temp.val + "->");
            temp = temp.next;
        }
        sb.append("NULL");
        return sb.toString();
    }

三.带头结点单向链表的非递归实现

1.带头结点单向链表的结构

带头结点的单链表有一个虚拟头结点,头结点里面存储的值并不是链表中真实的值,没有实际意义,只是作为一个头结点的存在,同时size的值不会因为虚拟头结点的存在加一.

public class SingleLinkedListWithHead<E> implements SeqList<E> {

    private class Node {
        E val;
        Node next;

        public Node(E val) {
            this.val = val;
        }
    }

    private Node dummyHead = new Node(null);
    int size = 0;

    @Override
    public void addFirst(E element) {

    }

    @Override
    public void addLast(E element) {

    }

    @Override
    public void add(int index, E element) {

    }

    @Override
    public E removeByIndex(int index) {
        return null;
    }

    @Override
    public void removeByValue(E element) {

    }

    @Override
    public void removeAllValue(E element) {

    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public E get(int index) {
        return null;
    }

    @Override
    public boolean contains(E element) {
        return false;
    }

    @Override
    public int indexOf(E element) {
        return 0;
    }


}

2.链表的添加操作(头插法和尾插法)

1.头插法

    @Override
    public void addFirst(E element) {
        Node node = new Node(element);
        node.next = dummyHead.next;
        dummyHead.next = node;
        size++;

    }

2.尾插法

    @Override
    public void addLast(E element) {
        Node temp = dummyHead;
        for (int i = 0; i < size; ++i) {
            temp = temp.next;
        }
        Node node = new Node(element);
        temp.next = node;
        size++;
        
    }

3. 链表的插入操作

    @Override
    public void add(int index, E element) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        if (index == 0) {
            addFirst(element);
            return;
        }
        if (index == size) {
            addLast(element);
            return;
        }
        Node temp = dummyHead;
        Node node = new Node(element);
        for (int i = 0; i < index; ++i) {
            temp = temp.next;
        }
        node.next = temp.next;
        temp.next = node;
        size++;
    }

4.链表的删除操作

1.删除指定索引的结点

    @Override
    public E removeByIndex(int index) {
        //边界的判断
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("get index illegal!");
        }
        Node temp = dummyHead;
        for (int i = 0; i < index; ++i) {
            temp = temp.next;
        }
        Node node = temp.next;
        temp.next = temp.next.next;
        return node.val;
    }

2.删除指定值的第一个结点

    @Override
    public void removeByValue(E element) {
        Node temp = dummyHead;
        while (temp.next != null) {
            if (temp.next.val.equals(element)) {
                temp.next = temp.next.next;
                size--;
                return;
            }
            temp = temp.next;
        }
        System.out.println("当前链表中不存在值为:" + element + "的结点");

    }

3.删除指定值的所有结点

    @Override
    public void removeAllValue(E element) {
        Node temp = dummyHead;
        while (temp.next != null) {
            if (temp.next.val.equals(element)) {
                temp.next = temp.next.next;
                size--;
            } else {
                temp = temp.next;
            }
        }

    }

5.链表的其他操作

这里不对其他操作进行描述,因为大致方法和不带头结点的单链表的操作一致

四.不带头结单向点链表的递归实现

1.基本介绍

递归方式主要实现的是插入操作和删除操作,它的结构和不带头结点的单链表的结构一样

2. 链表的插入操作

    //在索引值为index处插入val的结点
    public void add(int index, int val) {
        head = addInternal(head, index, val);
    }

    private Node addInternal(Node head, int index, int val) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("不合法");
        }
        //头插
        if (index == 0) {
            Node node = new Node(val);
            node.next = head;
            head = node;
            size++;
            return head;
        }
        //index不在头结点插入
        head.next = addInternal(head.next, index - 1, val);
        return head;

    }

3.递归打印链表结点的值

1.正序打印

    //正序打印
    public void print(Node head) {
        if (head == null) {
            System.out.print("NULL");
            return;
        }
        
        System.out.print(head.val + "->");
        print(head.next);
    }

2.逆序打印

    //逆序打印
    public void printReverse(Node head) {
        if (head == null) {
            return;
        }
        printReverse(head.next);
        if (head == this.head) {
            System.out.print(head.val + "->NULL");
        } else {
            System.out.print(head.val + "->");
        }
    }

4.链表的删除操作

1.删除指定值的所有结点

力扣:力扣

    public ListNode removeElements(ListNode head, int val) {
        if (head == null)
            return null;
        head.next = removeElements(head.next, val);
        return head.val == val ? head.next : head;

    }

2.删除链表中的重复值(保留)

力扣:力扣

    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        head.next = deleteDuplicates(head.next);
        return head.val == head.next.val ? head.next : head;

    }

3.删除链表中的重复值(不保留)

力扣:力扣

解法一:

    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        if (head.val != head.next.val) {
            head.next = deleteDuplicates(head.next);
            return head;
        } else {
            //头结点就是重复的结点
            while (head.next != null && head.val == head.next.val) {
                head = head.next;
            }
            head = deleteDuplicates(head.next);
            return head;
        }

    }

 解法二:

    public ListNode deleteDuplicates(ListNode head) {
        if(head==null||head.next==null){
            return head;
        }
        if(head.val!=head.next.val){
        head.next=deleteDuplicates(head.next);
          return head;
        }else{
            //头结点就是重复的结点
            ListNode cur=head.next;
            while(cur!=null&&cur.val==head.val){
                cur=cur.next;
            }
            return deleteDuplicates(cur);
        }
        
    }

5.链表的其他操作

在这里不做细致的介绍

五.不带头结点双向链表的非递归实现

1.不带头结点双向链表的结构

public class DoubleLinkedList<E> implements SeqList<E> {
    private class DoubleNode {
        E val;
        DoubleNode pre;
        DoubleNode next;

        public DoubleNode(E val) {
            this.val = val;
        }

        public DoubleNode(E val, DoubleNode pre, DoubleNode next) {
            this.val = val;
            this.pre = pre;
            this.next = next;
        }
    }

    private DoubleNode head;
    private DoubleNode tail;
    private int size = 0;


    //尾插法
    @Override
    public void addLast(E element) {
        

    }

    @Override
    public void addFirst(E element) {

    }

    //找到索引为index的位置的结点
    private DoubleNode node(int index) {
        return null;

    }


    @Override
    public void add(int index, E element) {


    }

    @Override
    public E removeByIndex(int index) {
         return null;
    }

    @Override
    public void removeByValue(E element) {

    }

    @Override
    public void removeAllValue(E element) {

    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public E get(int index) {
         return null;
    }

    @Override
    public boolean contains(E element) {
        return false;
    }

    @Override
    public int indexOf(E element) {
        return 0;
    }
}

2.链表的添加操作(头插法和尾插法)

1.头插法

    @Override
    public void addFirst(E element) {
        DoubleNode node = new DoubleNode(element);
        if (tail == null) {
            tail = node;
        } else {
            head.pre = node;
            node.next = head;
        }
        head = node;
        size++;
        
    }

2.尾插法

    //尾插法
    @Override
    public void addLast(E element) {
        DoubleNode node = new DoubleNode(element);
        if (head == null) {
            head = node;
        } else {
            tail.next = node;
            node.pre = tail;
        }
        tail = node;
        size++;

    }

3. 链表的插入操作

    //找到索引为index的位置的结点
    private DoubleNode node(int index) {
        DoubleNode result = null;
        if (index < (size >> 1)) {
            result = head;
            for (int i = 0; i < index; ++i) {
                result = result.next;
            }
        } else {
            result = tail;
            for (int i = size - 1; i > index; --i) {
                result = result.pre;
            }
        }
        return result;


    }

    private boolean rangeCheck(int index) {
        if (index < 0 || index > size) {
            return false;
        }
        return true;

    }


    @Override
    public void add(int index, E element) {
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("illegal index!!!");
        }
        if (index == 0) {
            addFirst(element);
        }
        if (index == size) {
            addLast(element);
        }
        DoubleNode pre = node(index - 1);
        DoubleNode next = pre.next;
        DoubleNode node = new DoubleNode(element, pre, next);
        pre.next = node;
        next.pre = node;
        size++;

    }

4.链表的删除操作

1.删除指定索引的结点

    //在当前链表中删除node结点
    private DoubleNode unlink(DoubleNode node) {
        DoubleNode pre = node.pre;
        DoubleNode next = node.next;
        // 先处理左半区域
        if (pre == null) {
            this.head = next;
        } else {
            node.pre = null;
            pre.next = next;
        }
        // 在处理右半区域
        if (next == null) {
            this.tail = pre;
        } else {
            node.next = null;
            next.pre = pre;
        }
        size--;
        return node;

    }

    @Override
    public E removeByIndex(int index) {
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("illegal index!!");
        }
        DoubleNode node = node(index);
        return unlink(node).val;
    }

2.删除指定值的第一个结点

    @Override
    public void removeByValue(E element) {
        DoubleNode node = head;
        for (int i = 0; i < size; i++) {
            if (node.val.equals(element)) {
                unlink(node);
                return;
            }
            node = node.next;
        }

    }

3.删除指定值的所有结点

    @Override
    public void removeAllValue(E element) {
        DoubleNode node = head;
        // 因为每次unlink之后都会修改size的值,但是删除所有元素,
        // 要把所有链表节点全部遍历一遍
        int length = this.size;
        for (int i = 0; i < length; i++) {
            DoubleNode next = node.next;
            if (node.val.equals(element)) {
                unlink(node);
            }
            node = next;
        }
    }

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

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

相关文章

一文带你领略 WPA3-SAE 的 “安全感”

引入 WPA3-SAE也是针对四次握手的协议。 四次握手是 AP &#xff08;authenticator&#xff09; 和 &#xff08;supplicant&#xff09;进行四次信息交互&#xff0c;生成一个用于加密无线数据的秘钥。 这个过程发生在 WIFI 连接 的 过程。 为了更好的阐述 WPA3-SAE 的作用 …

Thread的小补丁

Thread小补丁线程状态NewRunnableWaitingTimed_waitingBlocked线程安全线程的抢占式执行同时对同一个变量进行修改指令重排序操作不是原子的解决方案万恶之源优化我们自己的代码Synchronized和Volatile上一篇博客中,我们简单介绍了线程Thread的一些知识,一些基本的使用,但是单单…

数据结构和算法(1):数组

目录概述动态数组二维数组局部性原理越界检查概述 定义 在计算机科学中&#xff0c;数组是由一组元素&#xff08;值或变量&#xff09;组成的数据结构&#xff0c;每个元素有至少一个索引或键来标识 In computer science, an array is a data structure consisting of a col…

文心一言发布,你怎么看?chatGPT

百度全新一代知识增强大语言模型“文心一言”于2021年3月16日正式发布&#xff0c;作为一款自然语言处理技术&#xff0c;它引起了广泛的关注和讨论。 首先&#xff0c;文心一言是一款具有重大意义的自然语言处理技术。在人工智能领域&#xff0c;自然语言处理技术一直是一个难…

PyTorch 之 神经网络 Mnist 分类任务

文章目录一、Mnist 分类任务简介二、Mnist 数据集的读取三、 Mnist 分类任务实现1. 标签和简单网络架构2. 具体代码实现四、使用 TensorDataset 和 DataLoader 简化本文参加新星计划人工智能(Pytorch)赛道&#xff1a;https://bbs.csdn.net/topics/613989052 一、Mnist 分类任…

Lambda表达式

第一章 Java为什么引入 Lmabda表达式目的尽可能轻量级的将代码封装为数据1.1 什么是Lambda表达式Lambda表达式也被成为箭头函数、匿名函数、闭包 Lambda表达式体现的是轻量级函数式编程思想 ‘->’符号是Lambda表达式的核心符号&#xff0c;符号左侧是操作参数&#xff0c;符…

YOLOv8 多目标跟踪

文章大纲 简介环境搭建代码样例跟踪原理代码分析原始老版实现新版本封装代码实现追踪与计数奇奇怪怪错误汇总lap 安装过程报错推理过程报错参考文献与学习路径简介 使用yolov8 做多目标跟踪 文档地址: https://docs.ultralytics.com/modes/track/https://github.com/ultralyt…

【多线程】多线程案例

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;we can not judge the value of a moment until it becomes a memory. 目 录&#x1f35d;一. 单例模式&#x1f364;1. 饿汉模式实现&#x1f9aa;2. 懒汉模…

java如何创建线程

java如何创建线程1. java如何创建线程1.1 通过继承Thread类来创建线程1.2 通过实现Runnable接口来创建线程1.3 通过匿名内部类来创建线程1.4 lambda表达式1.5 通过实现Runnable接口的方式创建线程目标类的优缺点1. java如何创建线程 一个线程在Java中使用一个Thread实例来描述…

android8 rk3399 同时支持多个USB摄像头

文章目录一、前文二、CameraHal_Module.h三、CameraHal_Module.cpp四、编译&烧录Image五、App验证一、前文 Android系统默认支持2个摄像头&#xff0c;一个前置摄像头&#xff0c;一个后置摄像头需要支持数量更多的摄像头&#xff0c;得修改Android Hal层的代码 二、Camer…

VueX快速入门(适合后端,无脑入门!!!)

文章目录前言State和Mutations基础简化gettersMutationsActions&#xff08;异步&#xff09;Module总结前言 作为一个没啥前端基础&#xff08;就是那种跳过js直接学vue的那种。。。&#xff09;的后端选手。按照自己的思路总结了一下对VueX的理解。大佬勿喷qAq。 首先我们需要…

我的 System Verilog 学习记录(11)

引言 本文简单介绍 SystemVerilog 的其他程序结构。 前文链接&#xff1a; 我的 System Verilog 学习记录&#xff08;1&#xff09; 我的 System Verilog 学习记录&#xff08;2&#xff09; 我的 System Verilog 学习记录&#xff08;3&#xff09; 我的 System Verilo…

Linux lvm管理讲解及命令

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

软件行业的最后十年【ChatGPT】

在这篇文章中&#xff0c;我将说明像 ChatGPT 这样的生成式人工智能 (GAI) 将如何在十年内取代软件工程师。 预测被离散化为 5 个阶段&#xff0c;总体轨迹趋向于完全接管。 但首先&#xff0c;一个简短的前言。 推荐&#xff1a;用 NSDT场景设计器 快速搭建3D场景。 1、关于AI…

二叉搜索树:AVL平衡

文章目录一、 二叉搜索树1.1 概念1.2 操作1.3 代码实现二、二叉搜索树的应用K模型和KV模型三、二叉搜索树的性能分析四、AVL树4.1 AVL树的概念4.2 AVL树的实现原理4.3 旋转4.4 AVL树最终代码一、 二叉搜索树 1.1 概念 二叉搜索树&#xff08; Binary Search Tree&#xff0c;…

LeetCode刷题记录---数位DP算法

😄 学会数位dp算法,可以连杀好几道力扣困难题,加油~ 🚀题目: 难度题目困难2376. 统计特殊整数困难1012. 至少有 1 位重复的数字困难233. 数字 1 的个数困难面试题 17.06. 2出现的次数🚀学习资料: 数位dp算法,我是跟着灵神学的,感谢灵神!数位 dp 通用模板参考灵神…

Python数据分析案例24——基于深度学习的锂电池寿命预测

本期开始案例较为硬核起来了&#xff0c;适合理工科的硕士&#xff0c;人文社科的同学可以看前面的案例。 案例背景 这篇文章是去年就发了&#xff0c;刊物也印刷了&#xff0c;现在分享一部分代码作为案例给需要的同学。 原文链接&#xff08;知网文章 C核&#xff09;&…

python如何快速采集美~女视频?无反爬

人生苦短 我用python~ 这次康康能给大家整点好看的不~ 环境使用: Python 3.8 Pycharm mou歌浏览器 mou歌驱动 —> 驱动版本要和浏览器版本最相近 <大版本一样, 小版本最相近> 模块使用: requests >>> pip install requests selenium >>> pip …

不是,到底有多少种图片懒加载方式?

一、也是我最开始了解到的 js方法&#xff0c;利用滚动事件&#xff0c;判断当时的图片位置是否在可视框内&#xff0c;然后进行渲染。 弊端&#xff1a;代码冗杂&#xff0c;你还要去监听页面的滚动事件&#xff0c;这本身就是一个不建议监听的事件&#xff0c;即便是我们做了…

【selenium学习】数据驱动测试

数据驱动在 unittest 中&#xff0c;使用读取数据文件来实现参数化可以吗&#xff1f;当然可以。这里以读取 CSV文件为例。创建一个 baidu_data.csv 文件&#xff0c;如图所示&#xff1a;文件第一列为测试用例名称&#xff0c;第二例为搜索的关键字。接下来创建 test_baidu_da…