数据结构和算法-单链表

数据结构和算法-单链表

1. 链表介绍

  链表是有序的列表,但是它在内存中是存储如下

在这里插入图片描述

图1 单链表示意图

小结:

  1. 链表是以节点的方式存储
  2. 每个节点包含data域,next域,指向下一个节点。
  3. 如图:发现链表的各个节点不一定是连续存储。比如地址为150的节点下一个节点的地址不为160,而是指向地址为110的节点。
  4. 链表分带头节点的链表没有头节点的链表,根据实际的需求来确定。

单链表(带头结点)逻辑结构示意图如下

在这里插入图片描述

图2 带头结点的单链表

注意:图2中逻辑结构的连接并不是按照地址的顺序画的,而是按照每个节点的next域的内存地址连接的。真实的内存存储世图1所示

2. 单链表的应用实例

使用带head头的单向链表实现-水浒英雄排行榜管理

  1. 完成对英雄人物的增删改查操作,注:删除和修改,查找。
  2. 第一种方法在添加英雄时,直接添加到链表尾部
  3. 第二种方法在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

思路:使用一个类来代表一个节点 其中类中的属性来表示节点中的内容

  • 添加(创建)
    • 先创建一个head节点,作用就是表示单链表的头
    • 后面每添加一个节点,就直接加入到链表的最后
  • 遍历
    • 通过一个辅助变量遍历,帮助遍历整个链表
class HeroNode{
    int no;
    String name;
    String nickName;
    HeroNode next;
}

在这里插入图片描述

图3 单链表的创建示意图

2.1 直接将节点添加到链表尾部

实现添加节点时,直接添加到链表尾部

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        //进行测试
        //先创建节点
        HeroNode heroNode1 = new HeroNode(1, "松江", "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");

        //创建一个列表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        //加入
        singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.add(heroNode4);
        //显示
        singleLinkedList.list();
    }
}

//定义SingleLinkedList管理节点
class SingleLinkedList {
    //先初始化一个头节点 头节点不要动 不存放具体的数据
    private HeroNode head = new HeroNode(0, " ", " ");

    //添加节点到单向链表
    //思路,当不考虑编号顺序时
    //1. 找到当前链表的最后节点
    //2. 将最后这个节点的next 指向 新的节点
    public void add(HeroNode heroNode) {
        //因为head节点不能动 因此需要一个辅助变量temp
        HeroNode temp = head;
        //遍历链表 找到最后
        while (true) {
            //链表的最后一个节点的next为null
            if (temp.next == null) {//说明此时temp就指向了链表的最后
                temp.next = heroNode;
                break;
            } else {
                temp = temp.next;//将此节点的下一个节点作为新循环的判断(后移一个节点判断)
            }
        }
    }

    //显示链表[遍历]
    public void list() {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为head节点不能动 因此需要一个辅助变量temp来遍历
        HeroNode temp = head.next;
        while (true) {
            //输出节点信息
            System.out.println(temp);
            //判断是否到链表最后
            if (temp.next == null) {//说明这个节点是最后一个节点
                break;
            }
            temp = temp.next;//后移一个节点
        }
    }
}

//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode next;   //指向下一个节点
    //构造器

    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                ", next=" + (next == null ? null : next.hashCode()) +
                '}';
    }
}

2.2 按顺序添加节点

实现添加节点时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

在这里插入图片描述

图4 按顺序添加节点示意图

需要按照编号的顺序添加节点 思路:

  1. 首先找到新添加的节点的位置,是通过辅助变量(指针),通过遍历来搞定
  2. 新的节点.next = temp.next
  3. 将 temp.next = 新的节点

自己写代码如下,重写了一下类

//定义SingleLinkedList2按顺序管理节点
class SingleLinkedList2 {
    //先初始化一个头节点 头节点不要动 不存放具体的数据
    private HeroNode head = new HeroNode(0, " ", " ");

