单向链表和双向链表的增删改查以及排序问题

news2024/11/20 20:26:45

文章目录

  • 单向链表
  • 双向链表
  • 运行结果
  • 完整代码


本文介绍链表的增删改查以及排序问题,其中最重要的还是指针的使用!

单向链表

定义一个单向链表,里面包含一个整型数据和一个指向后向的指针。

typedef struct linklist{
    int data;
    struct linklist *next;
}LinkList;

指针负责在每次链表发生变化时指向下一个正确的链表元素,当在链表尾时,其指向为NULL。
通过用户输入创建链表,最后创建链表的函数返回链表头指针。

LinkList *CreateLinkList()
{
    int n = 0;
    LinkList *head,*p,*q;
    head = NULL;
    p = q = (LinkList *)malloc(sizeof(LinkList));
    printf("input data %d (input 65535 end):",n+1);
    scanf("%d",&p->data);
    if(p->data == 65535)
        return head;
    
    while(1)
    {
        n++;
        if(n==1)
            head = p;
        else
            q->next = p;
        q = p;
        p = (LinkList *)malloc(sizeof(LinkList));
        printf("input data %d (input 65535 end):",n+1);
        scanf("%d",&p->data);
        if(p->data == 65535)
            break;
    }
    q->next = NULL;
    return head;
}

链表的创建通过用户输入完成,因此内存都是动态分配的,每次用到一个就分配一个,记得在最后将尾指针指向NULL。
打印链表的函数如下。

void print_linklist(LinkList *head)
{
    LinkList *p;
    p = head;
    if(head != NULL)
    {
        do{
            printf("%d ",p->data);
            p = p->next;
        }while(p != NULL);
    }
    else
    {
        printf("The link list is empty!\n");
    }
}

打印链表比较简单,前提是确保链表的尾指针指向NULL,否则会陷入无尽循环!
冒泡排序函数如下。

void bubblesort(LinkList *head)
{
    LinkList *cur,*tail;
    cur = head;
    tail = NULL;
    while(cur != tail)
    {
        while(cur->next != tail)
        {
            if(cur->data < cur->next->data)
            {
                int temp;
                temp = cur->data;
                cur->data = cur->next->data;
                cur->next->data = temp;
            }
            cur = cur->next;
        }
        tail = cur;
        cur = head;
    }
}

冒泡排序就是每次将序列中的最大值或者最小值移动到链表的最左边或者最右边,这样经过几次排序后,链表序列就是由小到大或由大到小的顺序排列了。
选择排序函数如下。

void selectsort(LinkList *head)  //选择排序
{
    LinkList *cur,*p,*q;
    int cur_data,min_data;
    cur = head;
    p = q = NULL;
    while(cur != NULL)
    {
        p = cur->next;
        cur_data = cur->data;
        min_data = cur_data;
        while(p != NULL)
        {
            if(p->data < min_data)
            {
                min_data = p->data;
                q = p;
            }
            p = p->next;
        }
        if(cur_data != min_data)
        {
            cur->data = min_data;
            q->data = cur_data;
        }
        cur = cur->next;
    }
}

选择排序就是在一次排序中找到序列中的最大或最小值,然后记住该位置,在每次检查到头后交换当前指针位置和记录位置的值,以此达到排序的目的。
在链表中插入。

LinkList *linklist_insert(LinkList *head,int data)
{
    LinkList *p,*q;
    q = (LinkList *) malloc(sizeof(LinkList));
    q->data = data;
    
    if(head == NULL || data <= head->data)  //在链头插入
    {
        q->next = head;
        head = q;
    }
    else
    {
        p = head;
        while(p != NULL)
        {
            if(data >= p->data && (p->next == NULL || data <= p->next->data))
            {
                q->next = p->next;
                p->next = q;
                break;
            }
            p = p->next;
        }
    }
    return head;
}

本代码中的插入操作是将链表的值由小到大排序后进行的,在插入值后,保持序列的排列顺序不变,因此就要找到合适的插入位置插入。
在链表中删除。

