数据结构-单向不带头不循环链表

链表知识总结

逻辑结构:线性结构(元素之间存在一对一关系)
存储结构(物理结构):链式存储(存储顺序和逻辑顺序不在乎是否一致)

1.链表的特点:擅长进行动态删除和增加操作,不擅长随机访问(需要遍历,因为链表不按顺序存放)

2.链表分类:单双向链表

单链表:元素节点有两部分组成(数据域-存储当前的元素、指针域-指向下一个节点地址)
双链表:元素节点有三部分组成(数据域-存储当前元素节点,2个指针域,1个用来指向上一个元素节点地址,1个用来指向下一个元素节点地址)

带头不带头:是否带有头指针
循环不循环:尾结点的next指针指向空-不循环,指向首元素节点或头指针-循环

3.链表的操作:创建、销毁、增(头插、尾插、中间插)删(头删、尾删、中间删)改查
由此得出:链表的分类的一共有8种       讲解单向、不带头、不循环链表

1、链表的介绍          链表是一种链式存储的线性表

1.1、链表的特点     节点定义成结构体类型

链表是一种基本的数据结构,它由一系列节点组成,每个节点包含一个值和指向下一个节点的指针

特点:1.不要求大片连续空间,改变容量方便。2.可以动态的添加和删除节点

缺点:不方便随机存取,要耗费一定空间存放指针。

2、链表的分类

链表可以分为三种类型:

  • 单向链表:每个节点只有一个指针指向下一个节点,最后一个节点的指针指向NULL。

  • 双向链表:每个节点有两个指针,分别指向上一个节点和下一个节点,可以在O(1)时间内实现向前和向后遍历。

  • 循环链表:在单向或双向链表的基础上,将最后一个节点的指针指向头节点,形成一个环。

链表的种类其实有很多种,按照单双向、带头不带头、循环不循环,一共可以分为8种类型,但最常见就是单向链表(单向不带头不循环)和双向链表(双向带头)

带头:有一个指针,专门指向首元素节点

3、链表的操作

链表的操作包括插入、删除、查找、遍历等。

  • 插入操作:可以在链表头或尾插入节点,也可以在指定位置插入节点。    头插、尾插、中间插
  • 删除操作:可以删除指定节点或按照值删除节点。                                    头删、尾删、中间删
  • 查找操作:可以查找指定节点或按照值查找节点。
  • 遍历操作:可以遍历整个链表,输出每个节点的值或执行其他操作

4、单向链表

单向链表是由若干个节点组成的数据结构,每个节点包含两个部分:数据域和指针域。

数据域存储节点的数据,指针域存储下一个节点的地址。

单链表的一个存储节点包含两部分:

data部分称为数据域,用于存储线性表的一个数据元素;   放的是元素值也可以叫值域

link部分称为指针域,用于存放一个指针,该指针指示链表下一个结点的开始存储地址   

指向下一个节点的指针

5、单链表的操作

5.1、main.c

#include <stdio.h>
#include "01.h"

int main() {
    fn3();
    return 0;
}

5.2、01.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>  //包含有调试文件

//定义单向、不带头、不循环链表的元素节点结构体类型
typedef int eleT;
typedef struct Link_Node {
    eleT val;//数据域  存储节点元素值
    struct Link_Node* next;//指针域   指向下一个节点
}LKNode;


void fn3(void);
void printf_menu(void);
LKNode* init_Link(eleT val);
void print_Link(LKNode* LinkNode);
void head_insert(LKNode** LinkNode, eleT val);
void tail_insert(LKNode* LinkNode, eleT val);
void head_delete(LKNode** LinkNode);
void tail_delete(LKNode** LinkNode);
LKNode* find_item(LKNode* LinkNode, eleT val);
void middle_delete(LKNode* LinkNode, eleT val);//中间删除1.0
void mid_del(LKNode* temp);//中间删除2.0
void mid_insert(LKNode* temp, eleT newval);
void destory(LKNode** LinkNode);

5.3、01.c

#include "01.h"