    //按顺序添加节点到单向链表
    public void add(HeroNode heroNode) {
        if (head.next == null) {//说明此时空链表
            head.next = heroNode;//直接将此节点添加到头节点的后面
            return;
        }
        HeroNode temp = head;
        do {
            if (heroNode.no < temp.next.no) {//将此节点插入到temp和temp.next中间
//                HeroNode temp2 = temp.next;//保存temp的下一个节点
//                temp.next = heroNode;     //将temp下一行节点指向heroNode
//                heroNode.next = temp2;    //将heroNode的下一个节点指向到原temp.next
                heroNode.next = temp.next;  //将heroNode的下一个节点指向到temp.next
                temp.next = heroNode;       //将temp下一行节点指向heroNode
                return;  //直接结束战斗
            } else if (heroNode.no == temp.next.no) {
                System.out.println("已经当有相同节点了 添加失败 请自重");
                return;
            }
            temp = temp.next; //节点后移
        } while (temp.next != null);
        //如果正常退出 说明此对象的序号值最大 直接放在最后面
        temp.next = heroNode;
    }

    //显示链表[遍历]
    public void list() {
        if (head.next == null) {
            System.out.println("别闹宝 空链表~~");
            return;
        }
        HeroNode temp = head;
        while (temp.next != null) {//如果下一个节点不为空(存了数据)
            System.out.println(temp.next);  //输出下一个节点
            temp = temp.next;   //后移到下一个节点
        }
    }
}

韩老师教的如下 在SingleLinkedList类中新添加了一个按顺序排列的函数

//第二种方式在添加英雄时 根据排名将英雄插入到指定位置
//(如果有这个排名 则添加失败 并给出提示)
public void addByOrder(HeroNode heroNode) {
    //因为头节点不能动 因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
    //因为是单链表 找的temp是位于添加位置的前一个节点 否则插入不了
    HeroNode temp = head;
    boolean flag = false;   //标志添加的编号是否存在 默认为false
    while (true) {
        if (temp.next == null) {//说明temp已经在链表的最后
            break;
        }
        if (temp.next.no > heroNode.no) { //位置找到 就在temp的后面插入
            break;
        } else if (temp.next.no == heroNode.no) {  //说明希望添加的heroNode的编号已经存在
            flag = true;    //说明编号存在
            break;
        }
        temp = temp.next;   //后移一位 相当于遍历链表
    }
    //判断flag的值
    if(flag){   //不能添加 说明编号存在
        System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
    } else {//因为不管是temp.next == null还是temp.next.no > heroNode.no 都需要把heroNode放在temp的后面
        //加入到链表中 temp的后面
        heroNode.next = temp.next;
        temp.next = heroNode;
    }
}

2.3 单链表节点的修改(根据no编号来修改)

自己写的如下 在类中增加了一个修改函数

//修改节点的信息 根据no编号来修改 即no编号不能改
public void modify(int no, String name,String nickname) {
    if (head.next == null) {//说明此时空链表
        System.out.println("空链表 修改失败 改不了");
        return;
    }

    HeroNode temp = head;
    while (temp.next != null){  //遍历
        if(no == temp.next.no){ //如果编号相同
            temp.next.name = name;
            temp.next.nickname = nickname;
            return;//结束函数
        }
        temp = temp.next;
    }
    //如果正常退出循环 则说明没找到对应的编号
    System.out.println("不好意思,没找到对应编号的位置");
}

韩老师写的如下

//修改节点的信息,根据no编号来修改 即no编号不能改
//说明
//1. 根据newHeroNode 的 no 来修改即可
public void update(HeroNode newHeroNode){
    //判断是否为空
    if(head.next == null){
        System.out.println("链表为空");
        return;
    }
    //找到需要修改的节点 根据no编号
    //定义一个辅助变量
    HeroNode temp = head.next;
    boolean flag = false;//表示是否找到该节点
    while (true){
        if(temp == null){
            break;//已经遍历完链表
        }
        if(temp.no == newHeroNode.no){
            //找到
            flag = true;
            break;
        }
        temp = temp.next;
    }
    //根据flag 判断是否找到要修改的节点
    if(flag){
        temp.name = newHeroNode.name;
        temp.nickname = newHeroNode.nickname;
    }else {//没有找到
        System.out.println("修改失败 没有找到");
    }
}

2.4 单链表节点的删除

自己写的代码如下

//节点的删除 根据no编号来删除
public void delete(int no){
    if (head.next == null) {//说明此时空链表
        System.out.println("空链表 删除失败 请自重宝~~");
        return;
    }
    HeroNode temp = head;
    while (temp.next != null){
        if(temp.next.no == no){
            temp.next = temp.next.next; //将temp指向后移两位
            return;//结束函数
        }
        temp = temp.next;//后移一位
    }
    //正常退出while 则说明没找到对应的编号
    System.out.println("没找到对应的编号");
}

