C语言对单链表操作
单链表的操作
链表是学习数据结构经常使用的数据结构,本篇仅记录学习中对单链表操作,循环链表和双向链表暂时自己没有记录,另外,本博客数据结构均已使用C语言版。
单链表的结构体定义
这里我们为了方便理解,采用相对简单的结构
typedef struct person{
int data;
struct person *next;
}ListNode;
单链表的创建
采用头插法进行创建,最后返回创建好的链表头地址
/*
*创建链表(头插法)
*参数:*length链表长度
*/
ListNode *create(){
ListNode *p1,*head,*p2;
head = NULL;
int a;
printf("输入链表节点值每输入后回车结束,输入0默认停止\n");
while(1){
p1 = (ListNode *)malloc(sizeof(ListNode));
scanf("%d",&a);
if(a == 0){
break;
}
p1->data = a;
p1->next = NULL;
if(head == NULL){
head = p1;
}else{
p2->next = p1;
}
p2 = p1;
}
return head;
}
链表打印
这里我们为了方便封装一个链表打印方法,减少代码量
/*
*链表数据打印
*参数:*s打印链表名
*/
void listPrint(ListNode *s){
ListNode *t;
t = s;
printf("\n链表当前存储数据: ");
while(t != NULL){
printf("%d ",t->data);
t = t->next;
}
free(t);
printf("\n");
}
获取链表长度
为了方便后续操作,封装获取链表长度函数
/*
*计算链表长度
*参数:s需要计算链表地址
*/
int getListLength(ListNode *s){
ListNode *p;
int i = 0;
p = s;
while(p != NULL){
p = p->next;
i++;
}
// 释放内存
free(p);
return i;
}
链表的插入
这里没有采用指针地址直接修改的方法,返回值是插入后单链表的头结点地址
/*
*链表插入数据
*参数:*s插入链表名,t 插入位置,e插入数据
*/
ListNode *ListInsert(ListNode *s, int t, int e){
ListNode *head,*q;
int i;
i = 1;
q = (ListNode *)malloc(sizeof(ListNode));
q->data = e;
// 记录插入前链表头指针地址
head = s;
// 当删除节点位置为1时,特殊处理
if(t == 1){
q->next = head;
head = q;
return head;
}
// 找到插入数据位置
while(i != (t - 1)){
s = s->next;
i++;
}
q->next = s->next;
s->next = q;
// 返回头指针地址
return head;
}
链表的排序
这里采用的是降序排序(冒泡排序),因为暂时自己没有别的好的方法实现,因为单链表排序个人感觉有点麻烦
/*
*链表排序
*参数:*s需要排序的链表
*/
void *listSort(ListNode *s){
ListNode *begin,*end,*p;
end = NULL;
begin = s;
while(s->next != end){
while(begin->next != end){
if(begin->data < begin->next->data){
int tmp = begin->data;
begin->data = begin->next->data;
begin->next->data = tmp;
}
begin = begin->next;
}
end = begin;
begin = s;
}
}
链表的删除
这里采用删除指定链表位置的节点,操作完成后返回操作后的头结点
/*
*删除指定位置链表
*参数:s待操作链表,t删除节点位置
*/
ListNode *delList(ListNode *s , int t){
ListNode *head,*q;
int length,i;
length = getListLength(s);
i = 1;
// 记录头结点地址
head = s;
if(t > length){
printf("删除元素超出链表长度!");
return s;
}
while(s != NULL){
// 当删除节点位置为1时特殊处理
if( t == 1){
// 记录头结点地址
q = s;
// 修改头结点地址为删除后地址
head =s->next;
break;
}
// 删除节点位置不为1时通用处理 ,此处拿到待删除节点上一节点地址
if(i == t-1){
// 记录待删除节点
q = s->next;
// 待删除节点父节点指向待删除节点子节点地址
s->next = s->next->next;
break;
}
s = s->next;
i++;
}
// 释放删除节点内存
free(q);
// 返回头结点地址
return head;
}
链表修改
这里采用修改链表指定位置的节点值
/*
*修改指定节点数据
*参数;s待修改链表地址,t修改节点位置默认从1开始,e需要修改元素
*/
void updateList(ListNode *s, int t, int e){
ListNode *head,q;
int length,i;
i = 1;
length = getListLength(s);
if(t > length){
printf("修改元素超出链表长度!");
return;
}
head = s;
while(s != NULL){
if(t == i){
s->data = e;
}
s = s->next;
i++;
}
s = head;
}
链表节点值搜索
这里采用的是搜索链表中是否含有搜索值,并且返回有几个搜索值
/*
*查询链表中是否有某元素
*参数:s查询链表指针地址,e待查询元素
*/
void searchList(ListNode *s,int e){
ListNode *p;
int i = 0;
p = s;
while(p != NULL){
if(p->data == e){
i++;
}
p = p->next;
}
free(p);
if(i == 0){
printf("\n链表中不包含待查询元素!\n");
}else{
printf("\n链表中共有%d个查询值。\n",i);
}
}
主函数
主函数我的流程写的比较死板,主要为了测试链表操作是否完善
void main(){
ListNode *p;
int length,t,e;
p = create();
length = getListLength(p);
listPrint(p);
printf("\n请输入需要插入数据位置并回车,起始位置为1\n ");
scanf("%d",&t);
if(t > length){
printf("插入位置大于链表长度!");
}else{
printf("请输入插入数据:\n");
scanf("%d",&e);
}
p = ListInsert(p,t,e);
listPrint(p);
printf("\n降序排序后如下:\n");
listSort(p);
listPrint(p);
printf("\n请输入需要删除的节点位置,默认1开始\n");
scanf("%d",&t);
p = delList(p,t);
listPrint(p);
printf("\n请输入需要修改链表节点位置,默认从1开始\n");
scanf("%d",&t);
printf("\n请输入修改链表节点数据\n");
scanf("%d",&e);
updateList(p,t,e);
listPrint(p);
printf("\n请输入需要查询的值\n");
scanf("%d",&t);
searchList(p,t);
}
完整代码展示
#include<stdlib.h>
#include<stdio.h>
typedef struct person{
int data;
struct person *next;
}ListNode;
/*
*创建链表(头插法)
*参数:*length链表长度
*/
ListNode *create(){
ListNode *p1,*head,*p2;
head = NULL;
int a;
printf("输入链表节点值每输入后回车结束,输入0默认停止\n");
while(1){
p1 = (ListNode *)malloc(sizeof(ListNode));
scanf("%d",&a);
if(a == 0){
break;
}
p1->data = a;
p1->next = NULL;
if(head == NULL){
head = p1;
}else{
p2->next = p1;
}
p2 = p1;
}
return head;
}
/*
*链表数据打印
*参数:*s打印链表名
*/
void listPrint(ListNode *s){
ListNode *t;
t = s;
printf("\n链表当前存储数据: ");
while(t != NULL){
printf("%d ",t->data);
t = t->next;
}
free(t);
printf("\n");
}
/*
*计算链表长度
*参数:需要计算链表地址
*/
int getListLength(ListNode *s){
ListNode *p;
int i = 0;
p = s;
while(p != NULL){
p = p->next;
i++;
}
// 释放内存
free(p);
return i;
}
/*
*链表插入数据
*参数:*s插入链表名,t 插入位置,e插入数据
*/
ListNode *ListInsert(ListNode *s, int t, int e){
ListNode *head,*q;
int i;
i = 1;
q = (ListNode *)malloc(sizeof(ListNode));
q->data = e;
// 记录插入前链表头指针地址
head = s;
// 当插入节点位置为1时,特殊处理
if(t == 1){
q->next = head;
head = q;
return head;
}
// 找到插入数据位置
while(i != (t - 1)){
s = s->next;
i++;
}
q->next = s->next;
s->next = q;
// 返回头指针地址
return head;
}
/*
*链表排序
*参数:*s需要排序的链表
*/
void *listSort(ListNode *s){
ListNode *begin,*end,*p;
end = NULL;
begin = s;
while(s->next != end){
while(begin->next != end){
if(begin->data < begin->next->data){
int tmp = begin->data;
begin->data = begin->next->data;
begin->next->data = tmp;
}
begin = begin->next;
}
end = begin;
begin = s;
}
}
/*
*删除指定位置链表
*参数:s待操作链表,t删除节点位置
*/
ListNode *delList(ListNode *s , int t){
ListNode *head,*q;
int length,i;
length = getListLength(s);
i = 1;
// 记录头结点地址
head = s;
if(t > length){
printf("删除元素超出链表长度!");
return s;
}
while(s != NULL){
// 当删除节点位置为1时特殊处理
if( t == 1){
// 记录头结点地址
q = s;
// 修改头结点地址为删除后地址
head =s->next;
break;
}
// 删除节点位置不为1时通用处理 ,此处拿到待删除节点上一节点地址
if(i == t-1){
// 记录待删除节点
q = s->next;
// 待删除节点父节点指向待删除节点子节点地址
s->next = s->next->next;
break;
}
s = s->next;
i++;
}
// 释放删除节点内存
free(q);
// 返回头结点地址
return head;
}
/*
*修改指定节点数据
*参数;s待修改链表地址,t修改节点位置默认从1开始,e需要修改元素
*/
void updateList(ListNode *s, int t, int e){
ListNode *head,q;
int length,i;
i = 1;
length = getListLength(s);
if(t > length){
printf("修改元素超出链表长度!");
return;
}
head = s;
while(s != NULL){
if(t == i){
s->data = e;
}
s = s->next;
i++;
}
s = head;
}
/*
*查询链表中是否有某元素
*参数:s查询链表指针地址,e待查询元素
*/
void searchList(ListNode *s,int e){
ListNode *p;
int i = 0;
p = s;
while(p != NULL){
if(p->data == e){
i++;
}
p = p->next;
}
free(p);
if(i == 0){
printf("\n链表中不包含待查询元素!\n");
}else{
printf("\n链表中共有%d个查询值。\n",i);
}
}
void main(){
ListNode *p;
int length,t,e;
p = create();
length = getListLength(p);
listPrint(p);
printf("\n请输入需要插入数据位置并回车,起始位置为1\n ");
scanf("%d",&t);
if(t > length){
printf("插入位置大于链表长度!");
}else{
printf("请输入插入数据:\n");
scanf("%d",&e);
}
p = ListInsert(p,t,e);
listPrint(p);
printf("\n降序排序后如下:\n");
listSort(p);
listPrint(p);
printf("\n请输入需要删除的节点位置,默认1开始\n");
scanf("%d",&t);
p = delList(p,t);
listPrint(p);
printf("\n请输入需要修改链表节点位置,默认从1开始\n");
scanf("%d",&t);
printf("\n请输入修改链表节点数据\n");
scanf("%d",&e);
updateList(p,t,e);
listPrint(p);
printf("\n请输入需要查询的值\n");
scanf("%d",&t);
searchList(p,t);
}
总结
单链表的操作基本就上面的,可以看得出传参修改有的用的指针直接改,有的使用函数返回值进行修改,有的地方还不完善,欢迎大家指出。