数据结构(二)——单链表

一 单链表简介

1、定义

单链表是一种基本的数据结构,它由一系列节点组成,每个节点包含两部分:数据和指向下一个节点的
指针。

在单链表中,每个节点的指针只指向下一个节点,最后一个节点的指针指向空值,表示链表的尾部。链
表的头部指针指向链表的第一个节点。

2、优缺点

链表的优势在于可以动态地添加和删除节点,不需要预先分配内存空间。但是查找某个节点的效率相对
较低,需要从头部开始遍历。

二 代码示例

1、头文件


/*===============================================
*   文件名称:linklist.h
*   创 建 者:    
*   创建日期:2023年08月08日
*   描    述:
================================================*/
#ifndef _LINKLIST_H
#define _LINKLIST_H
typedef int data_t;

//定义节点类型
typedef struct linklist
{
    data_t data;
    struct linklist* next ;
}link,*plink;

//创建头节点
plink init_linklist();

//插入节点
int insert_linklist(plink L , int index , data_t value);

//按值删除
int delete_linklist(plink L , data_t value);

//按下标删除
int pdelete_linklist(plink L , int pos );

//判空
int empty_linklist(plink L );

//打印
int show_linklist(plink L );

//求长度
int getlen_linklist(plink L);

//按值修改
int change_linklist(plink L , data_t value ,data_t data);

//按下标修改
int pchange_linklist(plink L , int pos ,data_t data);


//按值查询下标
int select_linklist(plink L , data_t value );

//按下标查询值
int pselect_linklist(plink L , int pos );

//清空
int clear_linklist( plink L );

//销毁
int destroy_linklist( plink *L );

//逆序
int Rs_linklist( plink L);

//排序
int sort_linklist( plink L );

#endif

2、功能代码


创建头节点
//创建头节点
plink  init_linklist()
{
    plink L = (plink)malloc(sizeof(link)); // 开辟空间
    if( NULL == L )
    {
        return NULL;
    }
    L->next = NULL; //初始l->next为0
    return L;
}


插入函数
//插入节点
int insert_linklist(plink L , int index ,data_t value)
{
    if( NULL == L )
    {
        return -1;
    }
    if( index < 0 || index > getlen_linklist(L))
        return -2;

    //将L移动到需要插入的位置
    while(index--)
        L = L->next;
        
    plink S = (plink)malloc(sizeof(link));  //开辟空间给插入的节点
    if( NULL == S )
    {
        return -1;
    }
    S->next = L->next ;//新节点的next先指向L的next,防止L的后面节点丢失
    L->next = S ;  // L的next指向S
    S->data = value ;// 给新节点赋值
    return 0;
}

删除(按值)
//按值删除
int delete_linklist(plink L , data_t value)
{
    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return -2;
    }
    plink p = L->next ; // p 指向第一个有数据的节点
    while(p->next) //通过p循环遍历
    {
        
        if( value == p->next->data )  // 找到链表中对应的值
        {
            plink q = p->next; // q暂存需要删除的节点
            p->next = q->next; // p断开该节点
            free(q);  // 释放该节点空间
            q = NULL ;  // 将q指向NULL 防止野指针
            continue; // 如果找到了之后,删除节点后结束本次循环,避免下一次循环跳过两个数值相邻且都需要删除的情况
        }
    
        p = p->next; // p向后移动
    }
    
    return 0;
}

删除(按下标)
//按下标删除
int pdelete_linklist(plink L , int pos )
{

    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return -2;
    }
 
    //将L移动到相应下标处
    while(pos--)
    L = L->next; 
    plink q = L->next; //定义q临时存储需要删除的节点
    L->next = q->next;  // L指向下一个节点
    free(q); // 释放q指向节点
    q = NULL ;//将q指向NULL 防止野指针

    return 0;
}

判空
//判空
int empty_linklist(plink L )
{
    if( NULL == L )
    {
        return -1;
    }
    if( NULL == L->next ) // 如果头节点的next为空,则链表为空
        return 0 ;

}

