数据结构与算法-----顺序表(链表篇)

news2024/11/24 22:41:44

 目录

前言

顺序表

链表

概念

与数组的不同 

单链表

1. 创建节点

2.插入节点

尾插节点(形成链表结构)

向指定位置插入节点(链表已有)

​编辑

3.遍历链表数据

4.获取链表长度

5.删除节点

删除尾节点

删除指定节点

6.清空链表

7.链表的数据查找

8.链表的转置

 测试集

 环形链表

创建环形链表 

双向循环链表

创建双向循环链表


前言

        在此之前我们学习过了数组,数组也是一种顺序表,连续储存结构,但是数组本质上是一种静态空间,而我们主要讲的是动态空间的顺序表,顾名思义也就是我们常见的链表,链表又分为单链表以及环状链表双向循环链表,下面我们就一起来看看吧!(我已经上传代码,可下载代码资源)

顺序表

定义:

顺序表,全名顺序存储结构,是线性表的一种。通过《什么是线性表》一节的学习我们知道,线性表用于存储逻辑关系为“一对一”的数据,顺序表自然也不例外。
不仅如此,顺序表对数据的物理存储结构也有要求。顺序表存储数据时,会提前申请一整块足够大小的物理空间,然后将数据依次存储起来,存储时做到数据元素之间不留一丝缝隙。 

我们常见的顺序表有数组和链表等,前者是一个静态空间的数据储存结构,后者是一个动态空间数据储存结构。二者都可以去通过引索去实现表的操作,下面就看链表的相关操作。

链表

概念

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作。 

 链表的节点是分为两部分:数据域指针域

数据域:存放各种数据,比如int、char类型等等

指针域:作为指向下一个节点的桥梁 

typedef struct student{
    //数据域
    int index;//作为下标表示第几位
    long num;//学号
    char name[20];//名字
    int score;//分数
    //指针域
    struct student* next;//指向下一位的指针
}Student;

图解

 

与数组的不同 

单链表

  单链表是作为最简单的一种线性表的数据结构,通过动态空间分配可以实现单链表的创建,下面我们就学习单链表的创建以及增删改查等方法。

1. 创建节点

这里我们就不去按照之前学习链表时的做法了,之前我们是一次性去创建节点的同时还把节点连接起来,这里我们就把这两个步骤分开来做,让代码更加灵活。 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct date{
    long num;//学号
    char name[20];//名字
    int score;//分数
}Date;//存放数据
typedef struct student{
    //数据域
    Data x;
    //指针域
    struct student* next;//指向下一位的指针
}Student;


//创建节点
Student* create_node(Date x) {
    Student* node = (Student*)malloc(sizeof(Student));
    //数据赋值
    node->x = x;
    //指针域指向空
    node->next = NULL;
    //返回这个节点
    return node;
}

2.插入节点

尾插节点(形成链表结构)

//把节点连接起来
void create_studentlist(Student**head,Date x) {
    Student* new_node = create_node(x);
    //如果头指针指向空节点的话,那么将头指针指向这个新的节点
    if (*head == NULL) {
        *head = new_node;
    }
    //如果头指针指向一个不为空的节点的话,那么就要顺着这个链表找到最后的节点,把新节点放入
    else {
        Student* move=*head;//创建一个移动节点,找到最后一个节点的位置
        while (move->next != NULL) {
            move = move->next;  //一直往后移
        }
        move->next = new_node;//把新节点放入进去
    }
}

向指定位置插入节点(链表已有)

//指定位置插入节点
void insert_node(Student** head, Student *new_node,int n) //向链表第n个位置插入节点
{
    Student* move=*head;
    //通过循环找到这个节点
    for (int i = 0; i < n; i++)
    {
        move = move->next;
    }
    //下面这两步的顺序千万不能反过来!!!
    new_node->next = move->next;
    move->next = new_node;
}

3.遍历链表数据

//输出数据
void listprint(Student**head)
{
    Student* cur=*head;
    while (cur != NULL) {
        printf("%ld %s %d\n", cur->x.num, cur->x.name, cur->x.score);
        cur = cur->next;
    }
    printf("output over!");
}

4.获取链表长度

//获取链表长度
int get_listlength(Student**head)
{
    int length=0;
    Student* cur = *head;
    while (cur != NULL)
    {
        length++;
        cur = cur->next;
    }
    return length;//返回长度的大小
}

5.删除节点

删除节点是链表的基本操作之一,分为删除尾节点和删除指定的节点(不是尾节点),这两种情况要去分类讨论,下面请看代码案例:

删除尾节点