韩老师思路如下

  1. 从单链表中删除一个节点的思路
  2. temp.next = temp.next.next
  3. 被删除的节点 将不会有其它引用 会被垃圾回收机制回收

在这里插入图片描述

图5 单链表删除示意图

代码如下

//删除节点
//思路
//1. head 不能动 因此我们需要一个temp辅助找到带删除节点的前一个节点
//2. 说明在比较时 是temp.next.no 和 需要删除的节点的no比较
public void del(int no){
    HeroNode temp = head;
    boolean flag = false;   //标志是否找到待删除节点的
    while (true){
        if(temp.next == null){  //已经到链表的最后
            break;
        }
        if(temp.next.no == no){
            //找到待删除节点的前一个节点temp
            flag = true;
            break;
        }
        temp = temp.next;   //temp后移 遍历
    }
    if(flag){//找到
        //可以删除
        temp.next = temp.next.next;
    }else {
        System.out.println("要删除的节点不存在");
    }
}

3. 单链表面试题

3.1 求单链表中节点的个数(不统计头节点)

其中就是遍历一遍即可

3.2 查找单链表中的倒数第k个节点

//查找单链表中倒数第k个节点
//思路
//1. 编写一个方法 接收head节点 同时接收一个index
//2. index 表示是倒数第index个节点
//3. 先把链表从头到尾遍历,得到链表的总长度
//4. 得到size后,从链表的第一个开始遍历(size - index)个 就可以得到
//5. 找到则返回该节点 否则返回null
public static HeroNode findLastIndexNode(HeroNode head, int index) {
    //判断如果链表为空 返回null
    if (head.next == null) {
        return null;    //没有找到
    }
    //第一次遍历得到链表的长度
    int size = getLength(head);
    //第二次遍历 size - index + 1位置 就是倒数的第k个节点
    //先做一个index的校验
    if (index <= 0 || index > size) {
        return null;
    }
    //定义一个辅助变量
    HeroNode temp = head.next;
    for (int i = 0; i < size - index; i++) {
        temp = temp.next;
    }
    return temp;
}

3.3 单链表的反转

按照自己写的如下 思路就是每一次分别找到原链表中第size - i 个节点即新链表中第i个节点(包含头节点),然后进行连接

    public static void reverse(HeroNode head) {
        if (head.next == null) {//判断如果链表为空
            return;    //没有找到
        }
        //第一次遍历得到链表的长度
        int size = getLength(head);
        HeroNode temp1 = null;
        HeroNode temp2 = null;
        HeroNode temp3 = head.next; //保存好原链表的第一个节点
        for (int i = 0; i < size; i++) {
            temp1 = temp3;
            for (int j = 0; j < size - i - 1; j++) {    //每个大循环依次从原链表中得到最后一个到第一个节点
                temp1 = temp1.next;
            }
//            System.out.println(temp1);
            temp2 = head;
            for (int j = 0; j < i; j++) {   //每个大循环依次从新链表得到头节点到最后第二个节点
                temp2 = temp2.next;
            }
//            System.out.println(i + " " + temp1 + " " + temp2);
            temp2.next = temp1; //重新将节点连接起来
        }
        temp3.next = null;  //切记 别忘了把新链表的最后一个(原链表的第一个)的next置null
    }

韩老师的思路:

  1. 先定义一个节点 reverseHead = new HeroNode();
  2. 从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
  3. 原来的链表的head.next=reverseHead.next;
//将单链表反转
public static void reverseList(HeroNode head){
    //如果当前链表为空 或者只有一个节点 无需反转 直接返回
    if(head.next == null || head.next.next == null){
        return;
    }

    //定义一个辅助指针(变量) 帮助遍历原来的链表
    HeroNode cur = head.next;
    HeroNode next = null;   //指向当前节点[cur]的下一个节点
    HeroNode reverseHead = new HeroNode(0,"","");
    //遍历原来的链表 每遍历一个节点 就将其取出 并放在新的链表reverseHead的最前面
    while (cur != null){
        next = cur.next;	//保存原链表中的下一个节点 
        cur.next = reverseHead.next; //先让此节点的next域指向新链表的首位
        reverseHead.next = cur;     //再将新链表的头部指向此节点 实现cur节点插入
        cur = next;     //再将该cur引用重新指向原链表没有进行操作的节点
    }
    //将head.next 指向reverseHead.next 实现单链表的反转
    head.next = reverseHead.next;
}

