草庐IT

带头结点的单链表

君有云 2023-03-28 原文

带头结点的单链表

  与顺序表相同,链表也是一种线性表。与顺序表不同的是,链表的物理存储结构是用一组地址任意的存储单元存储数据。它不像顺序表那样需要占据一段地址连续存储空间,而是将存储单元分散在内存的任意地址上。

  在链表结构中,每个数据元素都存放在链表中的一个结点(node)上,而每个结点之间通过指针将其连接起来,这样就形成了一条如“链”的结构。

  在C程序中,链表中的每个结点都可以用结构体来表示。在链表的每个结点中都必须有一个存放指针(地址)的域,也叫做指针域,指针域用来保存后继结点的地址,这样就将每个结点连接在一起,形成一条链。

  一个链表中通常有一个表头,表头的用来保存第一个结点的地址。一个链表的最后一个结点的指针域要置空(NULL),因为它没有后继结点。

单链表的特点

  • 除“第一个”数据元素外,其余数据元素有且仅有一个 前驱元素 prev,

  • 除“最后一个”数据元素外,其余数据元素有且仅有一个 后继元素 next

  • 单链表的每个结点只有一个指针域

结点:每一个结点都包含两个部分:数据域指针域。数据域用于存放数据元素,指针域用于存放后继结点的地址。

头指针:无论链表是否为空,头指针均不为空,头指针是链表的必要元素,头指针指向表头结点
头结点:放在第一个元素结点之前,其数据域一般无意义(当然也可以用来存放链表的长度等)
首元结点:第一个元素的结点,它是表头结点之后的第一个结点。

  带头结点的单链表在后期的维护中更方便,头结点与其他结点一样拥有数据域指针域,头结点的数据域可以不存储任何信息,但也可以存储如链表长度等附加信息。 头结点的指针域用于保存首元结点的地址。带头结点的单链表的相关代码如下。


带头结点的单链表操作


单链表元素结点

单链表的结点(LNode)通常使用结构体表示。

typedef struct node
{
    //结点数据域
    int data;
    //结点指针域
    struct node *next;
}LNode;

单链表头结点

单链表的头结点(HNode)也使用结构体表示,头结点的指针域保存首元结点的地址,所以其指针域的类型需要与元素结点类型相同。

typedef struct head
{
    //记录链表长度
    int len;
    LNode *next;
}HNode;

创建带头结点的单链表

创建一个带头结点的单链表,并返回一个指向单链表头结点的头指针。头指针用来标识单链表,保存单链表的入口地址。

/***************************************************************
函数功能: 创建带头结点的单链表
入口参数: 无
返回值: head(单链表头指针)
****************************************************************/
HNode *create_linked_list(void)
{
    //创建一个头指针
    HNode*head = NULL;
    //创建一个头结点,并将头指针指向头结点
    head = (HNode*)malloc(sizeof(HNode));
    head->len = 0;
    head->next = NULL;
    return head;
}

打印单链表

打印单链表的所有元素。

/***************************************************************
函数功能: 打印带头结点的单链表
入口参数: head(单链表头指针))
返回值:  无
****************************************************************/
void print_linked_list(HNode*head)
{
    LNode *list = NULL;
    list = head->next;
    while(list)
    {
        printf("%d ", list->data);
        list = list->next;
    }
    printf("\n");
}

例如:

单链表:1 2 3 4 5 6 7 8 9

输出:1 2 3 4 5 6 7 8 9


尾插法

在单链表最后一个结点之后插入一个新结点,并将新插入的结点的指针域置NULL,单链表最后一个结点没有后继节点。

/***************************************************************
函数功能: 在单链表最后一个结点之后插入一个结点
入口参数: 
        参数1:head(单链表头指针)
        参数2:x(需要保存的元素数据)
返回值: head(单链表头指针)
****************************************************************/
HNode *add_linked_list_last(HNode *head,int x)
{
    LNode *last_node = NULL;
    last_node = head->next;
    //判断是否为最后一个结点
    while(last_node->next)
    {
        last_node=last_node->next;
    }
    //申请开辟一个结点空间
    LNode *new_node = (LNode*)malloc(sizeof(LNode));
    new_node->data = x;
    new_node->next=NULL;
    //将结点插入最后
    last_node->next = new_node;
    last_node = new_node;
    head->len++;//链表长度加1
    return head;
}