//删除尾节点
void pop_lastnode(Student** head)
{
    //如果没有节点,就直接返回
    if (*head == NULL)
        return;
    //如果只有一个节点的话,就直接删除
    else if ((*head)->next == NULL)
    {
        free(*head);
        *head = NULL;//重新置空
    }
    /*如果有多个节点的话,要找到最后一个节点的上一个节点,删除掉最后一个节点
    然后再把这个节点的next指向空*/
    else{
        Student* move = *head;
        while (move->next->next!=NULL)
        {
            move = move->next;
        }
        free(move->next);
        move->next = NULL;
    }
}

删除指定节点

//删除指定节点
void pop_node(Student** head, int n)//删除第n个节点
{
    Student* move = *head;
    Student* p;
    //通过循环依次去找到要删除节点的上一个节点
    for (int i = 0; i < n-1; i++)
    {
        move = move->next;
    }
    p = move->next;//此时要删除的节点标记为p节点
    move->next = p->next;//让p节点的上一个节点指向p节点的下一个节点,此时p节点已经断链
    free(p);
}

6.清空链表

//清空链表,释放全部的空间
void clear_list(Student**head){
    Student* cur=*head;
    Student* next;
    while (cur != NULL) {
        next = cur->next;
        free(cur);
        cur = next;
    }
    *head = NULL;//头指针置空
    printf("clear over!\n");
}

7.链表的数据查找

//数据查找

//按学号查找
Student* list_search_num(Student** head, int num) {
    Student* cur = *head;
    while (cur!=NULL) {
        if (cur->x.num == num) { 
            return cur; //找到了就返回这个节点
        }
        else
            cur = cur->next;
    }
    printf("No find");
    return NULL;
}

//按名字查找
Student* list_search_name(Student** head, char *name) {
    Student* cur = *head;
    while (cur != NULL) {
        if (strcmp(cur->x.name,name)==0) {
            return cur;   //找到了就返回这个节点
        }
        else
            cur = cur->next;
    }
    printf("No find");
    return NULL;
}

8.链表的转置

我们已有的链表顺序是: 1->2->3->4->5  现在我想让这个链表顺序变为5->4->3->2->1(如下图所示)   这个过程就是链表的转置,我们可以去通过头插法去实现

头插法步骤如下: 

//链表的转置(头插法)
void list_reverse(Student** head) {
    Student *L, *start,* p, * q;
    //开辟一个暂存节点L
    L = (Student*)malloc(sizeof(Student));
    //初始化
    start = *head;
    L->next = start;
    p = L->next;
    L->next = NULL;
    while (p!= NULL) {
        q = p->next;
        p->next = L->next;
        L->next = p;
        p = q;
    }
    *head = L->next;//将此时的头结点等于新的头结点
    free(L);//释放掉暂存节点
    printf("reverse over!\n");
}

 测试集

//测试结果
int main()
{
    Date a[4] = { {1,"xiaoming",90} ,{ 2,"Jack",80 } ,{ 3,"Amy",98 } ,{ 4,"John",77 } };

    Student* head = NULL;//实例化一个头节点指针
    for (int i = 0; i < 4; i++) {
        //要把实例化的头指针的地址存入进去
        create_studentlist(&head, a[i]);//依次成链
    }
    listprint(&head);//输出数据
    printf("当前节点数量: %d\n", get_listlength(&head));
    list_reverse(&head);//逆转链表
    listprint(&head);//输出数据
    pop_lastnode(&head);//删除最后一个节点
    printf("当前节点数量: %d\n", get_listlength(&head));
    listprint(&head);//输出数据
    clear_list(&head);//清空链表
    printf("当前节点数量: %d\n", get_listlength(&head));

}
//输出如下:
/*1 xiaoming 90
2 Jack 80
3 Amy 98
4 John 77
output over!
当前节点数量: 4
reverse over!
4 John 77
3 Amy 98
2 Jack 80
1 xiaoming 90
output over!
当前节点数量: 3
4 John 77
3 Amy 98
2 Jack 80
output over!
clear over!
当前节点数量: 0
*/

 环形链表

链表,从名字上来看是一条数据链,一般的链表其末尾节点是没有指向的,但当把链表的末尾节点指定为指向头节点时,则构成了一个环形链表。所以要想去建立一个环形链表只需要把最后一个节点重新指向头节点就行了,或者指向其他节点可以形成局部环形链表。下面我就讲讲通过代码去创建环形链表,相关操作方法(增删改查)跟上面所说的基本一样。

创建环形链表 

#include<stdio.h>
#include<stdlib.h>
typedef struct node {
	int date;
	struct node* next;
}Node;
//创建节点
Node *create_node(int date) {
	Node* node = (Node*)malloc(sizeof(Node));
	node->date = date;
	node->next = NULL;
	return node;
}
//把节点连起来
void create_annuallist(Node** head,int date) {
	Node* new_node = create_node(date);
	if (*head == NULL) {
		*head = new_node;
	}
	else
	{
		Node* cur = *head;
		while (cur->next) {
			cur = cur->next;
		}
		cur->next = new_node;
	}//成链
	//成环
	new_node->next = *head;
}