不得不说啊,这韩老师就是牛啊,短短几行代码就搞定了!!!其实就类似于插入,将原链表的节点按顺序插入到新链表的头部与第一个节点之间,此节点就作为新链表的第一个节点。那么最后原链表中的第i个节点就依次排到新链表中的size-i个节点中了。

3.4 从尾到头打印单链表

【方式一:反向遍历 方式二:Stack栈】

3.4.1 逆序输出 反向遍历

韩老师没演示这种 自己写的如下

//遍历逆序输出 
public static void reverseOutput(HeroNode head){
    if (head.next == null) {//判断如果链表为空
        return;    //没有找到
    }
    //第一次遍历得到链表的长度
    int size = getLength(head);
    HeroNode temp1 = null;
    for (int i = 0; i < size; i++) {
        temp1 = head.next;
        for (int j = 0; j < size - i - 1; j++) {    //每个大循环依次从原链表中得到最后一个到第一个节点
            temp1 = temp1.next;
        }
        System.out.println(temp1);
    }
}
//递归逆序输出
public static void reverseOutputDiGui(HeroNode head){

    if(head.next==null){//说明到头了
    }else {
        reverseOutputDiGui(head.next);
    }
    if(head.no != 0){
        System.out.println(head);
    }
}
3.4.2 Stack栈

韩老师思路如下

  • 可以利用栈这个数据结构,将各个节点压入到栈中 然后利用栈的先进后出的特点 就实现了逆序打印的效果

    /**
     * @author 小小低头哥
     * @version 1.0
     * 演示Stack的使用
     */
    public class TestStack {
        public static void main(String[] args) {
            Stack<String> stack = new Stack<>();
            //入栈
            stack.add("jack");
            stack.add("tom");
            stack.add("smith");
            //出栈
            while (stack.size() > 0){
                System.out.println(stack.pop());//POP就是将栈顶的数据取出
            }
        }
    }
    
    //逆序输出
    //利用栈这个数据结构,将各个节点压入到栈中 然后利用栈的先进后出的特点 就实现了逆序打印的效果
    public static void reversePrint(HeroNode head){
        if (head.next == null) {//判断如果链表为空
            return;    //空链表 不能打印
        }
        //创建一个栈 将各个节点压入栈
        Stack<HeroNode> stack = new Stack<>();
        HeroNode cur = head.next;
        //将链表的所有节点压入栈
        while (cur != null){
            stack.push(cur);    //入栈
            cur = cur.next; //后移
        }
        //将战中节点打印
        while (stack.size() > 0){
            System.out.println(stack.pop());    //出栈 先进后出
        }
    }
    

3.5 合并两个有序的单链表,合并之后的链表依然有序

自己苦战出来的,试过输入没毛病的情况下 ,结果还可以

思路:
以链表一为基准,遍历链表一的每一个节点。在遍历每个链表一的节点期间,将链表二中节点编号依次与此时遍历的链表一中 的节点编号进行比较,满足条件的则接在新链表的最后一个节点上。由于是链表一二都是顺序排列,所以一旦链表二中节点编号有不满足条件的,则将此时链表一中遍历的节点接在新链表的最后一个节点上,并跳出遍历链表二的循环,继续遍历链表一中的下一个节点,再接着上次链表二中第一个不满足的节点进行比较(注意不是重新与链表二中的节点一个个比较)。以此循环。

/**
 * 合并两个有序链表
 * 但是要确保输入的两个链表都是从小到大排序的 否则需要if(temp2.no <= temp1.no)中的 <= 换成 >=
 * @param head1
 * @param head2
 * @return 合并后的链表
 */
