一 单链表简介
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;
}