void fn3(void) {
    int order = 0;//接收用户输入的命令
    printf_menu();
    LKNode* LinkNode = NULL;    //存放头结点指针(地址)
    eleT val = 0;
    LKNode* temp = NULL;

    while (1) {
        printf("请输入您的操作指令:");
        scanf("%d", &order);
        switch (order) {
        case 1:
            //初始化    初始化一个头结点,不包含头(头结点包含元素)
            printf("请输入头结点元素值:");
            scanf("%d", &val);
            LinkNode=init_Link(val);
            break;
        case 2:
            //遍历
            print_Link(LinkNode);
            break;
        case 3:
            //链表头插
            printf("请输入要头插的元素");
            scanf("%d", &val);
            head_insert(&LinkNode,val);//头插,头结点要变,只能传地址
            break;
        case 4:
            //链表尾插
            printf("请输入要尾插的元素");
            scanf("%d", &val);
            tail_insert(LinkNode, val);//尾部插入,头结点不变,也可以传值
            break;
        case 5:
            //链表头删
            head_delete(&LinkNode);
            break;
        case 6:
            //链表尾删
            tail_delete(&LinkNode);
            break;
        case 7:
            //链表的查找
            //思路:找到了返回当前元素节点的指针   找不到返回NULL
            printf("请输入要查找的元素");
            scanf("%d", &val);
            temp = find_item(LinkNode, val);
            if (temp == NULL) {
                printf("没有此元素");
            }
            else {
                printf("找到的元素值为:%d\n", temp->val);
            }
            break;
        case 8:
            //链表的中间删(随机删),至少需要两个元素(要删除的目标元素以及它的前一个元素)

            /*
            printf("请输入要删除的元素");
            scanf("%d", &val);
            middle_delete(LinkNode, val);
            */
            printf("请输入要删除的前一个元素");
            scanf("%d", &val);
            temp = find_item(LinkNode, val);
            if (temp == NULL) {
                printf("没有此元素==》要删除的前一个\n");
            }
            else {
                //删除查找的元素temp的后一个元素
                mid_del(temp);
            }
            break;
        case 9:
            //链表的中间插
            printf("请输入您要在哪个元素后面插入:");
            scanf("%d", &val);
            printf("请输入要中间插的新元素");
            eleT newval = 0;
            scanf("%d", &newval);
            temp = find_item(LinkNode, val);
            if (temp == NULL) {
                printf("无法插入\n");
            }
            else {
                //正常中间插
                mid_insert(temp, newval);
            }
            break;
        case 10:
            //链表销毁
            destory(&LinkNode);
            break;
        case 11:
            return;
        default:
            printf("指令输入有误");
        }
    }
}

//打印菜单
void printf_menu(void) {
    system("cls");//系统函数 用于屏幕清空
    printf("操作指令说明:\n");
    printf("1:链表初始化\n2:打印链表\n");
    printf("3:链表头插\n4:链表尾插\n");
    printf("5:链表头删\n6:链表尾删\n");
    printf("7:链表的查找\n8:链表的中间删\n");
    printf("9:链表的中间插\n10:链表销毁\n");
    printf("11:退出\n");
}

//初始化头结点
LKNode* init_Link(eleT val) {
    //申请节点内存
    LKNode* newnode = (LKNode*)malloc(sizeof(LKNode));
    //判断内存是否申请成功
    /*
    if (!newnode) {
        printf("内存申请失败\n");
        return NULL;
    }
    */
    assert(newnode);//如果为空,报错
    newnode->val = val;
    newnode->next = NULL;
    printf("初始化成功\n");
    return newnode;
}

//遍历
void print_Link(LKNode* LinkNode) {
    assert(LinkNode);//是否初始化
    //正常遍历
    //方法1
/*
    LKNode* temp = LinkNode;
    while (temp->next != NULL) {
        printf("%d ", temp->val);//输出不是尾结点的元素
        temp = temp->next;
    }
    printf("%d ", temp->val);//输出尾结点的元素值
}
*/
//方法2
    LKNode* temp = LinkNode;
    while (1) {
        printf("%d ", temp->val);
        if (temp->next == NULL) {//尾结点
            break;
        }
        temp = temp->next;
    }
    printf("\n");
}


