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);
}

总结

单链表的操作基本就上面的,可以看得出传参修改有的用的指针直接改,有的使用函数返回值进行修改,有的地方还不完善,欢迎大家指出。

posted @ 2022-08-05 17:49  Tireless  阅读(74)  评论(0编辑  收藏  举报