【数据结构】Java实现单链表

目录

1. ArrayList的缺陷

2. 链表

2.1 链表的概念及结构

2.2 接口的实现

3. 动手实现单链表

3.1 重写SeqList接口方法

3.2 在当前链表头部添加节点(头插)

3.3  在 第index位置添加节点(任意位置)

3.4 在当前链表尾部添加节点(尾插)

3.5  删除第index个节点

3.6 检验index是否合法

3.7 删除第一个值element的节点

3.8 删除所有值element的节点

3.9 修改第index个节点的值为element

3.10 获取第index个节点的值

3.11 判断链表中是否存在element

3.12  获取element在链表中的位置

3.13 打印链表

4. SingleLinkedList整体实现

4.1 SingleLinkedList类

4.2 Test类

4.3 测试结果


1. ArrayList的缺陷

由于其底层是一段连续空间,当 ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多的场景 。因此: java集合中又引入了LinkedList ,即链表结构。

2. 链表

2.1 链表的概念及结构

链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的 引用链接 次序实现的 。
实际中链表的结构非常多样,以下情况组合起来就有6 种链表结构:
(1)  单向或者双向

(2) 带头或者不带头

(3)循环或者非循环

虽然有这么多的链表的结构,但是我们重点掌握两种 :
无头单向非循环链表 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如哈希桶、图的邻接表等等。
无头双向链表 :在 Java 的集合框架库中 LinkedList 底层实现就是无头双向循环链表。

2.2 接口的实现

使用泛型更好的接收不同数据类型(一个顺序表只能接受一种类型!)

public interface SeqList<E> {
    //    尾插
    void add(E element);
    //    将 element 插入到 index 位置
    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);
    E get(int index);
    //    判断 o 是否在其中
    boolean contains(E element);
    int indexOf(E element);
    int size();
    void clear();
 
}

3. 动手实现单链表

实现没有头节点的单链表

3.1 重写SeqList接口方法

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

    private Node head;//头节点

    private int size; // 节点个数,保存的元素个数

    //类的定义,内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }
    public void addFrist(E val){ }
    @Override
    public void add(E element) { }
    @Override
    public void add(int index, E element) { }
    @Override
    public E removeByIndex(int index) { }
    @Override
    public void removeByValue(E element) { }
    @Override
    public void removeAllValue(E element) { }
    @Override
    public E set(int index, E element) { }
    public boolean rangeCheck(int index){ }
    @Override
    public E get(int index) { }
    @Override
    public boolean contains(E element) { }
    @Override
    public int indexOf(E element) { }
    @Override
    public int size() { }
    @Override
    public void clear() { }
    @Override
    public String toString() { }
}

3.2 在当前链表头部添加节点(头插)

无论链表中是否包含节点,头插法都是这相同的代码处理~

public void addFrist(E val){
        // 要插入一个元素,首先要产生一个新节点
        Node node = new Node(val);
        // 将当前节点插入到链表中
        node.next = head;
        // 更新head的引用,指向当前的新节点
        head = node;
        size ++;
    }

    public void add(E element) {
        add(size,element);
    }

思考一下,①和②的代码顺序能换吗?

先执行2再执行1,原来的链表就丢了,head指向新节点,最终链表中只剩下新节点了~

3.3  在 第index位置添加节点(任意位置)

(1)判断index是否合法

(2)判断没有前驱,没有就是头插

(3)寻找index位置的前驱节点(单链表的核心操作就是在寻找前驱节点!)

(4)先将新节点和原先位置节点连接

(5)再更改原先prev的引用

public void add(int index, E element) {
        // 1.base case,边界判断
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index illegal!");
        }
        // 2.判断没有前驱的情况
        if (index == 0) {
            addFrist(element);
            return;
        }
        // 3.确实是中间位置的插入,寻找待插入位置的前驱!
        Node prev = head;
        for (int i = 0; i < index - 1; i++) {
            prev = prev.next;
        }
        // prev走到待插入位置的前驱
        Node node = new Node(element);
        node.next = prev.next;
        prev.next = node;
        size ++;
    }

3.4 在当前链表尾部添加节点(尾插)

第一种方法

(1)走到当前链表的最后

(2)让最后节点的next指向这个新添加的节点

第二种方法

调用add()方法,index等于size就是在链表末尾插入了

    public void addLast(E val){
        Node prev = head;
        while (prev.next != null){
            prev = prev.next;
        }
        Node node = new Node(val);
        prev.next = node;
    }


    //尾插2
    @Override
    public void add(E element) {
        add(size,element);
    }