//头插
void head_insert(LKNode **LinkNode, eleT val) {
    assert(*LinkNode);//是否初始化
    //1.创建新节点
    LKNode* newnode = (LKNode*)malloc(sizeof(LKNode));
    assert(newnode);
    //2.给新节点赋值
    newnode->val = val;
    newnode->next = *LinkNode;
    //3.更新头结点
    *LinkNode = newnode;//更新头结点
    printf("头插成功\n");
}

    //补充调试代码常用的函数:
    //exit()     用于终止程序执行,输出一些参数,一般用0表示正常退出,!0异常退出
    //assert()  导入<assert.h>头文件   用于调试代码,条件为真,程序正常执行,条件为假,程序报错    


//尾插
void tail_insert(LKNode* LinkNode, eleT val) {
    assert(LinkNode);//判断是否初始化
    //1.创建新节点
    LKNode* newnode = (LKNode*)malloc(sizeof(LKNode));
    assert(newnode);//内存申请成功
    //2.给新节点成员赋值
    newnode->val = val;
    newnode->next = NULL;

    //3.将新节点放到链表的尾部,首先找到链表的尾结点
    LKNode* temp = LinkNode;
    while (1) {
        if (temp->next == NULL) {
            //4.说明找到尾结点,将新节点插到尾结点后面
            temp->next = newnode;
            break;
        }
        temp = temp->next;
    }
    printf("尾插成功!\n");
}


//头删
void head_delete(LKNode** LinkNode) {
    assert(LinkNode);//判断是否初始化
    LKNode* temp = (*LinkNode)->next;//保存当前头结点的后一个节点
    free(*LinkNode);//释放原来的头结点
    *LinkNode = temp;//更新头结点
    printf("头删成功!\n");
}

//尾删
void tail_delete(LKNode** LinkNode) {
    assert(*LinkNode);//判断是否初始化

    //考虑只有一个结点时
    if ((*LinkNode)->next == NULL) {
        free(*LinkNode);//释放当前节点
        *LinkNode = NULL;
    }
    else {
        //找到链表的倒数第二个元素节点
        LKNode* temp = *LinkNode;
        while (1) {
            if (temp->next->next == NULL) {
                //4.说明找到倒数第二个节点
                break;
            }
            temp = temp->next;
        }
        free(temp->next);//释放尾结点
        temp->next = NULL;//更新尾结点
        printf("尾删成功!\n");
    }
}

//查找
LKNode* find_item(LKNode* LinkNode, eleT val) {
    assert(LinkNode);//判断是否初始化
    LKNode* temp = LinkNode;
    while (temp) {
        if (temp->val == val) {
            //找到了查找的元素
            return temp;
        }
        temp = temp->next;
    }
    return NULL;
}

//中间删1.0
void middle_delete(LKNode* LinkNode, eleT val) {
    assert(LinkNode);//判断是否初始化
    int flag = 0;//表示查找元素的状态
    //查找到删除元素的前一个元素的地址
    LKNode* temp = LinkNode;
    while (temp) {
        if (temp->next->val == val) {
            flag = 1;
            //找到了,删除temp->next
            LKNode* ptr = temp->next;//保存要删除的节点
            temp->next = temp->next->next;
            free(ptr);//释放要删除的节点
            printf("中间删成功!\n");
            break;
        }
        temp=temp->next;
    }
    if(flag==0){
        printf("没有找到要删除的元素\n");
    }
}


//中间删2.0,至少需要两个元素(要删除的目标元素以及它的前一个元素),才能实现中间的随机删除
void mid_del(LKNode* temp) {
    //temp是要删除元素的前一个元素
    LKNode* ptr = temp->next->next;//保存要删除元素的后一个元素
    free(temp->next);//释放要删除的元素
    temp->next = ptr;
    printf("中间删成功!\n");
}

//中间插
void mid_insert(LKNode* temp, eleT newval) {
    LKNode* newnode =(LKNode*) malloc(sizeof(LKNode));
    assert(newnode);
    //给新节点赋值
    newnode->val = newval;
    //将新节点插入
    newnode->next = temp->next;
    temp->next = newnode;
    printf("中间插入成功!\n");
}