例如:

单链表:1 2 3 4 5 6 7 8 9

插入10后:1 2 3 4 5 6 7 8 9 10


头插法

在单链表首元结点之前插入一个新结点,并将头结点的指针指向新结点。

/***************************************************************
函数功能: 在单链表首元结点之前插入一个结点
入口参数: 
        参数1:head(单链表头指针)
        参数2:x(需要保存的元素数据)
返回值: head(单链表头指针)
****************************************************************/
HNode *add_linked_list_first(HNode *head,int x)
{
    LNode *new_node = NULL;
    //申请开辟一个结点空间
    new_node = (LNode*)malloc(sizeof(LNode));
    new_node->data = x;//保存需要插入的元素
    new_node->next = NULL;
    //将新结点插入到第一个结点之前
    new_node->next = head->next;
    head->next = new_node;
    head->len++;
    return head;
}

例如:

单链表:2 4 7 8 9 1

插入11后:11 2 4 7 8 9 1


删除单链表中第N个结点

删除单链表中的第N个结点,需要将第N-1个结点的后继指针指向第N+1个结点,然后将第N个结点的后继指针置NULL,然后再释放第N个结点的空间。

/***************************************************************
函数功能: 删除单链表中的第N个结点
入口参数: 
        参数1:head(单链表头指针)
        参数2:n(第N个结点)
返回值: head(单链表头指针)
****************************************************************/
HNode *delete_theN_node(HNode *head,int n)
{
    //判断n是否大于链表长度
    if (n > head->len || n < 1)
    {
        printf("该结点不存在!\n");
        return 0;
    }
    LNode *now_node = NULL;
    LNode *prev_node = NULL;
    now_node = head->next;
    //删除第一个结点
    if (1 == n)
    {
        head->next = now_node->next;
        now_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
    }
    //删除最后一个结点
    else if(head->len == n)
    {
        while(--n)
        {
            prev_node = now_node;
            now_node = now_node->next;
        }
        prev_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
    }
    //删除中间某个结点
    else
    {
        while(--n)
        {
            prev_node = now_node;
            now_node = now_node->next;
        }
        prev_node->next = now_node->next;
        now_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
    }
    return head;
}

例如:

单链表:2 4 7 8 9 1

删除第2个结点后: 2 7 8 9 1


删除单链表中元素为X的结点

前提:单链表中不包含重复元素。

/***************************************************************
函数功能: 删除单链表中元素为X的结点(单链表中不包含重复元素)
入口参数: 
        参数1:head(单链表头指针)
        参数2:x(删除的元素数据)
返回值: head(单链表头指针)
****************************************************************/
HNode *delete_num(HNode *head,int x)
{
    LNode *now_node = NULL;
    LNode *prev_node = NULL;
    now_node = head->next;

    //判断X是不是第一个结点的元素
    if(x == now_node->data){
        head->next = now_node->next;
        now_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
        return head;
    }
    //判断中间
    while(now_node->next)
    {
        if(x == now_node->data){
            prev_node->next = now_node->next;
            now_node->next = NULL;
            free(now_node);
            now_node = NULL;
            head->len--;
            return head;
        }
        else{
            prev_node = now_node;
            now_node = now_node->next;
        }
    }
    //判断最后一个结点的元素是否为X
    if(x == now_node->data){
        prev_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
    }
    else{
        printf("链表中无%d这个元素\n",x);
        return 0;
    }
    return head;
}

例如:

单链表:2 4 7 8 9 1

删除元素9后:2 4 7 8 1


删除单链表中连续重复的元素只保留一个

/***************************************************************
函数功能: 删除单链表中的重复元素
入口参数: head(单链表头指针)
返回值: head(单链表头指针)
****************************************************************/
HNode *delete_repetitive_num(HNode*head)
{
    LNode *now_node = NULL;
    LNode *prev_node = NULL;
    now_node = head->next;

    while(now_node->next){
        prev_node = now_node;
        now_node = now_node->next;
        //判断相邻两个元素是否相等
    if(prev_node->data == now_node->data){
            prev_node->next = now_node->next;
            now_node->next = NULL;
            free(now_node);
            now_node = prev_node;
        }
    }
    return head;
}

例如:

单链表:1 1 2 2 3 3 3 5 5

