数据结构——单向链表

1、前言

        我们学完顺序表以后我们现在来学习链表,我们先学习最简单的单向链表,

2.链表

        2.1特点

                链式存储不是连续存储,大小不固定,其插入和删除很简单,但是其访问和查找比较难实现,在单向链表中每个节点都有前驱和后继(头结点只有后继,尾结点只有前驱)

        2.2实现一个节点

                我们肯定使用我们学过的结构体来实现我们的节点,我们要在结构体内定义数据域和指针域

typedef int data_type
typedef struct link
{
    //数据域
    data_type data;
    //指针域
    struct link *next;
}Link;
//接下来定义头部节点
Link *head=(Link *)malloc(sizeof(Link));

接下来我们直接写代码吧 

2.2.1先来实现头结点的创建,我们先写库函数link.h

#ifndef _LINK_H
#define _LINK_H
//引入头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//创建结构体
//数据类型的定义
typedef int data_type
struct link
{
    data_type data;
    struct link *next;
}Link;

//定义错误枚举,在这里我把我们接下来可能出现的错误进行定义,以便我们可以快速查找错误
enum value
{
    EMPTY_ERROR=-7,
    FULL_ERROR,
    POS_ERROR,
    CREAT_ERROR,
    NULL_ERROR,
    MALLOC_ERROR,
    ERROR,
    OK
};
Link * my_node();//创建结点
#endif

2.2.2接下来写main.c

#include "../include/link.h"

int main()
{
    Link *phead=NULL;
    phead=my_head;
    if(NULL==phead)
    {
        printf("申请空间失败\n");
        return ERROR;
    }
    printf("申请成功\n");
    return 0;
}
    

2.2.3编写 link.c

#include "../include/link.h"
//链表节点的建立
Link * my_node()
{
    Link *pHead=(Link *)malloc(sizeof(Link));
    if(NULL==pHead)
    {
        printf("malloc error");
        return NULL;
    }
    memset(pHead,'\0',sizeof(Link));
    return pHead;
}
    

2.3:插入数据 

        主函数

#include "../include/link.h"

int main()
{
    Link *phead=NULL;
    phead=my_node();
    if(NULL==phead)
    {
        printf("申请空间失败\n");
        return ERROR;
    }
    printf("申请成功\n");
    //插入数据
    int pos;
    int data;
    while(1)
    {
        printf("请输入插入的位置\n");
        scanf("%d",&pos);
        if(pos==-1)
        {
            break;
        }
        printf("请输入插入的数据\n");
        scanf("%d",&data);
        my_insert(pHead,pos,data);
    }

    return 0;
}
    

 子函数