3.5  删除第index个节点

(1)判断index是否合法(链表为空则index不合法)

(2)判断没有前驱,没有就是头节点删除

(3)寻找index位置的前驱节点

(4)将前驱节点和原先位置的下一个节点连接

public E removeByIndex(int index) {
        // 1.base case
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("remove index illegal!");
        }
        // 2.判断头节点的删除问题
        if (index == 0) {
            Node node = head;
            head = head.next;
            node.next = null;
            size --;
            return node.val;
        }
        // 3.现在确实是中间位置的删除
        Node prev = head;
        for (int i = 0; i < index - 1; i++) {
            prev = prev.next;
        }
        Node node = prev.next;
        prev.next = node.next;
        node.next = null;
        size --;
        return node.val;
    }

3.6 检验index是否合法

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

3.7 删除第一个值element的节点

(1)判断链表是否为空

(2)判断头节点是否是待删除的节点,是的话删除完就方法返回

(3)寻找element节点的前驱节点(凡是用到引用的位置,一定要保证这个引用不为空)

(4)将前驱节点和原先位置的下一个节点连接

这里的图和3.5 中的图一致

 public void removeByValue(E element) {
        // 1.base case
        if (head == null) {
            return;
        }
        // 2.判断头节点恰好是待删除的节点
        if (head.val.equals(element)) {
            head = head.next;
            size --;
            return;
        }
        // 3.此时头节点不为空其一定不是待删除的结点
        Node prev = head;
        while (prev.next != null) {
            if (prev.next.val.equals(element)) {
                // prev走到了待删除节点的前驱位置
                prev.next = prev.next.next;
                size --;
                return;
            }
            prev = prev.next;
        }
        // 4.链表中没有待删除的节点
        System.out.println("当前链表中不存在值为" + element + "的节点");
    }

3.8 删除所有值element的节点

(1)判断链表是否为空

(2)判断头节点是否是待删除的节点

(3)判断链表是否已经删除完,删除完就返回

(4)寻找element节点的前驱节点(凡是用到引用的位置,一定要保证这个引用不为空)

(5)将前驱节点和原先位置的下一个节点连接

(6)如果下一个节点不是待删除的节点,prev指向下一个节点

(7)循环执行步骤(5)、(6)直到链表走完(prev.next != null)

 

 

public void removeAllValue(E element) {
        // 1.base case
        if(head == null) {
            return;
        }
        // 2.若头节点就是待删除的节点且出现连续的待删除节点
        while (head != null && head.val.equals(element)) {
            head = head.next;
            size --;
        }
        if (head == null) {
            // 整个链表已经删除完了
            return;
        }
        // 3.头节点一定不是待删除的节点且链表不为空!
        // prev一定指向的不是待删除的结点~
        Node prev = head;
        while (prev.next != null) {
            if (prev.next.val.equals(element)) {
                // 此时prev就是待删除节点的前驱
                prev.next = prev.next.next;
                size --;
            }else {
                // 只有后继节点不是待删除的结点才能移动prev引用!
                prev = prev.next;
            }
        }
    }

3.9 修改第index个节点的值为element

(1)判断index是否合法

(2)寻找index位置的节点

(3)将节点的val修改为element

public E set(int index, E element) {
//        1.索引的合法性校验
        if(!rangeCheck(index)){
            throw new IllegalArgumentException("set index illegal!");
        }
        // 从前向后遍历走到index对应的元素
        Node x = head;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        // 此时x就落在了待修改的节点位置
        E oldVal = x.val;
        x.val = element;
        return oldVal;
    }
    public boolean rangeCheck(int index){
        if (index < 0 || index >= size) {
            return false;
        }
        return true;
    }

3.10 获取第index个节点的值

public E get(int index) {
        if(!rangeCheck(index)){
            throw new IllegalArgumentException("get index illegal!");
        }
        Node x = head;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x.val;
    }

3.11 判断链表中是否存在element

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

3.12  获取element在链表中的位置

public int indexOf(E element) {
        int count = 0;
        Node cur = head;
        while (cur.next != null){
            if (cur.val.equals(element)){
                return count;
            }
            count++;
            cur = cur.next;
        }
        return -1;
    }

3.13 打印链表

public String toString() {
        StringBuilder sb = new StringBuilder();
        // 从当前链表的第一个节点开始向后遍历,直到走到尾结点为止
        // 第一个节点head
        for(Node x = head;x != null;x = x.next){
            sb.append(x.val);
            sb.append("->");
            if(x.next == null){
                // 此时temp走到了尾结点
                sb.append("NULL");
            }
        }
        return sb.toString();
    }