删除后:1 2 3 5


销毁单链表

销毁除头结点之外的结点。

/***************************************************************
函数功能: 销毁除头结点之外的结点
入口参数: head(单链表头指针))
返回值: head(单链表头指针)
****************************************************************/
HNode *destroy_linked_list(HNode *head)
{
    LNode *now_node = NULL;
    //从第一个元素结点开始销毁
    while(head->next){
        now_node = head->next;
        if(now_node->next == NULL){
            head->next = NULL;
            free(now_node);
            now_node = NULL;
            head->len--;
            break;
        }
        head->next = now_node->next;
        now_node->next = NULL;
        free(now_node);
        now_node = NULL;
        head->len--;
    }
    return head;
}

单链表逆置

说明:此逆置不改变结点原来的位置,只交换两个结点存储的数据。

/***************************************************************
函数功能: 逆置单链表(交换元素数据)
入口参数: head(单链表头指针)
返回值: head(单链表头指针)
****************************************************************/
HNode *reverse_linked_list(HNode *head)
{
    int n = head->len;
    int temp;
    int i,j;
    printf("链表长度:%d\n",n);
    LNode *first_node = NULL;
    LNode *temp1_node = NULL;
    LNode *temp2_node = NULL;

    first_node = head->next;
    temp1_node = first_node;
    //如果链表元素个数为奇数则(n-1)%2,反之n%2
    if(n%2 == 0||(n-1)%2==0)
    {        
        //不管是奇数个元素还是偶数个元素,都只需要交换n/2次
        //奇数个元素其最中间那一个元素不需要交换
        for(i=0;i<n/2;i++)
        {
            temp2_node = first_node;
            for(j=0;j<n-i-1;j++)
            {
                temp2_node = temp2_node->next;
            }
            temp = temp1_node->data;
            temp1_node->data = temp2_node->data;
            temp2_node->data = temp;
            //指向第一个结点的指针往后移动一个
            temp1_node = temp1_node->next;
        }
    }
    return head;
}

例如:

单链表:3 2 4 5 10 8 1

逆置后:1 8 10 5 4 2 3


将两个链表归并为一个有序链表

/***************************************************************
函数功能: 将两个链表归并为一个有序链表
入口参数: 
        参数1:head1(单链表头指针)
        参数2:head2(单链表头指针)
返回值: head3(单链表头指针)
****************************************************************/
HNode *combine_linked_list(HNode*head1,HNode*head2)
{
    HNode *head3 = NULL;
    LNode *now_node = NULL;
    LNode *next_node = NULL;
    int flag = 0;
    int temp;
    int n;
    now_node = head1->next;
    //找head1的最后一个结点
    while(now_node->next){
        now_node = now_node->next;
    }
    //连接head1和head2
    now_node->next = head2->next;
    //合并
    head3 = head1;
    head3->len = (head1->len) + (head2->len);
    n = head3->len;

    //冒泡排序
    for(int i=0;i<n-1;i++)
    {
        now_node = head3->next;
        next_node = now_node;
        for(int j=0;j<n-1-i;j++)
        {
            next_node = next_node->next;
            if(now_node->data > next_node->data)
            {
                temp = now_node->data;
                now_node->data = next_node->data;
                next_node->data = temp;
                flag = 1;
            }
            now_node = now_node->next;
        }
        if(flag == 0){
            break;
        }
    }
    return head3;
}

例如:

单链表1:5 4 3 2 1

单链表2:9 6 8 7

归并后:1 2 3 4 5 6 7 8 9


更新第N个结点的数据

/***************************************************************
函数功能: 修改第N个结点的数据
入口参数: 
        参数1:head(单链表头指针)
        参数2:n(第N个结点)
        参数3:更新的数据
返回值: head3(单链表头指针)
****************************************************************/
HNode *change_theN_node(HNode *head,int n,int x)
{
	//判断n是否大于链表长度
	if (n > head->len || n < 1)
	{
		printf("该结点不存在!\n");
		return 0;
	}
	LNode *now_node = NULL;
	now_node = head->next;
	while(--n)
	{
		now_node = now_node->next;
	}
	now_node->data = x;
	return head;
}

例如:

单链表:5 4 3 2 1

更新第2个结点数据为12:5 12 3 2 1


查找值为X的元素