//销毁    遍历后一个一个删除
void destory(LKNode** LinkNode) {
    assert(*LinkNode);//*LinkNode头结点
    while (*LinkNode) {
        LKNode* temp = (*LinkNode)->next;
        free(*LinkNode);
        *LinkNode = temp;
    }
    *LinkNode = NULL;//更新头结点
    printf("销毁成功!\n");
}

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

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

相关文章

28:CAN总线入门一:CAN的基本介绍

CAN总线入门 1、CAN总线简介和硬件电路1.1、CAN简要介绍1.2、硬件电路1.3、CAN总线的电平标准 2、帧格式2.1、数据帧&#xff08;掌握&#xff09;2.2、遥控帧&#xff08;掌握&#xff09;2.3、错误帧&#xff08;了解&#xff09;2.4、过载帧&#xff08;了解&#xff09;2.5…

2018年西部数学奥林匹克几何试题

2018G1 在 △ A B C \triangle ABC △ABC 中, O O O 为外心, M M M 为边 B C BC BC 的中点, 延长 A B AB AB 交 ( A O M ) (AOM) (AOM) 于点 D D D, ( A O M ) (AOM) (AOM) 交 A C AC AC 于点 E E E. 求证: E C D M ECDM ECDM. 证明: 设点 G G G 为 △ A B C …

知识图谱抽取分析中,如何做好实体对齐?

在知识图谱抽取分析中&#xff0c;实体对齐是将不同知识图谱中的相同实体映射到同一表示空间的关键步骤。为了做好实体对齐&#xff0c;可以参考以下方法和策略&#xff1a; 基于表示学习的方法&#xff1a; 使用知识图谱嵌入技术&#xff0c;如TransE、GCN等&#xff0c;将实体…

UnityXR Interaction Toolkit 如何检测HandGestures

前言 随着VR设备的不断发展,从最初的手柄操作,逐渐演变出了手部交互,即头显可以直接识别玩家的手部动作,来完成手柄的交互功能。我们今天就来介绍下如何使用Unity的XR Interaction Toolkit 来检测手势Hand Gesture。 环境配置 1.使用Unity 2021或者更高版本,创建一个项…

Maven在Win10上的安装教程

诸神缄默不语-个人CSDN博文目录 这个文件可以跟我要&#xff0c;也可以从官网下载&#xff1a; 第一步&#xff1a;解压文件 第二步&#xff1a;设置环境变量 在系统变量处点击新建&#xff0c;输入变量名MAVEN_HOME&#xff0c;变量值为解压路径&#xff1a; 在系统变…

高等数学学习笔记 ☞ 不定积分与积分公式

1. 不定积分的定义 1. 原函数与导函数的定义&#xff1a; 若函数可导&#xff0c;且&#xff0c;则称函数是函数的一个原函数&#xff0c;函数是函数的导函数。 备注&#xff1a; ①&#xff1a;若函数是连续的&#xff0c;则函数一定存在原函数&#xff0c;反之不对。 ②&…

KHOJ的安装部署

KHOJ的部署记录 KHOJ是一个开源的AI对话平台&#xff08;github标星超2w&#xff09;&#xff0c;有免费版本&#xff08;https://app.khoj.dev/&#xff09;。但本地部署&#xff0c;可以保证自己的文件安全&#xff0c;另外一方面&#xff0c;有数据库能随时查询过去自己的所…

windows 搭建flutter环境,开发windows程序

环境安装配置&#xff1a; 下载flutter sdk https://docs.flutter.dev/get-started/install/windows 下载到本地后&#xff0c;随便找个地方解压&#xff0c;然后配置下系统环境变量 编译windows程序本地需要安装vs2019或更新的开发环境 主要就这2步安装后就可以了&#xff0…

Jupyter notebook中运行dos指令运行方法