LinkList *linklist_delete(LinkList *head,int data)
{
    LinkList *p,*q;
    p = head;
	if(head == NULL)
	{
		return NULL;
	}
    while(p != NULL)
    {
        if(p->data != data)
            p = p->next;
		else
			break;
   }
	if(p == NULL)   //要删除的数不在链表中
    {
       printf("not found %d in the link list\n",data); 
       return head;
	}
	else
	{
		p = head;
	}
    while(p != NULL)
    {
        if(p->data == data) //链头就是要删除的
        {
            q = head;
            head = p->next;
            p = head;
            free(q);
        }
        else if(p->data < data && p->next->data == data)
        {
            q = p->next;
            p->next = q->next;
            free(q);
            if(p->next == NULL)
                break;
        }
        else   //如果找到了要删除的,指针不动,从当前位置接着判断
        {
            p = p->next;
        }
    }
    return head;
}

在链表中删除时,首先要查找要删除的值在不在链表中,如果在,就执行删除操作,删除操作会删除链表中所有等于要删除的值,如果不在,就提示用户输入的值并不在链表中。
修改链表中的某个值。

LinkList *linklist_modify(LinkList *head,int data,int modify_value)
{
    LinkList *p;
    p = head;
    if(head == NULL)
	{
		return NULL;
	}
    while(p != NULL)
    {
        if(p->data != data)
            p = p->next;
		else
			break;
   }
	if(p == NULL)
    {
       printf("not found %d in the link list\n",data); 
       return head;
	}
	else
	{
		p = head;
	}
    while(p != NULL)
    {
        if(p->data == data)
        {
            p->data = modify_value;
        }
        p = p->next;
    }
    return head;
}

修改链表中的值时同样先要进行查询,看看要修改的值在不在链表当中,如果在,就将其替换为要替换的值,否则还是打印信息提示用户要修改的值不在链表中。


双向链表

双向链表和单向链表差不多,但是多了一个指针,不仅有指向后向的,还有指向前向的,一定要注意当前节点是NULL的时候,前向和后向的指向,虽然不会报错,但是结果可能不会按照预想的输出。
定义一个双链表,里面包含一个整型数据和两个指针,分别指向前向和后向。

typedef struct linklist{
    int data;
    struct linklist *pre;
    struct linklist *next;
}LinkList;

双向链表的创建和单向链表差不多,不过要注意pre指针的指向。

LinkList *CreateLinkList()
{
    int n = 0;
    LinkList *head,*p,*q;
    head = NULL;
    p = q = (LinkList *)malloc(sizeof(LinkList));
    printf("input data %d (input 65535 end):",n+1);
    scanf("%d",&p->data);
    if(p->data == 65535)
        return head;
    
    while(1)
    {
        n++;
        if(n==1)
        {
            head = p;
            head->pre = NULL;
        }    
        else
        {
            p->pre = q;
            q->next = p;
        } 
        q = p;
        p->next = NULL;
        p = (LinkList *)malloc(sizeof(LinkList));
        printf("input data %d (input 65535 end):",n+1);
        scanf("%d",&p->data);
        if(p->data == 65535)
            break;
    }
    return head;
}

双向链表的插入代码如下,要注意只有插入位置的后向非空时,其后向才能使用pre指针。

LinkList *linklist_insert(LinkList *head,int data)
{
    LinkList *p,*q;
    q = (LinkList *) malloc(sizeof(LinkList));
    q->data = data;
    
    if(head == NULL || data <= head->data)  //在链头插入
    {
        q->next = head;
        if(head != NULL)
            head->pre = q;
        q->pre = NULL;
        head = q;
    }
    else
    {
        p = head;
        while(p != NULL)
        {
            if(p->data <= data && (p->next == NULL || p->next->data >= data))
            {
                q->next = p->next;
                q->pre = p;
                if(p->next != NULL)
                    p->next->pre = q;
                p->next = q;
                break;
            }
            p = p->next;
        }
    }
    return head;
}

下面是删除双向链表中某个或某些节点的代码,要注意在指针指向为NULL时及时退出循环。