/***************************************************************
函数功能: 查找值为X的元素
入口参数: 
        参数1:head(单链表头指针)
        参数2:x(查找的值)
返回值: 0 表示不存在,1表示存在
****************************************************************/
int find_data_linked_list(HNode *head,int x)
{
	LNode *now_node = NULL;
	now_node = head->next;
	while(now_node){
		if(now_node->data == x){
			return 1;
		}
		now_node = now_node->next;
	}
	return 0;
}

主函数参考

int main(int argc, char const *argv[])
{
	int num;
	int n;
	//创建一个头指针用于标识链表
	HNode *phead = NULL;
	//创建一个带头结点的单链表
	phead = create_linked_list();

	LNode*new_node = NULL;
	LNode*last_node = NULL;

	printf("请向单链表输入数据:");
	do{
		scanf("%d",&num);
		new_node = (LNode*)malloc(sizeof(LNode));
		new_node->data = num;
		new_node->next = NULL;
		if (phead->next == NULL)
		{
			phead->next = new_node;
			last_node = new_node;
			phead->len++;
		}
		else
		{
			last_node->next = new_node;
			last_node = new_node;
			phead->len++;
		}

	}while(getchar() != '\n');
	//打印链表所有元素
	printf("链表长度:%d\n",phead->len);
	printf("链表所有元素:");
	print_linked_list(phead);
    
    //尾插法
    
    //头插法
	
	//删除第N个结点
	
	//查找数据
	
	return 0;
}

注意:该文章仅供学习交流不得转载商用。