3.14  获取链表长度以及清空链表

    public int size() {
        return this.size;
    }

    @Override
    public void clear() {
        Node cur = this.head;
        Node curNext = null;
        while (cur != null) {
            curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

4. SingleLinkedList整体实现

4.1 SingleLinkedList类

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

    private Node head;//头节点

    private int size; // 车厢节点个数,保存的元素个数

    //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }
    //  ------------------------------------------------

//    头插,在当前链表头部添加元素  head在移动
    public void addFrist(E val){
        // 要插入一个元素,首先要产生一个新节点
        Node node = new Node(val);
        // 将当前节点插入到链表中
        node.next = head;
        // 更新head的引用,指向当前的新节点
        head = node;
        size ++;
    }


    @Override
    public void add(E element) {
        add(size,element);
    }

    @Override
    public void add(int index, E element) {
        // 1.base case,边界判断
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index illegal!");
        }
        // 2.判断没有前驱的情况
        if (index == 0) {
            addFrist(element);
            return;
        }
        // 3.确实是中间位置的插入,寻找待插入位置的前驱!
        Node prev = head;
        for (int i = 0; i < index - 1; i++) {
            prev = prev.next;
        }
        // prev走到待插入位置的前驱
        Node node = new Node(element);
        node.next = prev.next;
        prev.next = node;
        size ++;
    }

    @Override
    public E removeByIndex(int index) {
        // 1.base case
        if (!rangeCheck(index)) {
            throw new IllegalArgumentException("remove index illegal!");
        }
        // 2.判断头节点的删除问题
        if (index == 0) {
            Node node = head;
            head = head.next;
            node.next = null;
            size --;
            return node.val;
        }
        // 3.现在确实是中间位置的删除
        Node prev = head;
        for (int i = 0; i < index - 1; i++) {
            prev = prev.next;
        }
        Node node = prev.next;
        prev.next = node.next;
        node.next = null;
        size --;
        return node.val;
    }

    @Override
    public void removeByValue(E element) {
        // 1.base case
        if (head == null) {
            return;
        }
        // 2.判断头节点恰好是待删除的节点
        if (head.val.equals(element)) {
            head = head.next;
            size --;
            return;
        }
        // 3.此时头节点不为空其一定不是待删除的结点
        Node prev = head;
        while (prev.next != null) {
            if (prev.next.val.equals(element)) {
                // prev走到了待删除节点的前驱位置
                prev.next = prev.next.next;
                size --;
                return;
            }
            prev = prev.next;
        }
        // 4.链表中没有待删除的节点
        System.out.println("当前链表中不存在值为" + element + "的节点");
    }

    @Override
    public void removeAllValue(E element) {
        // 1.base case
        if(head == null) {
            return;
        }
        // 2.若头节点就是待删除的节点且出现连续的待删除节点
        while (head != null && head.val.equals(element)) {
            head = head.next;
            size --;
        }
        if (head == null) {
            // 整个链表已经删除完了
            return;
        }
        // 3.头节点一定不是待删除的节点且链表不为空!
        // prev一定指向的不是待删除的结点~
        Node prev = head;
        while (prev.next != null) {
            if (prev.next.val.equals(element)) {
                // 此时prev就是待删除节点的前驱
                prev.next = prev.next.next;
                size --;
            }else {
                // 只有后继节点不是待删除的结点才能移动prev引用!
                prev = prev.next;
            }
        }
    }

//    修改
    @Override
    public E set(int index, E element) {
//        1.索引的合法性校验
        if(!rangeCheck(index)){
            throw new IllegalArgumentException("set index illegal!");
        }
        // 从前向后遍历走到index对应的元素
        Node x = head;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        // 此时x就落在了待修改的节点位置
        E oldVal = x.val;
        x.val = element;
        return oldVal;
    }
    public boolean rangeCheck(int index){
        if (index < 0 || index >= size) {
            return false;
        }
        return true;
    }

    @Override
    public E get(int index) {
        if(!rangeCheck(index)){
            throw new IllegalArgumentException("get index illegal!");
        }
        Node x = head;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x.val;
    }

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

    @Override
    public int indexOf(E element) {
        int count = 0;
        Node cur = head;
        while (cur.next != null){
            if (cur.val.equals(element)){
                return count;
            }
            count++;
            cur = cur.next;
        }
        return -1;

    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public void clear() {
        Node cur = this.head;
        Node curNext = null;
        while (cur != null) {
            curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        // 从当前链表的第一个节点开始向后遍历,直到走到尾结点为止
        // 第一个节点head
        for(Node x = head;x != null;x = x.next){
            sb.append(x.val);
            sb.append("->");
            if(x.next == null){
                // 此时temp走到了尾结点
                sb.append("NULL");
            }
        }
        return sb.toString();
    }
}

4.2 Test类

public class SingleLinkedTest {
    public static void main(String[] args) {
        SingleLinkedList<Integer> list = new SingleLinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(9);
        list.add(10);
        list.add(10);
        list.add(6);
        list.add(10);
        list.add(5);
        list.add(7);
        list.add(10);
        list.add(10);

        System.out.println(list);
        System.out.println("------------添加测试-----------");
        System.out.println("从链表尾部添加99");
        list.add(99);
        System.out.println("添加index为2,element为88");
        list.add(2,88);
        System.out.println(list);
        System.out.println("-----------删除测试------------");
        System.out.println("删除index为0");
        list.removeByIndex(0);
        System.out.println("删除元素为6");
        list.removeByValue(6);
        System.out.println("删除所有10");
        list.removeAllValue(10);
        System.out.println(list);
        System.out.println("-----------其他------------");
        System.out.println("查看是否包含10这个元素");
        System.out.println(list.contains(10));
        System.out.println("修改index为3,element为19");
        list.set(3,19);
        System.out.println("获取index为3的元素");
        System.out.println(list.get(3));
        System.out.println(list);
        System.out.println("获取element为88的index");
        System.out.println(list.indexOf(88));
        System.out.println("获取顺序表长度");
        System.out.println(list.size());
        System.out.println("清空顺序表");
        list.clear();
        System.out.println(list + "...");
    }
}

4.3 测试结果

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

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

相关文章

Python|蓝桥杯进阶第四卷——图论

欢迎交流学习~~ 专栏&#xff1a; 蓝桥杯Python组刷题日寄 蓝桥杯进阶系列&#xff1a; &#x1f3c6; Python | 蓝桥杯进阶第一卷——字符串 &#x1f50e; Python | 蓝桥杯进阶第二卷——贪心 &#x1f49d; Python | 蓝桥杯进阶第三卷——动态规划 ✈️ Python | 蓝桥杯进阶…

四、快速上手 ODM 操作 Mongodb

文章目录一、ODM 的选择和安装二、MongoEngine 模型介绍三、文档的嵌套模型四、使用 ODM 查询数据4.1 查询一个文档4.2 条件查询4.3 统计、排序和分页五、使用 ODM 新增数据六、使用 ODM 修改和删除数据一、ODM 的选择和安装 MongoEngine&#xff1a; 使用最为广泛的 ODM。htt…

【C++】命名空间

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、命名空间产生的背景二、命名空…

基础篇:07-Nacos注册中心

1.Nacos安装部署 1.1 下载安装 nacos官网提供了安装部署教程&#xff0c;其下载链接指向github官网&#xff0c;选择合适版本即可。如访问受阻可直接使用以下最新稳定版压缩包&#xff1a;&#x1f4ce;nacos-server-2.1.0.zip&#xff0c;后续我们也可能会更改为其他版本做更…

图论学习(五)

极图 l部图的概念与特征 定义&#xff1a;若简单图G的点集V有一个划分&#xff1a; 且所有的Vi非空&#xff0c;Vi内的点均不邻接&#xff0c;设G是一个l部图。 如果l2&#xff0c;则G就是偶图。n阶无环图必是n部图。若l1<l2≤n&#xff0c;则任意的l1部图也是l2部图。…

【毕业设计】基于SpringBoot+Vue论坛管理系统【源码(完整源码请私聊)+论文+演示视频+包运行成功】

您好&#xff0c;我是码农飞哥&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&…

JavaScript学习笔记(7.0)

<!--* Author: RealRoad1083425287qq.com* Date: 2023-03-13 14:50:18* LastEditors: Mei* LastEditTime: 2023-03-13 15:08:54* FilePath: \vscode\鼠标跟随.html* Description: * * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. --> <!DOCTYPE …

Vue3(递归组件) + 原生Table 实现树结构复杂表格

一、递归组件 什么是递归&#xff0c;Javascript中经常能接触到递归函数。也就是函数自己调用自己。那对于组件来说也是一样的逻辑。平时工作中见得最多应该就是菜单组件&#xff0c;大部分系统里面的都是递归组件。文章中我做了按需引入的配置&#xff0c;所以看不到我引用组…

什么是让ChatGPT爆火的大语言模型(LLM)

什么是让ChatGPT爆火的大语言模型(LLM) 更多精彩内容: https://www.nvidia.cn/gtc-global/?ncidref-dev-876561 文章目录什么是让ChatGPT爆火的大语言模型(LLM)大型语言模型有什么用&#xff1f;大型语言模型如何工作&#xff1f;大型语言模型的热门应用在哪里可以找到大型语言…

西安石油大学C语言期末真题实战

很简单的一道程序阅读题&#xff0c;pa’默认为a【0】&#xff0c;接下来会进行3次循环 0 1 2 输出结果即可 前3题就是一些基础定义&#xff0c;在此不多赘述 要注意不同的数据类型的字节数不同 a<<2 b>>1&#xff08;b>>1;就是说b自身右位移一位&#xff08…

支付系统设计:消息重试组件封装

文章目录前言一、重试场景分析一、如何实现重试1. 扫表2. 基于中间件自身特性3. 基于框架4. 根据公司业务特性自己实现的重试二、重试组件封装1. 需求分析2. 模块设计2.1 持久化模块1. 表定义2. 持久化接口定义3. 持久化配置类2.2 重试模块1.启动2.重试3. 业务端使用1. 引入依赖…

Linux基础(3) Vim编辑器与Shell命令脚本

1、VIM文本编辑器 VIM编辑器的三大模式 命令模式&#xff1a; 控制光标移动&#xff0c;可对文本进行复制、粘贴和查找等工作输入模式&#xff1a; 正常的文本录入。末行模式&#xff1a; 保存或退出文档&#xff0c;以及设置编辑环境三种模式的切换&#xff1a; ​注意&…

app自动化测试——Android studio安装与配置

文章目录一、Appium框架介绍二、Appium 生态工具三、环境安装四、安装Android studio五、配置环境变量六、创建模拟器查看设备启动模拟器一、Appium框架介绍 1、跨语言&#xff1a;java、python等 2、跨平台&#xff1a;Android、IOS、Windows、Mac 3、底层多引擎切换 4、生态…

(待补充)小蒟蒻的刷题成长之路-------2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)

小蒟蒻的刷题成长之路 蓝桥杯的比赛流程和必考点_蓝桥杯省赛考点_时雨h的博客-CSDN博客 大一学生一周十万字爆肝版C语言总结笔记_大一c语言笔记_时雨h的博客-CSDN博客 程序设计与 C 语言期末复习_时雨h的博客-CSDN博客 P8597 [蓝桥杯 2013 省 B] 翻硬币个人思考总结第五届传智杯…

西瓜视频登录页面

题目 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>登录页面</title><style>td{width: 160px;height: 25px;}img{width: 20px;height: 20px;}.number, .password{background: rgba(0,0,0,.05);}.numbe…

指针进阶(上)

内容小复习&#x1f431;&#xff1a; 字符指针:存放字符的数组 char arr1[10]; 整型数组:存放整型的数组 int arr2[5]; 指针数组:存放的是指针的数组 存放字符指针的数组(字符指针数组) char* arr3[5]; 存放整型指针的数组(整型指针数组) int* arr[6]; 下面进入学习了哦~&…

【二分查找】

二分查找704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置结语704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在…

mybatis中获取参数的两种方式:${}和#{}

目录 1.#{} 2.${} 3.总结 1.#{} 本质是占位符赋值 示例及执行结果&#xff1a; 结论&#xff1a;通过执行结果可以看到&#xff0c;首先对sql进行了预编译处理&#xff0c;然后再传入参数&#xff0c;有效的避免了sql注入的问题&#xff0c;并且传参方式也比较简单&#xf…

Python制作9行最简单音乐播放器?不,我不满足

人生苦短 我用python 好久不见啦~这次就来给大家整个大福利 ~ 源码资料电子书:点击此处跳转文末名片获取 最简单的9行代码音乐播放器如下&#xff1a; import time import pygamefile r歌曲路径 pygame.mixer.init() print(正在播放,file) track pygame.mixer.music.load(f…

计算机面试常见问答题目

英语口语 自我介绍 Hello, teachers. My name is Wang Xu. I come from Ningxia. I graduated from the School of Computer Science, Xi an Jiaotong University, majoring in Internet of Things. Next, I will introduce myself from four aspects. First of all, I studi…
最新文章