LinkList *linklist_delete(LinkList *head,int data)
{
    LinkList *p,*q;
    p = head;
	if(head == NULL)
	{
		return NULL;
	}
    while(p != NULL)
    {
        if(p->data != data)
            p = p->next;
		else
			break;
   }
	if(p == NULL)
    {
       printf("not found %d in the link list\n",data); 
       return head;
	}
	else
	{
		p = head;
	}
   
    /*
    while(p != NULL)
    {
        if(p->data == data) //链头就是要删除的
        {
            q = head;
            head = p->next;
            if(p->next != NULL)
                p->next->pre = head;
            else
                break;
            head->pre = NULL;   //head非空时,其pre才可以指向NULL
            p = head;
            free(q);
        }
        else if(p->data < data && p->next->data == data)
        {
            q = p->next;
            p->next = q->next;
            if(p->next != NULL)
                q->next->pre = p;
            free(q);
            if(p->next == NULL)
                break;
        }
        else
        {
            p = p->next;
        }
    }
    */

    while(p != NULL)
    {
        if(p->data == data)
        {
            if(p->pre == NULL)           //链头就是要删除的
            {
                q = head;
                p = p->next;
                if(p != NULL)
                {
                	p->pre = NULL;    //一定要确保当前节点非空,pre才可以指向NULL
                	head = p;
                }    
                else
                {
                    head = NULL;
                    free(q);
                    break;
                } 
            }
            else
            {
                q = p;
                p->pre->next = p->next;
                if(p->next != NULL)
                {
					p->next->pre = p->pre;
					p = p->next; 
				}
                else
                {
                    free(q);
                    break;
                } 
            }
            free(q);
        }
        else
        {
            p = p->next;
        }
    }
    return head;
}

双向链表中的修改同单向链表的修改。


运行结果

单向链表和双向链表的运行结果是一样的,其结果如下图所示。
在这里插入图片描述
本代码中的删除和修改操作不是删除或者修改链表中的一个,所有相同值的都会被删除或者修改,如下图所示。
在这里插入图片描述


完整代码

单向链表和双向链表的完整代码

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

InnoDB 的默认隔离级别是什么?

分析&回答 大部分数据库系统&#xff08;如Oracle&#xff09;都将都将读提交&#xff08;Read-Commited&#xff09;作为默认隔离级别&#xff0c;而MySQL却选择可重复读&#xff08;Repeatable-Read&#xff09;作为其默认隔离级别。 如果没有隔离级别基础知识的话先看…

数学建模之插值法

目录 1 插值法概述2 插值法原理3 拉格朗日插值4 牛顿插值5 三次Hermite插值&#xff08;重点&#xff09;6 三次样条插值&#xff08;重点&#xff09;7 各种插值法总结8 n 维数据的插值9 插值法拓展10 课后作业 1 插值法概述 数模比赛中&#xff0c;常常需要根据已知的函数点进…

【Unity3D赛车游戏优化篇】新【八】汽车实现镜头的流畅跟随,以及不同角度的切换

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

YOLOV8实例分割——详细记录环境配置、自定义数据处理到模型训练与部署

前言 Ultralytics YOLOv8是一种前沿的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它在前代YOLO版本的成功基础上进行了进一步的创新&#xff0c;引入了全新的特性和改进&#xff0c;以进一步提升性能和灵活性。作为一个高速、精准且易于操作的设计&#xff0c;YO…

合宙Air724UG LuatOS-Air LVGL API控件-标签 (Label)

