单向链表的代码实现(vi编辑器)

本文介绍了单向链表的基本概念,包括头节点、尾节点和动态内存分配。详细讲解了如何实现单向链表的头插法、尾插法插入节点,以及链表的遍历。同时,提到了链表的删除操作,包括删除特定值的节点,但未涵盖删除特定位置的节点。最后,讨论了单向链表的反转输出,涉及三个指针的使用。
摘要由CSDN通过智能技术生成

写在开头:基于对指针的学习与应用,写下该文章记录单向链表的各种操作。

操作系统:ubuntu

编译工具:vim

链表是由程序员自己通过动态申请内存得到的数据结构,在单向链表中,头节点没有前仆,尾节点没有后继,也就是说头节点就是单向链表的开头,尾节点意味着单向链表的结束。一般头节点不用来存储数据,而用来指向链表的第一个节点,头节点中的数据可以用来存储链表元素的个数,个数可以方便我们后面进行遍历。

链表是由节点构成的,因此我们需要先创建节点的结构体:

struct Node
{
    int data;            //节点的数据
    struct Node *next;   //保存下一个节点的地址
};

刚才说到链表是通过动态申请得到的数据结构,需要用malloc函数,当然申请了内存就需要释放(free)啦。

链表是从头节点开始的,因此先写一个头节点创建函数

struct Node * list_init()
{
    struct Node* head=(struct Node*)malloc(sizeof(struct Node*));
    head->data=0;//头字节的数据用来存储个数,也可以用(*head).data=0;进行赋值,下面同样如此
    head->next=NULL;//头指针目前没有指向,因此设置为空
    return head;
}

在list_init()函数中,我们使用了malloc()函数区申请内存,因此要引用头文件#include <stdlib.h>

至此,节点的结构体和头节点我们已经创建完毕。接下来的操作就是链表的插入、删除、遍历、逆序输出。

一、单向链表的插入

链表的插入有两种方法:头插法和尾插法。

1.头插法:在链表的头部插入新节点,具体思路就是让新节点的指针指向头节点的指针,因为头结点的指针中保存了链表第一个元素的地址,现在让新插入的节点的指针指向第一个元素,然后让头节点指向新插入的节点,此时新插入的节点就变成了链表的第一个元素。

void head_insert(struct Node* list,int data)
{
    struct Node* temp=(struct Node*)malloc(sizeof(struct Node*)); //申请内存
    temp->data=data;        //赋值
    temp->next=list->next;  //让新节点指向头节点指向的第一个节点的地址
    list->next=temp;        //头节点指向新的节点作为第一个节点
}

既然我们已经写好了头插的函数,那么想要看见效果我们需要实现遍历链表打印。

void print(struct Node* list)
{
    struct Node* temp=list->next;//从第一个节点开始
    printf("list:");
    while(temp)
    {
        printf("%d  ",temp->data);//打印当前节点的值
        temp=temp->next;          //指向下一个节点  
    }
    printf("\n");
}

 在main函数中,我们可以调用头插法函数和打印函数查看效果:

int main(int argc, char *argv[])
{ 
    struct Node* list=list_init();//头节点创建
    head_insert(list,1);          //头插法插入数据1  
    print(list);                  //查看当前链表中的元素
    head_insert(list,2);          //头插法插入数据2    
    print(list);
    head_insert(list,3);
    print(list);
    head_insert(list,4);
    print(list);
    head_insert(list,5);
    print(list);
    return 0;
} 

具体运行截图如下:

 2.尾插法:在链表的尾部插入元素,因此当我们想要通过尾插法插入节点的时候,需要先遍历链表到最后一个。

void tail_insert(struct Node* list,int data)
{
    struct Node* temp=(struct Node*)malloc(sizeof(struct Node*));//申请内存
    temp->data=data;   
    temp->next=NULL;     //由于是尾插法,因此该节点就是末尾,它的指针域就是NULL
    struct Node* ergodic=list->next; //ergodic是用来遍历链表到最后的
    while(ergodic->next)
        ergodic=ergodic->next;
    ergodic->next=temp;  //让原链表的最后一项的指针指向我们新创建的节点
}