有关带头结点的单链表的更多相关文章

  1. 【数据结构和算法】实现带头双向循环链表(最复杂的链表) - 2

    前文,我们实现了认识了链表这一结构,并实现了无头单向非循环链表,接下来我们实现另一种常用的链表结构,带头双向循环链表。如有仍不了解单向链表的,请看这一篇文章(7条消息)【数据结构和算法】认识线性表中的链表,并实现单向链表_小王学代码的博客-CSDN博客目录前言一、带头双向循环链表是什么?二、实现带头双向循环链表1.结构体和要实现函数2.初始化和打印链表3.头插和尾插4.头删和尾删5.查找和返回结点个数6.在pos位置之前插入结点7.删除指定pos结点8.摧毁链表三、完整代码1.DSLinkList.h2.DSLinkList.c3.test.c总结前言带头双向循环链表,是链表中最为复杂的一种结

  2. c++(1): c++单链表的创建、查找、插入、删除 - 2

    1.单链表单链表是多个节点通过指针串联起来的线性结构,每个节点分为两部分,一个是数据域,一个为指针域,头节点的数据域为空,最后一个节点的指针域胃为空,链表的前一个节点的指针域,存放的是下一个节点的地址。数据域:存放数据;指针域:指向下一个节点的指针。头节点的作用:为了方便操作整个链表,它并不保存具有实际意义的数据。创建链表的步骤(1)构建节点计算机中没有现成的节点,我们需要自己创建它。任意的节点都包含了两部分:左边部分data存储数据,右边部分next存储指针,就是下一个节点的地址。data中可以存放任意数据,包括int,float,double等,可以存放单个数据,也尅存放多个数据。例子构建

  3. go - 插入一个简单的单链表 - 2

    我正在尝试解决Go中的leetcode问题来自学这门语言。我有一个单链表和一个插入函数:typeListNodestruct{ValintNext*ListNode}funcInsert(listNode*ListNode,iint){//@fixmehowtocheckthefirstnode?iflistNode==nil{listNode.Val=ilistNode.Next=nil}else{for;;listNode=listNode.Next{iflistNode.Next==nil{listNode.Next=&ListNode{i,nil}break}}}}funcma

  4. c# - WCF 终结点 : Message. WriteMessage 更改 XML 消息内的结束标记 - 2

    我使用自定义端点行为扩展来拦截消息,因为它们是由我的WCF服务端点接收的,使用IDispatchMessageInspector。我像这样检索消息内容:publicobjectAfterReceiveRequest(refMessagerequest,IClientChannelchannel,InstanceContextinstanceContext){MessageBuffermessageBuffer=request.CreateBufferedCopy(Int32.MaxValue);Messagemessage=messageBuffer.CreateMessage();u

  5. 数据结构体进阶链表【带头双向循环链表,单向链表的优化,从根部解决了顺序表的缺点】一文带你深入理解链表 - 2

     前言:  对于链表,上一篇的单链表解决了顺序表的一部分缺陷,但并没有彻底的解决顺序表的问题,比如在进行单链表尾插尾删的时候还是需要进行遍历找尾,并没有达到全部的O(1),并且在头插的时候还要分情况来考虑,比如传入为空指针和不是空指针时候还要分情况考虑,对于指针的改变还要传二级指针,这对于一部分人来说并不熟悉,所以!!!今天看完这篇文章,掌握带双向循环数据表,让我们不再害怕链表的增删插改😎😎   💞💞   欢迎来到小马学习代码博客!!!!          思维导图:目录一、链表实现前的准备 💜1.1结构图:💜1.2初步的理解:二、带头双向链表功能实现前的准备🤎 2.1链表实现所需要的头文件:

  6. 【单链表】的增删查改 - 2

    🖊作者:Djx_hmbb📘专栏:数据结构😆今日分享:“Oncinablumoon”:“罕见的,千载难逢的”(出现在19世纪,指的是"在一个月内出现的第二次圆月”,这种现象每隔32个月发生一次。)文章目录✔单链表的功能实现:🔎申请一个结点空间:🔎构建n个链表:🔎打印链表:🔎尾插:🔎尾删:🔎头插:🔎头删:🔎查找:🔎在pos位置后插入x:🔎在pos位置前插入x:🔎删除pos位置后一个指针:🔎删除pos位置的指针:🔎释放空间:✔头文件(详情):✔测试文件(详情):家人们,点个![请添加图片描述](https://img-blog.csdnimg.cn/11dae7d2dd1b46b2b021edacc

  7. c++ - 当 SLIST_ENTRY 不是项目列表的第一个成员时使用单链表 - 2

    这是来自MSDN的代码(使用单链表):typedefstruct_PROGRAM_ITEM{SLIST_ENTRYItemEntry;ULONGSignature;/*MYDATA*/}PROGRAM_ITEM,*PPROGRAM_ITEM;intmain(){ULONGCount;PSLIST_ENTRYpFirstEntry,pListEntry;PSLIST_HEADERpListHead;PPROGRAM_ITEMpProgramItem;pListHead=(PSLIST_HEADER)_aligned_malloc(sizeof(SLIST_HEADER),MEMORY_A

  8. c# - 在 WCF 服务中公开 webHttpBinding 终结点 - 2

    我创建了一个WCF服务并公开了三个端点,它们是basicHttpBinding、wsHttpBinding和webHttpBinding。这是我使用WCF进行实验的测试服务。但是,每当我使用.svc文件添加服务引用时,我只会得到两个(基本和ws)端点。由于某种原因,似乎没有公开第三个(webHttpBidning)端点。要重现此问题,请创建一个WCF应用程序项目,删除Service1服务,添加名为TestService的新项目>WCF服务,并将配置文件更改为以下内容:这里是ITestService.cs的代码:[ServiceContract]publicinterfaceITestS

  9. c# - 找不到终结点 - WCF Web 服务 - 2

    我已经为我的WCF服务创建了2个端点。它在basicHttpBinding上工作正常,但在webHttpBinding上会导致错误。错误=未找到端点。操作合约定义[OperationContract][WebInvoke(Method="POST",BodyStyle=WebMessageBodyStyle.WrappedRequest,ResponseFormat=WebMessageFormat.Json)]VINDescriptionCallADSWebMethod(stringvin,stringstyleID);web.config:请建议我如何解决这个问题?

  10. 【Kubernetes 系列】一文带你吃透 K8S 应用pod结点 - 2

    作者:半身风雪上一节:创建K8s集群项目简介:上一节我们一起学习了,如何去部署一个K8S的应用程序,这一节,我们主要讲解一下,K8S应用的框架结构。K8S应用pod结点目标一、KubernetesPods1.1、Kubernetes中的pod是做什么的二、工作结点三、故障排除3.1、常见kubectl命令3.2、可视化界面四、pod资源详情总结目标本节我将和大家一起学习Kubernetes应用中的pod结点了解KubernetesPod。了解Kubernetes工作节点。对已部署的应用故障排除。一、KubernetesPods在上一节中,我们一起学会了如何使用kubectl创建一个应用。这里我

随机推荐