标签 (Label) 标签是 LVGL 用来显示文字的控件。 示例代码 label lvgl.label_create(lvgl.scr_act(), nil) lvgl.label_set_recolor(label, true) lvgl.label_set_text(label, "#0000ff Re-color# #ff00ff words# #ff0000 of\n# align the lines …

Vision Transformer(VIT 网络架构)

论文下载链接&#xff1a;https://arxiv.org/abs/2010.11929 文章目录 引言1. VIT与传统CNN的比较2. 为什么需要Transformer在图像任务中&#xff1f; 1. 深入Transformer1.1 Transformer的起源&#xff1a;NLP领域的突破1.2 Transformer的基本组成1.2.1 自注意机制 (Self-Atte…

C++将派生类赋值给基类

在 C/C++ 中经常会发生数据类型的转换,例如将 int 类型的数据赋值给 float 类型的变量时,编译器会先把 int 类型的数据转换为 float 类型再赋值;反过来,float 类型的数据在经过类型转换后也可以赋值给 int 类型的变量。 数据类型转换的前提是,编译器知道如何对数据进行取舍…

星际争霸之小霸王之小蜜蜂(十)--鼠道

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;九&#xff09;--狂鼠之灾 星际争霸之小霸王之小蜜蜂&#xff08;八&#xff09;--蓝皮鼠和大脸猫 星际争霸之小霸王之小蜜蜂&#xff08;七&#xff09;--消失的子弹 星际争霸之小霸王之小蜜蜂&#xff08;六&#xff09;-…

大数据课程K20——Spark的SparkSql概述

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的SparkSQL由来; ⚪ 了解Spark的SparkSQL特点; ⚪ 了解Spark的SparkSQL优势; ⚪ 掌握Spark的SparkSQL入门; 一、SparkSQL概述 1. 概述 Spark为结构化数据处理引入了一个称…

SQLI-labs-第四关

知识点&#xff1a;get双引号(")和括号注入 思路&#xff1a; 1、判断注入点 首先&#xff0c;输入?id1 --&#xff0c;看看正常的回显状态 接着输入?id1 --&#xff0c;结果还是正常回显&#xff0c;说明这里不存在单引号问题 试试双引号&#xff0c;这里爆出了sql语…

深入了解GCC编译过程

关于Linux的编译过程&#xff0c;其实只需要使用gcc这个功能&#xff0c;gcc并非一个编译器&#xff0c;是一个驱动程序。其编译过程也很熟悉&#xff1a;预处理–编译–汇编–链接。在接触底层开发甚至操作系统开发时&#xff0c;我们都需要了解这么一个知识点&#xff0c;如何…

C# 如何读取dxf档案

需求来源&#xff1a; 工作中&#xff0c;客户提供一张CAD导出的dxf 档案&#xff0c;然后需要机器人将其转成点位&#xff0c;走到对应的位置。 下面介绍一下dxf档案到底是什么&#xff1f;以及语法规则。 dxf 格式介绍&#xff1a;DXF 格式 dxf LINE 格式。 其实上述文档…

软考:中级软件设计师:多媒体基础,音频,图像,颜色,多媒体技术的种类,图像音频视频的容量计算,常见的多媒体标准

软考&#xff1a;中级软件设计师:多媒体基础 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &am…

在公网上使用SSH远程连接安卓手机Termux:将Android手机变身为远程服务器

文章目录 前言1.安装ssh2.安装cpolar内网穿透3.远程ssh连接配置4.公网远程连接5.固定远程连接地址 前言 使用安卓机跑东西的时候&#xff0c;屏幕太小&#xff0c;有时候操作不习惯。不过我们可以开启ssh&#xff0c;使用电脑PC端SSH远程连接手机termux。 本次教程主要实现在…

介绍OpenCV

OpenCV是一个开源计算机视觉库&#xff0c;可用于各种任务&#xff0c;如物体识别、人脸识别、运动跟踪、图像处理和视频处理等。它最初由英特尔公司开发&#xff0c;目前由跨学科开发人员社区维护和支持。OpenCV可以在多个平台上运行&#xff0c;包括Windows、Linux、Android和…

高等数学刷题

分段函数主要看在临界点处的左右极限是否相等&#xff0c;若相等则整段函数即为连续 &#xff08;反之若是连续函数&#xff0c;在某一点为间断点&#xff0c;则可推导出一定为可去间断点&#xff09; 无定义的点一定为间断点 如果该点有极限则为可去间断点 由于x的不确定导…

RK3568-spi-适配1.8寸TFT彩屏驱动芯片st7735s

RK3568-spi-适配1.8寸TFT彩屏 驱动芯片st7735s 显示分辨率128x160硬件连接 VCC -- 3.3V GND -- GND BL -- 背光控制 CS -- 片选引脚 DC -- 数据/命令控制 RES -- 屏幕复位 SCL -- i2c时钟引脚 SDA -- i2c数据引脚设备树编写 &spi0 {pinctrl-names = "default"…

docker安装Apache NIFI

说明 系统&#xff1a;CentOS7.9 nifi版本&#xff1a;1.23.2 下载镜像 nifi的镜像比较大&#xff0c;大概有2G左右&#xff0c;下载时间根据个人网速而定 docker pull apache/nifi:1.23.2 查看下载好的镜像 docker images 复制容器数据 创建挂载目录 创建挂载目录的目…

【Yolov5+Deepsort】训练自己的数据集(3)| 目标检测追踪 | 轨迹绘制 | 报错分析解决

&#x1f4e2;前言&#xff1a;本篇是关于如何使用YoloV5Deepsort训练自己的数据集&#xff0c;从而实现目标检测与目标追踪&#xff0c;并绘制出物体的运动轨迹。本章讲解的为第三部分内容&#xff1a;数据集的制作、Deepsort模型的训练以及动物运动轨迹的绘制。本文中用到的数…

qt day 6

登录界面 #include "window.h" #include<QDebug> #include<QIcon> Window::Window(QWidget *parent) //构造函数的定义: QWidget(parent) //显性调用父类的构造函数 {//判断数据库对象是否包含了自己使用的数据库Student.dbif(!db.contains(&…