当你仔细细看的话,前面跟链表的创建基本上没什么区别,实际上就在最后面加上一个尾节点指向头节点就是了!

双向循环链表

带头双向循环链表是链表当中结构最复杂的链式结构,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个链表虽然结构复杂,但是使用代码实现以后会发现能带来很多优势。

创建双向循环链表

实现思路:

要想创建双向循环链表,就意味着在单相链表的基础上增加一个前指针来指向上一个节点,然后在成链的过程中后一个节点要指向前一个节点,前一个节点指向后一个节点,最后头尾节点互相指向就可以形成双向循环了。

代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//创建双向循环链表
typedef struct node {
	struct node* prev; //前指针
	char date[10];//数据域
	struct node* next; //后指针
}D_node;
D_node* create_D_node(char*date) {
	D_node* new_node = (D_node*)malloc(sizeof(D_node));
	strcpy(new_node->date, date);
	//前后指针都指向空,初始化
	new_node->prev = NULL;
	new_node->next = NULL;
	return new_node;
}
void create_D_list(D_node** head, char* date) {
	D_node* new_node = create_D_node(date);
	if (*head == NULL) {  //判断头结点是否为空
		*head = new_node;
	}
	else
	{
		D_node* cur = *head;
		while (cur->next) {
			cur = cur->next;
		}
		//双向指向(前指向后,后指向前)
		new_node->prev = cur;
		cur->next = new_node;
	}
	//头尾指针互相指向,成环
	new_node->next = *head;
	(*head)->prev = new_node;
}

 好了,以上就是本期的全部内容了,我们下一期再见!

分享一张壁纸:

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

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

相关文章

51单片机项目(10)——基于51单片机的电压计

本次设计的电压计&#xff0c;使用ADC0832芯片&#xff0c;测到电压后&#xff0c;将电压信息发送到串口进行显示。仿真功能正常&#xff0c;能够运行。&#xff08;工程文件和代码放在最后&#xff09; 电路图如下&#xff1a; 运行过程如下&#xff1a; ADC0832介绍&#xff…

linux下检测CPU性能的mpstat命令安装与用法

1、安装命令 $ sudo apt-get install sysstat sysstat安装包还包括了检测设备其它状态的命令&#xff0c;查看命令如下&#xff1a; 2、检测CPU命令语法 $ mpstat --h //查看mpstat的语法 Usage: mpstat [ options ] [ <interval> [ <count> ] ] Options are: …

设计模式之访问器模式(Visitor)的C++实现

1、访问器模式的提出 在软件开发过程中&#xff0c;早已发布的软件版本&#xff0c;由于需求的变化&#xff0c;需要给某个类层次结构增加新的方法。如果在该基类和子类中都添加新的行为方法&#xff0c;将给代码原有的结构带来破坏&#xff0c;同时&#xff0c;也违反了修改封…

D. Sorting By Multiplication

Problem - D - Codeforces 思路&#xff1a;我们首先考虑当只能乘以正数时&#xff0c;那么变为单调增的方法就是找所有w[i]>w[i1]的对数&#xff0c;因为如果存在一个w[i]>w[i1]&#xff0c;那么我们一定至少需要进行一次操作&#xff0c;并且我们还知道我们进行一次操…

Redis经典问题:缓存穿透

&#xff08;笔记总结自《黑马点评》项目&#xff09; 一、产生原因 用户请求的数据在缓存中和数据库中都不存在&#xff0c;不断发起这样的请求&#xff0c;给数据库带来巨大压力。 常见的解决方式有缓存空对象和布隆过滤器。 二、缓存空对象 思路&#xff1a;当我们客户…

JP《乡村振兴振兴战略下传统村落文化旅游设计》许少辉书香续,山水长

JP《乡村振兴振兴战略下传统村落文化旅游设计》许少辉书香续&#xff0c;山水长

MySQL--MySQL表的增删改查(基础)

排序&#xff1a;ORDER BY 语法&#xff1a; – ASC 为升序&#xff08;从小到大&#xff09; – DESC 为降序&#xff08;从大到小&#xff09; – 默认为 ASC SELECT … FROM table_name [WHERE …] ORDER BY column [ASC|DESC], […]; *** update

【数据结构--顺序表】合并两个有序数组

题目描述&#xff1a; 代码实现&#xff1a; void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){int x0;if(m0)//如果nums1为空&#xff0c;而nums2不为空&#xff0c;则将nums2拷贝至nums1{while(nums1Size--){nums1[x]nums2[x];x;}}if(n0)//…

深入学习 GC 算法 - 标记清除算法