在主函数中调用:

int main(int argc, char *argv[])
{ 
    struct Node* list=list_init();  //头节点
    head_insert(list,1);            //头插法
    print(list);
    head_insert(list,2);
    print(list);
    head_insert(list,3);
    print(list);
    head_insert(list,4);
    print(list);
    head_insert(list,5);
    print(list);
    tail_insert(list,6);            //尾插法
    print(list);
    tail_insert(list,7);
    print(list);
    tail_insert(list,8);
    print(list);
    tail_insert(list,9);
    print(list);
    return 0;
} 

运行截图如下:

二、单向链表的遍历

void print(struct Node* list)
{
    struct Node* temp=list->next;//从第一个节点开始
    printf("list:");
    while(temp)
    {
        printf("%d  ",temp->data);//打印当前节点的值
        temp=temp->next;          //指向下一个节点  
    }
    printf("\n");
}

 三.单向链表的删除:当我们想要删除链表中某个特定值或者特定位置的时候,当然是从头遍历找到到某个值或者位置,然后让拥有该值的节点的上一个节点指向该节点的下一个节点,然后把这个节点占用的内存空间释放。

1.删除特定值

void delete(struct Node* list,int data)
{
    struct Node* pre=list;       //被删除的节点的前一个节点
    struct Node* del=pre->next;  //被删除的节点
    while(del)
    {
        if(data==del->data)
        {
            pre->next=del->next; //当前节点的上一个节点指向当前节点的下一个节点
            free(del);
        }
        pre=del;                 //不断更新前一个节点 
        del=del->next;           //当还没找到被删除的节点,不断地向下一个节点寻找
    }
}

main函数中的代码如下:

int main(int argc, char *argv[])
{ 
    struct Node* list=list_init();
    head_insert(list,1);
    print(list);
    head_insert(list,2);
    print(list);
    head_insert(list,3);
    print(list);
    head_insert(list,4);
    print(list);
    head_insert(list,5);
    print(list);
    tail_insert(list,6);
    print(list);
    tail_insert(list,7);
    print(list);
    tail_insert(list,8);
    print(list);
    tail_insert(list,9);
    print(list);
    delete(list,5);
    printf("deleted list:");
    print(list);
    delete(list,9);
    printf("deleted list:");
    print(list);
    delete(list,1);
    printf("deleted list:");
    print(list);
    return 0;
} 

运行截图:

目前的代码仅能删除一个元素,未实现删除重复的元素,上述的链表元素都是不同元素组成的,后续敲出来再来更新(如果有大佬也可以指点一下,欢迎指正和交流)

2.删除特定位置(未完成待更新)

四、单向链表的反转输出:链表的反转输出需要借助三个节点指针,一个用于记录删除节点的上一个节点的地址,一个是用于遍历链表的,还有一个用于辅助遍历的。这样说明并不够明朗,请具体看代码实现:

void reverse(struct Node* list)
{
    struct Node *pre,*temp,*next;
    pre=NULL;
    temp=list->next;     //temp将从链表的第一项开始遍历
    while(temp)
    {
        next=temp->next; //next用于保存temp指向的下一个节点,辅助temp向链表继续遍历
        temp->next=pre;  //实现反转的思想是把原来的指向下一个节点的连接断开,然后指向上一个节点
                         //因此pre指针就用于保存上一个节点的地址
        pre=temp;        //pre保存现在这个节点的地址,用于下一个节点来指向它
        temp=next;       //temp靠next遍历
    }
    list->next=pre;      //遍历结束后,原来的头变成了尾,原来的尾变成了头,由于pre保存了最后一个                                                                                  
                         //节点的地址,因此头指针直接指向pre。
}

运行截图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值