public static HeroNode mergeList(HeroNode head1, HeroNode head2) {
    HeroNode newHero = new HeroNode(0," "," ");//新建一个节点
    HeroNode end = newHero;   //保存newHero最后一个节点
    HeroNode temp1 = head1.next; //链表一指针
    HeroNode temp2 = head2.next; //链表二指针
    HeroNode next = null;   //保存下一个节点
    while (true){//以链表一进行顺序遍历
        while (true){//当链表二中有节点的no 小于链表一中的当前节点的no时
            if(temp2.no <= temp1.no){ //从小到大排序
                next = temp2.next;
                end.next = temp2;//将temp2插入到newHero最后
                end = end.next; //将end后移一个
                temp2 = next;   //将temp2指向链表二的下一个节点继续与temp1比较
            }else {
                next = temp1.next;
                end.next = temp1;//将temp1插入到newHero最后
                end = end.next; //将end后移一个
                if(next == null){ //说明temp1比较完了 直接把temp2插入到newHero最后
                    end.next = temp2;
                    return newHero;
                }
                temp1 = next;   //没比较完 则temp1后移
                break;  //结束链表二中的节点与链表一中temp1节点的比较(因为是有序的 所以链表二后面的肯定也不符合条件)
            }
            if(temp2 == null){//说明链表二中的节点都比较完毕 那么直接把链表一中剩下的节点temp1连接上新链表即可
                end.next = temp1;
                return newHero;
            }
        }
    }
}

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

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

相关文章

C语言函数详解

# 函数的概念 对于函数&#xff0c;我想大家应该并不陌生&#xff0c;在数学中就存在函数的概念&#xff0c;比如&#xff1a;一次函数 ykxb &#xff0c;k和b都是常数&#xff0c;给⼀个任意的x&#xff0c;就能得到⼀个y值。 在C语言中也有函数的概念&#xff0c;函数也被称为…

unity 模型生成PNG图片并导出(可以任意控制方向和大小,本文提供三种方案)

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、插件RuntimePreviewGenerator&#xff08;方案一&#xff09;二、unity 官方提供的接口&#xff08;方案二&#xff09;三、方法三&#xff0c;可以处理单个模型&#xff0c;也…

STM32基于USB串口通信应用开发

✅作者简介&#xff1a;热爱科研的嵌入式开发者&#xff0c;修心和技术同步精进&#xff0c; 代码获取、问题探讨及文章转载可私信。 ☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。 &#x1f34e;获取更多嵌入式资料可点击链接进群领取&#xff0c;谢谢支持&#xff01;…

git自动更新功能

确认权限 因为一般Linux系统网页用的www 或 www-data用户和用户组,所以要实现自动来去,首先要在www用户权限下生成ssh密钥,不然没有权限,其次就是,要把用root用户拉去的代码,批量改成www用户 1. 给www权限 vi /etc/sudoers www ALL=(ALL) NOPASSWD:/bin/chow…

整体式雨水收集pp模块可根据需求承重可达30到60吨每平方米

整体式雨水收集pp模块的承重能力主要取决于其设计和制造工艺&#xff0c;以及所使用的材料。一般来说&#xff0c;模块的尺寸越大&#xff0c;承重能力也越大。同时&#xff0c;模块的设计和制造工艺也会影响其承重能力。 在设计和制造整体式雨水收集pp模块时&#xff0c;需要…

unity 2d 入门 飞翔小鸟 小鸟碰撞 及死亡(九)

1、给地面&#xff0c;柱体这种添加2d盒装碰撞器&#xff0c;小鸟移动碰到就不会动了 2、修改小鸟的脚本&#xff08;脚本命名不规范&#xff0c;不要在意&#xff09; using System.Collections; using System.Collections.Generic; using UnityEngine;public class Fly : Mo…

CompletableFuture异步多任务最佳实践

简介 CompletableFuture相比于Java8的并行流&#xff0c;对于处理并发的IO密集型任务有着得天独厚的优势&#xff1a; 在流式编程下&#xff0c;支持构建任务流时即可执行任务。CompletableFuture任务支持提交到自定义线程池&#xff0c;调优方便。 本文所有案例都会基于这样…

向日葵远程控制鼠标异常的问题

​ 在通过向日葵进行远程控制的时候&#xff0c;可能会遇到鼠标位置异常的问题。此时&#xff0c;不管怎么移动鼠标&#xff0c;都会停留在屏幕最上方&#xff0c;而无法点击到正确的位置。如图&#xff1a; 此时&#xff0c;如果启用了“被控端鼠标”功能&#xff0c;可以正…

