链表--头插与尾插实现队列和栈

本文介绍了链表的基本概念,包括其非连续存储的特性,节点由数据域和指针域组成。进一步讨论了队列和栈这两种特殊的线性表,队列遵循先进先出(FIFO)原则,而栈则是先进后出(FILO)。通过实例展示了如何使用头插法和尾插法创建链表,并提供了链表释放内存的实现。此外,还分别演示了头插法实现的栈和尾插法实现的队列的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表

链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.
链表中的数据是以节点来表示的.节点是由数据域(data) ,以及指针域(next) 构成.数据域存放数据, 指针域存放的是下一个节点的地址.
链表在内存中存储方式是非连续的,非顺序的.
在这里插入图片描述
链式存储结构如下:

typedef struct node_s
{
        int 			data;
        struct node_s  	*next;
} node_t;

队列与栈

队列 :只允许在表的前端进行删除操作,在表的后端进行插入操作的线性表.
栈 :只能在某一端插入和删除的特殊线性表.
简单来说 ,队列是数据先进先出(first in first out,FIFO), 栈是先进后出(first in last out,FILO).

头插法与尾插法

头插与尾插, 简单来说, 新malloc一个节点, 这个节点是插入在头部还是尾部.
在这里插入图片描述

链表的free

在这里插入图片描述

通过建立一个新指针 freenode, 让freenode 等于 *head(即head指向的节点), 让head等于第二个节点. 这样 就把我们要释放的节点断开, 并保证之后的节点处于链接状态. 不丢失数据. 我们此时释放freenode, 并将freenode 指针指向的空间赋值为NULL.

free函数的实现

void Free(node_t **head)
{
        node_t  *freenode=NULL;
        freenode = *head;

        while( *head!=NULL )
        {
                freenode = *head;
        		*head = (*head)->next;
                free(freenode);
        		freenode = NULL;
                printf("%p\n", freenode);	//打印地址来判断释放内存是否完全成功, 即看打印次数
        		printf("bye\n");
        }
                return ;
}

通过尾插法实现队列

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void Free(node_t **head);

typedef struct node_s
{
        int data;
        struct node_s   *next;
} node_t;

int main (int argc, char **argv)
{ 
        node_t          *head = NULL;
        node_t          *new_node;
        node_t          *tail;
        int             i;

		//for循环实现尾插法
        for(i=0; i<10; i++)
        {
                new_node = (node_t *)malloc(sizeof(node_t));
                memset(new_node, 0, sizeof(node_t) );
                new_node->next = NULL;
                new_node->data = i+1;
                
                if( head == NULL )
                {
                        head = new_node;
                }
                else
                {
                        tail->next = new_node;
                }

                tail = new_node;
                
                new_node->next = head;
                head = new_node;
        }
        
        {
                node_t  *node=NULL;
				//遍历链表
                for(node=head; node!=NULL; node=node->next)
                {
                        printf("%d\n",node->data);
                }
        }  
        //释放链表
        Free(&head);      
        return 0;
 }
 
void Free(node_t **head)
{
        node_t  *freenode=NULL;
        freenode = *head;

        while( *head!=NULL )
        {
                freenode = *head;
        		*head = (*head)->next;
                free(freenode);
        		freenode = NULL;
                printf("%p\n", freenode);	//打印地址来判断释放内存是否完全成功, 即看打印次数
        		printf("bye\n");
        }
                return ;
}

输出结果为:
我们可以观察到一共free十次, 每释放一次就将freenode指针赋值为NULL.
在这里插入图片描述

通过头插法实现栈

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct node_s
{
        int data;
        struct node_s   *next;
} node_t;

int main (int argc, char **argv)
{ 
        node_t          *head = NULL;
        node_t          *new_node;
        int             i;

        for(i=0; i<10; i++)
        {
                new_node = (node_t *)malloc(sizeof(node_t));
                memset(new_node, 0, sizeof(node_t) );
                new_node->next = NULL;
                new_node->data = i+1;
                
                new_node->next = head;
                head = new_node;
        }
        
        {
                node_t  *node=NULL;

                for(node=head; node!=NULL; node=node->next)
                {
                        printf("%d\n",node->data);
                }
        }
        return 0;
 }

输出结果为:
在这里插入图片描述