Jupyter notebook中运行dos指令运行方法 目录 Jupyter notebook中运行dos指令运行方法一、DOS(磁盘操作系统&#xff09;指令介绍1.1 DOS介绍1.2 DOS指令1.2.1 DIR - 显示当前目录下的文件和子目录列表。1.2.2 CD 或 CHDIR - 改变当前目录1.2.3 使用 CD .. 可以返回上一级目录1…

SpringMVC——原理简介

狂神SSM笔记 DispatcherServlet——SpringMVC 的核心 SpringMVC 围绕DispatcherServlet设计。 DispatcherServlet的作用是将请求分发到不同的处理器&#xff08;即不同的Servlet&#xff09;。根据请求的url&#xff0c;分配到对应的Servlet接口。 当发起请求时被前置的控制…

Python从0到100(八十三):神经网络-使用残差网络RESNET识别手写数字

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能…

做跨境电商服务器用什么宽带好?

做跨境电商服务器用什么宽带好&#xff1f;做跨境电商服务器&#xff0c;推荐选择光纤宽带或高性能的5G网络。光纤宽带高速稳定&#xff0c;适合处理大量数据和实时交互&#xff1b;5G网络则提供超高速移动连接&#xff0c;适合需要灵活性和移动性的卖家。具体选择需根据业务规…

python密码学列置换加密解密程序

1.置换密码 置换密码&#xff08;Permutation Cipher)又叫换位密码&#xff08;Transposi-tionCipher)&#xff0c;它根据一定的规则重新排列明文&#xff0c;以便打破明文的结构特性。置换密码的特点是保持明文的所有字符不变&#xff0c;只是利用置换打乱了明文字符的位置和次…

基于SpringBoot+Vue的酒店管理系统设计与实现

在介绍文章之前呢&#xff0c;小伙伴们需要掌握关于咱们前后端的相关的知识点&#xff0c;我整理了几个课程&#xff0c;有兴趣的话可以了解一下&#xff1a; 课程1-java和vue前后端分离项目实战 课程2-HTML5入门级开发 课程3-vue入门级开发教程 课程4-CSS入门级开发 可以进行自…

HarmonyOS命令行工具

作为一个从Android转过来的鸿蒙程序猿&#xff0c;在开发过程中不由自主地想使用类似adb命令的命令行工具去安装/卸载应用&#xff0c;往设备上推或者拉去文件&#xff0c;亦或是抓一些日志。但是发现在鸿蒙里边&#xff0c;华为把命令行工具分的很细&#xff0c;种类相当丰富 …

Linux Top 命令 load average 指标解读

前言 作为平台开发的同学&#xff0c;维护平台稳定性是我们最基本的工作职责&#xff0c;下面主要介绍下top 命令里 &#xff0c;load average 这个指标如何去衡量机器负载程度。 概念介绍 load average 是系统在过去 1 分钟、5 分钟、15 分钟 的平均负载&#xff0c;它表示运…

Oracle 可观测最佳实践

简介 Oracle 数据库是一种广泛使用的商业关系数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由甲骨文公司&#xff08;Oracle Corporation&#xff09;开发。它支持 SQL 语言&#xff0c;能够存储和管理大量数据&#xff0c;并提供高级数据管理功能&#xff0c;如数…

imbinarize函数用法详解与示例

一、函数概述 众所周知&#xff0c;im2bw函数可以将灰度图像转换为二值图像。但MATLAB中还有一个imbinarize函数可以将灰度图像转换为二值图像。imbinarize函数是MATLAB图像处理工具箱中用于将灰度图像或体数据二值化的工具。它可以通过全局或自适应阈值方法将灰度图像转换为二…

《深入理解Mybatis原理》Mybatis中的缓存实现原理

一级缓存实现 什么是一级缓存&#xff1f; 为什么使用一级缓存&#xff1f; 每当我们使用MyBatis开启一次和数据库的会话&#xff0c;MyBatis会创建出一个SqlSession对象表示一次数据库会话。 在对数据库的一次会话中&#xff0c;我们有可能会反复地执行完全相同的查询语句&…

网络安全面试题汇总(个人经验)

1.谈一下SQL主从备份原理&#xff1f; 答&#xff1a;主将数据变更写入自己的二进制log,从主动去主那里去拉二进制log并写入自己的二进制log,从而自己数据库依据二进制log内容做相应变更。主写从读 2.linux系统中的计划任务crontab配置文件中的五个星星分别代表什么&#xff…