打印
//打印
int show_linklist(plink L )
{
    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return -2;
    }
    plink p = L ;
    int i = 0 ;
    p = p->next;// p指向第一个有数据的节点
    while( 1 )
    {
        
        if( p == NULL ) // 如果p==NULL 则指向最后,结束循环
            break;
        printf("节点[%d].data = %d  ",i,p->data);
        p = p->next;  // p移动一位 
        i++; 
    }
    puts("");
    return 0;
}

计算长度
//计算链表长度
int getlen_linklist(plink L )
{

    if( NULL == L )
    {
        return -1;
    }
    plink p=L->next;//p指向第一个有数据的节点
    int count=0;//记录链表节点个数
    while(L->next) // 循环整个链表
    {
        count++;//一次循环,则增加一次
        L=L->next;
    }
    return count;//返回节点个数
}


修改(按值)
//按值修改
int change_linklist(plink L , data_t value , data_t data)
{

    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return -2;
    }
    

    L = L->next; //使L指向第一个有数据的节点
    while( L )
    {
        if( value == L->data) // 找到匹配的数据值
        {
            L->data = data; //将该数据值更改
        }
        L = L->next; // L 移动一位
    }
    return 0;
}

修改(按下标)
//按下标修改
int pchange_linklist(plink L , int pos , data_t data)
{
    if( NULL == L )
    {
        return -1;
    }
    if( pos < 0 || pos >= getlen_linklist(L))
        return -2;

   // 找到该下标对应节点
    while(pos--)
        L = L->next;
    L = L->next;   // 因为L是从头节点开始,所以要再移动一位才是对应的节点
    L->data = data; //更改数据值
    return 0;
}


查询(按值)
//按值查询下标
int select_linklist(plink L , data_t value )
{
    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return -2;
    }

    int count = 0;
    L = L->next; //使L指向第一个有数据的节点
    while(L) 
    {
        if( value == L->data )  // 找到对应值
        {
            break; //结束循环,此时count记录的值即为下标
        }
        L = L->next;
        count++;
    }
    if( 0 == count )
        return -3;// 如果没有找到对应值,则返回-3
    else
        return count;
}

查询(按下标)
//按下标查询值
int pselect_linklist(plink L , int pos )
{

    if( NULL == L )
    {
        return -1;
    }
    if( pos < 0 || pos >= getlen_linklist(L))
        return -2;

    //找到该下标的节点
    while(pos--)
        L = L->next;
    L = L->next;
    
    return L->data; //返回该节点的数据
}


清空链表
//清空
int clear_linklist( plink L )
{

    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return 0;
    }

	//得到链表长度
    int i = getlen_linklist(L);
    
    //循环调用删除函数,将所有节点(除头节点外)删除
    while(i--)
    {
        pdelete_linklist(L,0);
    
    }
    return 0;
}

销毁链表
//销毁
int destroy_linklist( plink *L )
{

    if( NULL == *L )
    {
        return -1;
    }


    clear_linklist(*L); //调用清除函数,将链表只有头节点
    free(*L);//释放头节点
    *L = NULL; //将*L指向NULL
    return 0;
}

逆序

将链表的数据逆反过来,将头结点与有数据的链表分开,看作两个链表,将数据链表节
点依次按照头插的方式插入头结点链表,(头插方式先插入的在后面)

//逆序  将链表的数据逆过来存储在其中
int Rs_linklist( plink L)
{
    
    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return 0;
    }

    plink p = L->next  ; //p指向第一个有数据的节点
    L->next = NULL ;  // 断开头节点
    
    while(p) // 使用头插循环插入
    {
        plink q = p->next; // q存储p->next,防止链表地址丢失
        
        // 将p指向节点通过头插连接到头节点的链表上
        p->next = L->next; 
        L->next = p ;
        
        
        p = q ;//p指向下一个节点
    }
    return 0;
}