#include "../include/link.h"
Link * my_node()
{
    Link *pHead=(Link *)malloc(sizeof(Link));
    if(NULL==pHead)
    {
        printf("malloc error");
        return NULL;
    }
    memset(pHead,'\0',sizeof(Link));
    return pHead;
}
//插入函数
//在插入函数的中我们要分三个区域,分别为头部插入(在头结点之后插入)
//中间插入
//尾部插入
int my_insert(SeqLink *phead,int pos,int Newdata)
{
    //入参检查
    if(NULL==*phead)
    {
        printf("creat error:");
        return CREAT_ERROR;
    }
    if(pos<0)
    {
        printf("pos error: ");
        return POS_ERROR;
     }
    //为我们要插入的数据申请一个 结点
    Link *pnew=my_node();
    penw->data=newdata;
    //接下来我们判断插入位置进行插入
    switch(pos)
    {
        //首部插入
        case 1:
           {     //判断此时链表是否为空
                if(NULL==phead->next)
                {
                     phead->next=pnew;
                }
                else
                {
                    pnew->next=phead->next;
                    phead->next=pnew;
                }
                break;
            }
        //尾部插入
        case 0:
            {
                Link *ptail=phead->next;
                if(NULL==ptail)
                {
                    phead->next=pnew;
                }
                else
                {
                    while(NULL != ptail->next)
                    {
                        ptail=ptail->next;
                     }
                    ptail->next=pnew;
                break;
           }
        default:
            {
                Link *ppre=phead;
                if(NULL==ppre->next)
                {
                    ppre->next=pnew;
                }
                else
                {
                    for(int i=0;(i<pos-1)&&(NULL != ppre);i++)
                    {
                        ppre=ppre->next;
                    }
                    if(NULL==ppre)
                    {
                        printf("pos error:");
                        return POS_ERROR;
                    }
                    else
                    {
                        pnew->next=ppre->next;
                        ppre->next=pnew;
                    }
                }
            }
    }
    rerurn OK;
}

            
    

2.4删除函数

#include "../include/link.h"
Link * my_node()
{
    Link *pHead=(Link *)malloc(sizeof(Link));
    if(NULL==pHead)
    {
        printf("malloc error");
        return NULL;
    }
    memset(pHead,'\0',sizeof(Link));
    return pHead;
}
//插入函数
//在插入函数的中我们要分三个区域,分别为头部插入(在头结点之后插入)
//中间插入
//尾部插入
int my_insert(SeqLink *phead,int pos,int Newdata)
{
    //入参检查
    if(NULL==*phead)
    {
        printf("creat error:");
        return CREAT_ERROR;
    }
    if(pos<0)
    {
        printf("pos error: ");
        return POS_ERROR;
     }
    //为我们要插入的数据申请一个 结点
    Link *pnew=my_node();
    penw->data=newdata;
    //接下来我们判断插入位置进行插入
    switch(pos)
    {
        //首部插入
        case 1:
           {     //判断此时链表是否为空
                if(NULL==phead->next)
                {
                     phead->next=pnew;
                }
                else
                {
                    pnew->next=phead->next;
                    phead->next=pnew;
                }
                break;
            }
        //尾部插入
        case 0:
            {
                Link *ptail=phead->next;
                if(NULL==ptail)
                {
                    phead->next=pnew;
                }
                else
                {
                    while(NULL != ptail->next)
                    {
                        ptail=ptail->next;
                     }
                    ptail->next=pnew;
                break;
           }
        default:
            {
                Link *ppre=phead;
                if(NULL==ppre->next)
                {
                    ppre->next=pnew;
                }
                else
                {
                    for(int i=0;(i<pos-1)&&(NULL != ppre);i++)
                    {
                        ppre=ppre->next;
                    }
                    if(NULL==ppre)
                    {
                        printf("pos error:");
                        return POS_ERROR;
                    }
                    else
                    {
                        pnew->next=ppre->next;
                        ppre->next=pnew;
                    }
                }
            }
    }
    rerurn OK;
}

//删除数据
int my_delete(Link *pHead,int pos, data_type *pDelData)
{
	if(NULL == pHead)
	{
		printf("creat is error!\n");
		return CREAT_ERROR;
	}
	if(pos < 0)
	{
		printf("pos is error!\n");
		return POS_ERROR;
	}
	if(NULL == pDelData)
	{
		printf("null error!\n");
		return NULL_ERROR;
	}
	switch(pos)
	{
		case HEAD:
			{
				//头部删除
				printf("I am Head!\n");
				break;
			}
		case TAIL:
			{
				//尾部删除
				printf("I am Tail!\n");
				break;
			}
		default:
			{
				//中间删除
				//判断是否为空链表
				Link *pDelPre = pHead;
				if(NULL == pHead->pNext)
				{
					printf("空链表! 不能删除操作!\n");
					return EMPTY_ERROR;
				}
				else
				{
					//采用寻找pos-1的位置即可
					int i;
					for(i=0;(i<pos-1) && (NULL != pDelPre);i++)
					{
						pDelPre = pDelPre->pNext;
					}
					//判断引起for结束的原因 
					if(NULL == pDelPre)
					{
						printf("pos is error!\n");
						return POS_ERROR;
					}
					//定义一个临时指针接替pDelPre->pNext
					Link *pDel = pDelPre->pNext;
					//保存被删除结点之后的所有结点
					pDelPre->pNext = pDel->pNext;
					//保存被删除的数据
					*pDelData = pDel->data;
					//释放 
					free(pDel);
					pDel = NULL;
				}
			}
	}
	return OK;
}
            
    

2.5显示函数

        

int my_show(Link *phead)
{
    //入参检查
    if(NULL==phead)
    {
        printf("creak error:\n");
        return CREAK_ERROR;
    }
    Link *pshow=phead->next;
    if(NULL==pshow)
    {
        printf("空链表!\n");
        return EMPTY_ERROR;
    }
    else
        {
             while(NULL != pshow)
                {
                    printf("%3d",pshow->data);
                    pshow=pshow->next;
                }
        
            printf("\n");
            printf("显示完毕\n");
        
         }
    return OK;
}
          

2.6销毁函数 

int DestroyLink(Link *pHead)
{
	if(NULL == pHead)
	{
		printf("creat is error!\n");
		return CREAT_ERROR;
	}
	//判断是否为空链表
	if(NULL == pHead->pNext)
	{
		//释放头结点的空间
		free(pHead);
		//该置空是不会引起实际参数的指向发生变化,形式参数的指向此时被改变了
		pHead = NULL;
	}
	else
	{
		//不能去直接释放pHead,后续还有一些结点
		//(1)先释放头结点之后的所有结点
		Link *pDelHead = pHead->pNext;
		Link *pDelTemp = NULL;

		while(NULL != pDelHead)
		{
			pDelTemp = pDelHead;
			//释放pDelTemp结点之前:先保存pDelTemp->pNext/pDelHead->pNext
			//之后的所有结点
			pDelHead = pDelHead->pNext;
			//释放
			free(pDelTemp);
			pDelTemp = NULL;
		}
		//(2)最后再释放头结点
		free(pHead);
		//给形式参数pHead置空
		pHead = NULL;
	}
	return OK;

}

3、最后 

       我在单向链表中为大家展示了如何创建节点,如何在插入数据,如何删除数据,如何展示数据,如何销毁链表,因为我们是在堆区开辟的空间最后要我们手动释放空间,剩下的还有如何查找数据,还有修改数据,这两个函数留给大家自己练手,不会的可以私信我,好了,我们的单向链表到这里就要结束了,下一篇文章,我将会为大家讲解双向链表,其大部分的知识点与单向链表差不多,大家学完单向链表再学习双向链表就比较简单了

         

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习C语言之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值