ChatGLM3-6B和langchain阿里云部署

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ChatGLM3-6B部署搭建环境部署GLM3 二、Chatglm2-6blangchain部署三、Tips四、总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; …

回顾2023 亚马逊云科技 re_Invent,创新AI,一路同行

作为全球云计算龙头企业的亚马逊云科技于2023年11月27日至12月1日在美国拉斯维加斯举办了2023 亚马逊云科技 re:Invent&#xff0c;从2012年开始举办的亚马逊云科技 re:Invent 全球大会,到现如今2023 亚马逊云科技 re:Invent&#xff0c;回顾历届re:Invent大会&#xff0c;亚马…

imutils库介绍及安装学习

目录 本机环境 安装 函数及属性 列举imutils库信息 属性和函数介绍及使用 属性 常用函数 方法使用 图像平移 图像缩放 图像旋转 骨架提取 通道转换 OPenCV版本的检测 综合测试 介绍 imutils 是一个用于图像处理和计算机视觉任务的 Python 工具包。它提供了一系…

Python爬虫-实现批量抓取王者荣耀皮肤图片并保存到本地

前言 本文是该专栏的第12篇,后面会持续分享python爬虫案例干货,记得关注。 本文以王者荣耀的英雄皮肤为例,用python实现批量抓取“全部英雄”的皮肤图片,并将图片“批量保存”到本地。具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。注意,这里抓取的图片…

centos7中的计划任务

一次调度执行-----at 安装&#xff1a; [rootzaotounan ~]# yum -y install at ​ 启动&#xff1a; [rootzaotounan ~]# systemctl start atd ​ 开机自启动&#xff1a; [rootzaotounan ~]# systemctl enbale atd ​ 语法&#xff1a; at <时间规格> 时间规格参数&…

Linux下C++动态链接库的生成以及使用

目录 一.前言二.生成动态链接库三.使用动态链接库 一.前言 这篇文章简单讨论一下Linux下如何使用gcc/g生成和使用C动态链接库&#xff08;.so文件&#xff09;。 二.生成动态链接库 先看下目录结构 然后看下代码 //demo.h#ifndef DEMO_H #define DEMO_H#include<string&…

键盘打字盲打练习系列之矫正坐姿——4

一.欢迎来到我的酒馆 盲打&#xff0c;矫正坐姿&#xff01; 目录 一.欢迎来到我的酒馆二.继续练习二.矫正坐姿1.键盘与鼠标2.椅子 三.改善坐姿建议 二.继续练习 前面的章节&#xff0c;我们重点向大家介绍了主键盘区指法和键盘键位。经过一个系列的教程学习&#xff0c;相信大…

C语言数据结构-双向链表

文章目录 1 双向链表的结构2 双向链表的实现2.1 定义双向链表的数据结构2.2 打印链表2.3 初始化链表2.4 销毁链表2.5 尾插,头插2.6 尾删,头删2.7 根据头次出现数据找下标2.8 定点前插入2.9 删除pos位置2.10 定点后插入 3 完整代码3.1 List.h3.2 Lish.c3.3 test.c 1 双向链表的结…

redis中缓存雪崩,缓存穿透,缓存击穿等

缓存雪崩 由于原有缓存失效&#xff08;或者数据未加载到缓存中&#xff09;&#xff0c;新缓存未到期间&#xff08;缓存正常从Redis中获取&#xff0c;如下图&#xff09;所有原本应该访问缓存的请求都去查询数据库了&#xff0c;而对数据库CPU和内存造成巨大压力&#xff0c…

数据结构算法-希尔排序算法

引言 在一个普通的下午&#xff0c;小明和小森决定一起玩“谁是老板”的扑克牌游戏。这次他们玩的可不仅仅是娱乐&#xff0c;更是要用扑克牌来决定谁是真正的“大老板”。 然而&#xff0c;小明的牌就像刚从乱麻中取出来的那样&#xff0c;毫无头绪。小森的牌也像是被小丑掷…

C++ 学习系列 -- 实现简单的 String

1 标准库 std::string c 中的 std::string 是一个重要的字符串的类, 我们在日常工作中常常与之打交道。 string是C标准库的重要部分&#xff0c;主要用于字符串处理。使用string库需要在同文件中包括该库 #include<string> std::string 实际上是 std::basic_string<…

基于ssm应急资源管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本应急资源管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…