排序

将链表的数据进行排序,将头结点与有数据的链表分开,看作两个链表,将数据链表节
点依次插入头结点链表,循环头结点链表进行比较,将数据节点插入到比自己大的节点前一个位置上

//排序
int sort_linklist( plink L )
{
    
    if( NULL == L )
    {
        return -1;
    }
    if( 0 == empty_linklist(L) )
    {
        return 0;
    }

    plink p = L->next; //p指向第一个有数据的节点
    L->next = NULL ; // 断开头节点
    
    
    //外循环,遍历有数据的链表
    while(p)
    {
        plink q = L ; // q为头节点链表的指针
        plink t = p->next; // t存储了p->next
        
        //内循环,遍历头节点的链表
        while( q->next )
        {
            if( q->next->data > p->data)//找到比p节点数据大的数据,确定插入位置为该数据前一位
                break;
            q = q->next;
        }
        
        // 将p指向的节点插入到头节点的链表中
        p->next = q->next;
        q->next = p ;
        
        
        p = t; // p向后移一位

    }
    return 0;

}


3、测试代码

/*===============================================
*   文件名称:main.c
*   创 建 者:   
*   创建日期:2023年08月08日
*   描    述:
================================================*/
#include <stdio.h>
#include "linklist.h"
int main(int argc, char *argv[])
{ 
    //初始化单链表
    plink L = init_linklist();
    if( NULL == L )
    {
        puts("init err");
        return -1;
    }

//插入节点
    for(int i = 0 ; i < 8 ; i++ )
    {
        if( 0 != insert_linklist(L ,i,i+1))
        {
            puts("for: insert err");
            return -1;
        }
    }
    if( 0 != show_linklist(L ))
    {
        puts("show err");
        return -1;
    }
    puts("");

//删除

    puts("删除3");
    if( 0 != delete_linklist(L , 3))
    {
        puts(" delete  err");
        return -1;
    }
    
    if( 0 != show_linklist(L ))
    {
        puts("show err");
        return -1;
    }
    puts("");
//删除

    puts("删除下标2");
    if( 0 != pdelete_linklist(L , 2))
    {
        puts(" pdelete  err");
        return -1;
    }
    
    if( 0 != show_linklist(L ))
    {
        puts("show err");
        return -1;
    }
    puts("");

//修改

    puts("按值修改,1");
    if( 0 != change_linklist(L , 1 , 1111))
    {
        puts(" change  err");
        return -1;
    }
    
    if( 0 != show_linklist(L ))
    {
        puts("show err");
        return -1;
    }
    puts("");


//按下标修改

    puts("按下标修改,2");
    if( 0 != pchange_linklist(L , 2 , 2222))
    {
        puts("pchange  err");
        return -1;
    }
    
    if( 0 != show_linklist(L ))
    {
        puts("show err");

    return 0;
    }
    puts("");

//按值查询

    puts("按值查询.2222");
    int ret = 0;
    if( 0 > ( ret = select_linklist(L ,2222)))
    {
        puts(" select  err");
        return -1;
    }
    printf("查询到的下标是:%d\n",ret);
    puts("");


//按位置查询

    puts("按位置查询,5");
    if( 0 >( ret = pselect_linklist(L ,5)))
    {
        puts(" pselect  err");
        return -1;
    }
    printf("查询到的值是:%d\n",ret);
    puts("");

//逆序
   
    puts("逆序:");
    Rs_linklist(L);
    show_linklist(L);
    puts("");


//排序
    puts("排序:");
    sort_linklist(L);
    show_linklist(L);
    puts("");

//清空
    puts("清空:");
    clear_linklist(L);
    ret = show_linklist(L);
    if( -2 == ret )
        puts("已清空");



//销毁
    puts("销毁:");
    destroy_linklist(&L);
    ret = show_linklist(L);
    if( -1 == ret )
        puts("已销毁");


return 0;
}

三 运行结果

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值