<think>嗯,用户想了解链表法。首先,我得回忆一下这两种方法的基本概念。链表是由节点组成的,每个节点包含数据指向下一个节点的指针。法都是向链表中添加节点的方法,但它们的入位置不同。 法是在链表入新节点,也就是每次新节点都成为新的节点。这样的话,原来的节点会变成第二个节点,依此类推。这样操作的话,链表的顺序会入的顺序相反。比如,如果依次入1、2、3,那么链表会是3->2->1。需要维护一个指针,每次入时调整指针的指向。时间复杂度是O(1),因为不需要遍历链表法则是在链表入新节点,这样新节点总是最后一个节点。这样的话,链表的顺序入顺序一致。例如,入1、2、3,链表就是1->2->3。不过,每次入需要找到链表部,所以如果只有指针的话,时间复杂度会是O(n),但如果有指针的话,可以优化到O(1)。 接下来要考虑用户可能的疑问。比如,这两种方法的应用场景有什么区别?法适合需要逆序的情况,或者频繁在入,比如实现法则适合维护入顺序,比如队列。用户可能还会想知道如何实现这两种方法,是否需要额外的指针。 然后要检查是否覆盖了关键点,比如步骤、时间复杂度、示意图、优缺点应用场景。可能还需要用例子来说明,比如入顺序不同导致链表结构不同。另外,是否需要考虑链表为空的情况,这时候法都需要处理指针指针的初始化。 还要确保回答符合系统级指令,数学表达式用$...$,独立公式用$$...$$,虽然这里可能涉及代码,但用户没有特别要求代码,所以可能不需要。不过如果有伪代码的话,可能需要用正确的格式。但用户的问题主要是介绍概念,所以重点放在文字描述结构上。 结构方面,应该先定义两种方法,然后分步骤说明,比较它们的区别,优缺点,应用场景,可能的问题等。需要确保语言清晰,逻辑连贯,帮助用户逐步理解。 可能用户是刚开始学习数据结构的学生,所以需要避免过于复杂的术语,用简单易懂的例子说明。同时,确保回答正确,比如法的时间复杂度确实是O(1),而法在没有指针时是O(n)。如果有指针,法也是O(1),这点需要说明清楚。 还要注意回答中使用中文,符合格式要求。不需要使用代码块,但如果有必要,可以用自然语言描述步骤。例如,法的步骤:创建新节点,新节点的next指向原节点,指针指向新节点。法的步骤:找到当前节点,将节点的next指向新节点,更新指针到新节点。 最后总结两者的区别,帮助用户对比记忆。可能还需要提到内存管理,比如是否需要额外空间,但链表本身是动态分配,所以入节点不需要预先分配固定空间。 总之,整个思考过程需要覆盖用户的问题,确保信息准确,结构清晰,易于理解,同时符合格式要求。</think>### 链表法详解 链表是一种基础数据结构,其核心是通过**节点**(Node)的指针链接实现动态存储。法是两种向链表入新节点的基本方法,它们在操作逻辑应用场景上有显著差异。 --- #### **一、法(Head Insertion)** **定义**:将新节点入到链表的**部**,使其成为新的节点。 **步骤**: 1. 创建新节点,存储数据。 2. 将新节点的`next`指针指向原节点。 3. 更新链表指针(head),使其指向新节点。 **示意图**: 原始链表:`head -> A -> B -> C` 入节点`D`后:`head -> D -> A -> B -> C` **特点**: - **时间复杂度**:$O(1)$(直接操作指针)。 - **链表顺序**:入顺序最终链表顺序**相反**。例如,依次入1、2、3,结果为`3->2->1`。 - **适用场景**:需要逆序处理数据(如实现)、快速部。 **代码逻辑示例**: ```python new_node.next = head # 新节点指向原节点 head = new_node # 更新指针 ``` --- #### **二、法(Tail Insertion)** **定义**:将新节点入到链表的**部**,使其成为新的节点。 **步骤**: 1. 创建新节点,存储数据。 2. 找到链表节点(需遍历或维护指针)。 3. 将节点的`next`指针指向新节点。 4. 更新指针(若有)。 **示意图**: 原始链表:`head -> A -> B -> C` 入节点`D`后:`head -> A -> B -> C -> D` **特点**: - **时间复杂度**: -指针时:$O(n)$(需遍历到部)。 -指针时:$O(1)$(直接操作指针)。 - **链表顺序**:入顺序最终链表顺序**一致**。例如,依次入1、2、3,结果为`1->2->3`。 - **适用场景**:维护数据原始顺序(如实现队列)。 **代码逻辑示例**: ```python if head is None: # 链表为空 head = new_node tail = new_node else: tail.next = new_node # 原节点指向新节点 tail = new_node # 更新指针 ``` --- #### **三、法的对比** | **特性** | **法** | **法** | |----------------|-------------------------------|-------------------------------| | 入位置 | 部 | 部 | | 时间复杂度 | $O(1)$ | 无指针时$O(n)$,有指针时$O(1)$ | | 数据顺序 | 逆序 | 正序 | | 典型应用 | 、逆序链表 | 队列、维护原始顺序 | --- #### **四、常见问题** 1. **如何选择法或法?** - 若需快速入且不关心顺序(如撤销操作),用法。 - 若需保留入顺序(如日志记录),用法。 2. **是否需要额外指针?** - 法只需维护指针。 - 法建议维护指针以优化性能。 3. **空链表的处理** - 入第一个节点时,指针指针需同时指向该节点。 --- #### **总结** 法是链表操作的核心方法,理解其差异有助于灵活选择数据结构。法适合高频部操作,法则更适合顺序敏感的场景。实际应用中,可结合需求选择或混合使用(如双向链表)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值