前言&#xff1a; &#x1f4d5;作者简介&#xff1a;热爱编程的小七&#xff0c;致力于C、Java、Python等多编程语言&#xff0c;热爱编程和长板的运动少年&#xff01; &#x1f4d8;相关专栏Java基础语法&#xff0c;JavaEE初阶&#xff0c;数据库&#xff0c;数据结构和算法…

【蓝凌表单】如何限制明细表字段1与字段2一致时不允许提交

无需开发&#xff0c;表单内置功能快速解决&#xff1b; 有些搞笑&#xff0c;维护蓝凌系统好几年&#xff0c;对系统好多功能也不是很熟悉&#xff0c; 当接到业务需求&#xff0c;不允许某信息跟某信息一致的需求时&#xff0c;第一时间是想到用JS脚本去实现&#xff0c;忽略…

机器学习实战-系列教程3:手撕线性回归2之单特征线性回归(项目实战、原理解读、源码解读)

&#x1f308;&#x1f308;&#x1f308;机器学习 实战系列 总目录 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 手撕线性回归1之线性回归类的实现 手撕线性回归2之单特征线性回归 手撕线性回归3之多特征线性回归 手撕线性回归4之非线性回归# 5…

0-1背包-动态规划

一、01背包 描述&#xff1a;有 N 件物品和一个容量为 V 的背包&#xff0c;每件物品只能使用一次 第 i 件物品的体积是 Ci&#xff0c;价值是 Wi 求解将哪些物品装入背包&#xff0c;能够在不超过背包容量的情况下使总价值最大 求解&#xff1a;动态规划 使用dp[i][j]表示从…

zabbix监控H3C设备

背景 常见的服务和主机已经使用Prometheus进行监控了&#xff0c;但是网络设备还未配置监控。使用基于SNMP对网络设备进行监控。 设备概览 主要类型为H3C的路由器和交换机。H3CS5560交换机 路由器MER5200 er8300 步骤 配置网络设备开启telnet远程&#xff1b; 配置启用sn…

nodejs采集淘宝、天猫网商品详情数据以及解决_m_h5_tk令牌及sign签名验证(2023-09-09)

一、淘宝、天猫sign加密算法 淘宝、天猫对于h5的访问采用了和APP客户端不同的方式&#xff0c;由于在h5的js代码中保存appsercret具有较高的风险&#xff0c;mtop采用了随机分配令牌的方式&#xff0c;为每个访问端分配一个token&#xff0c;保存在用户的cookie中&#xff0c;通…

SAP-MM-销售订单库存转移到普通库存

业务需求&#xff1a; 特殊库存-销售订单库存 有产成品物料1个&#xff0c;现在需要在集团下的两个公司间调拨&#xff0c;需要把特殊库存E调拨到普通库存里&#xff0c;再从H020普通库存调拨到另一个工厂1000. 注意事项&#xff1a;库存地点需要扩充&#xff0c;否则调拨会报…

iOS 17新功能:教你轻松掌握锁定屏幕快捷方式

通过iOS 17&#xff0c;苹果为iPhone用户提供了使用快捷方式锁定手机屏幕的能力。 为什么你需要学习如何使用iOS锁定屏幕快捷方式&#xff1f;按下iPhone上的电源按钮激活这个屏幕肯定是最简单的吗&#xff1f;嗯&#xff0c;这并不总是正确的。如果你在按下物理按钮时遇到困难…

【2023知乎爬虫】批量获取问题的全部回答

一.需求 爬取任意问题下的所有回答&#xff0c;如下图&#xff1a; 1.根据问题&#xff0c;批量获取问题下的所有回答、与对应问题的关系到answer.csv文件&#xff1b; 2.保存当前问题基本信息到quesiton_info.csv文件&#xff1b; 二.展示爬取结果 三.讲解步骤 3.1 新建项…

个人开发者看过来,我搭了一个监控系统免费用

最近在做一个自己的项目&#xff0c;平时就在自己电脑上跑着&#xff0c;有一天回去突然就挂了&#xff0c;查了半天也没搞清楚原因&#xff0c;想看个监控都没有&#xff0c;什么时候挂的&#xff0c;为啥挂了&#xff0c;统统都不知道。平时做公司项目多了&#xff0c;监控用…

C/C++操作加密与不加密的zip文件

为了后续的方便操作zip文件&#xff0c; 将所有的操作封装成了一个动态库了。 /*** \description 从压缩包文件中解压出指定的文件到指定的目录.* \author sunsz* \date 2023/09/09**/ LIBZIP_API int UnpackFile(const char* password, char zipfilename[], char filename_…

rt-thread------任务调度

rt-thread------任务调度 1. 线程初始化 在rt-thread中线程主要包括以下一些内容&#xff0c;线程控制块、线程栈、函数入口。 1.1线程创建函数 RTOS基本都包括两种线程方式&#xff1a;动态创建rt_thread_create()和静态创建rt_thread_init()。 因为有些系统设计时对安全…