数据结构(c语言)单链表部分操作

  • 链表是由许多相同数据类型的数据按照特定顺序排列而成的线性表。其优点是数据的插入和删除都相当方便,缺点是设计数据结构十分麻烦。

一、单链表

(一)动态分配内存

c语言中可使用头文件下stdlib.h中的malloc()和free()函数在程序执行期间动态分配与释放内存。

  • 动态分配内存方式如下:
数据类型*指针名称 = (数据类型*)malloc (sizeof(数据类型)*n);
n=1即表示一个变量

变量或对象在使用动态内存分配后,必须进行内存的释放。否则会造成“内存漏失”现象。

  • 释放动态分配变量方式如下:
free(指针名称)
  • 例如:
#include<stdio.h>
#include<stdlib.h>

int main(void){
	
	float *piVal=(float*)malloc(sizeof(float));//动态分配一块浮点数内存空间 
	*piVal=3.14;
	int *piCal=(int*)malloc(sizeof(int));//动态分配一块整数内存空间 
	*piCal=1000;
	 printf("piVal所指向的地址内容为%f\n\n",*piVal);
	 printf("piCal所指向的地址内容为%d\n\n",*piCal);
	 free(piVal);//释放piVal所指向的内存空间 
	 free(piCal);//释放piCal所指向的内存空间 
	 
} 
/*piVal所指向的地址内容为3.140000

  piCal所指向的地址内容为1000*/
  • 实例一
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
int main(void) {
	char* s1 = (char*)malloc(sizeof(char) * 10);
	strcpy(s1, "Hello,C!");
	printf("%s ", s1);
	printf("s1=%d", s1);
	printf("\n");
	char* s2 = (char*)malloc(sizeof(char) * 10);
	strcpy(s2, "Hello,C!");
	printf("%s ", s2);
	printf("s2=%d", s2);
	free(s1);
	free(s2);
}
Hello,C! s1=2036736
Hello,C! s2=2059152
  • 实例二
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main(void){
	struct student{
		char name[20];
		int score;
	};
	
	student* s=(student*)malloc(sizeof(student));
	strcpy(s->name,"zhangsan");
	printf(s->name);
	s->score=85;
	printf("\nscore=%d",s->score);
	free(s);
} 
zhangsan
score=85

(二)单向链表

  • 一个单向链表节点基本上由两个元素构成,即数据字段和指针组成,而指针将会指向下一个元素在内存中的位置。
  • 在单向链表中第一个节点是“链表头指针”,指向最后一个节点的指针设为NULL,表示它是“链表尾”,不知向任何地方。
  • 注意,除非必要否则不可移动链表头指针。

1、建立单向链表

  • 在C语言中,若以动态分配产生链表节点的方式,可以先定义一个结构数据类型,接着在结构中定义一个指针变量,其数据类型与结构相同,作用是在指向下一个链表节点,另外结构中至少要有一个数据字段。
struct student
{
    char name[20];
    int score;
struct student *next;
}s1,s2;
  • 完成结构数据类型定义之后,就可以动态建立链表中每个节点。假设现在要新增一个节点至链表的末尾,且ptr指向链表的第一个节点,则程序上必须设计以下4步
  1. 动态分配内存空间给新节点使用
  2. 将原链表尾部的指针(next)指向新元素所在的内存地址
  3. 将ptr指针指向新节点的内存位置,表示这是新的链表尾部
  4. 由于新结点当前为链表的最后一个元素,所以将它的指针(next)指向NULL
  • 例如将s1的next变量指向s2的内存地址,而且s2的next变量指向NULL
s1.next = &s2;
s2.next = NULL;
  • 建立学生节点的单向链表算法
#include<stdio.h>
#include<stdlib.h>

int main(void){
	struct student{
		char name[20];
		char no[20];
		int Math;
		int Eng;
		struct student*next;
	};
	typedef struct student s_data;
	s_data*ptr;//存取指针 
	s_data*head;//链表头指针
	s_data*new_data;//新增元素所在位置的指针
	head=(s_data*)malloc(sizeof(s_data));//新增链表头的元素
	ptr=head;//设置存取指针的位置
	ptr->next=NULL;//当前无下一元素
	int select; 
	do
	{
		printf("(1)新增 (2)离开 =>");
		scanf("%d",&select);
		if(select!=2){
			printf("姓名 学号 数学成绩 英语成绩:");
			scanf("%s %s %d %d",ptr->name,ptr->no,&ptr->Math,&ptr->Eng);
			new_data=(s_data*)malloc(sizeof(s_data));//新增下一个元素 
			ptr->next=new_data;//存取指针设置为新元素所在的位置
			new_data->next=NULL;//下一个元素的next先设置为null
ptr=ptr->next; 
		}
	}while(select!=2);
}

2、遍历单向链表

  • 遍历单向链表过程中,就是要使用指针运算访问链表中的每一个节点。例如以上例子中,使用结构指针ptr作为链表的读取游标,一开始指向链表的头。每次读完链表的一个节点就将ptr往下一个节点移动,直到ptr指向NULL为止。
ptr=head;//设置存取指针从头开始
while(ptr->next!=NULL){
    {
        printf("姓名:%s\t 学号:%s\t 数学成绩:%d\t 英语成绩:%d\n",
                   ptr->name,ptr->no,ptr->Math,ptr->Eng);
        ptr=ptr->next;//将ptr移往下一个元素
    }
  • 实例一
#include<stdio.h>
#include<stdlib.h>

int main(void){
	struct student{
		char name[20];
		char no[10];
		int math;
		int Eng;
		struct student*next;
	};
	typedef struct student data;
	data*ptr;
	data*head;
	data*new_data;
	head=(data*)malloc(sizeof(data));
	ptr=head;
	ptr->next=NULL;
	int select;
	float math_sum,math_ave=0;
	float Eng_sum,Eng_ave=0;
	int n=0;
	do{
		printf("(1) 新增 (2) 离开=>");
		scanf("%d",&select);
		if(select!=2){
		printf("姓名 学号 数学成绩 英语成绩:"); 
		scanf("%s %s %d %d",ptr->name,ptr->no,&ptr->math,&ptr->Eng);
		new_data=(data*)malloc(sizeof(data));
		ptr->next=new_data;
		new_data->next=NULL;
		ptr=ptr->next;
		n++;
	}
	}while(select!=2);
	ptr=head;
	while(ptr->next!=NULL){
		printf("姓名:%s\t 学号:%s\t 数学成绩:%d\t 英语成绩:%d\n",
		ptr->name,ptr->no,ptr->math,ptr->Eng);
		math_sum+=ptr->math;
		Eng_sum+=ptr->Eng;
		ptr=ptr->next;
	}
	math_ave=math_sum/n;
	Eng_ave=Eng_sum/n;
	printf("======================================\n");
	printf("数学成绩平均值为:%.1f 英语成绩平均值为%.1f",math_ave,Eng_ave);
}
(1) 新增 (2) 离开=>1
姓名 学号 数学成绩 英语成绩:张三 01 65 89
(1) 新增 (2) 离开=>1
姓名 学号 数学成绩 英语成绩:李四 02 98 68
(1) 新增 (2) 离开=>1
姓名 学号 数学成绩 英语成绩:王麻子 03 68 96
(1) 新增 (2) 离开=>2
姓名:张三       学号:01        数学成绩:65    英语成绩:89
姓名:李四       学号:02        数学成绩:98    英语成绩:68
姓名:王麻子     学号:03        数学成绩:68    英语成绩:96
======================================
数学成绩平均值为:77.0 英语成绩平均值为84.3

3、释放单向链表节点的空间

  • 在使用动态方式分配链表的内存后,事后必须进行释放内存的操作。除了利用free()函数或者delete运算符来释放单个节点的内存外,还可以利用单向链表的遍历技巧来收回整个链表的内存空间。算法如下:
while(first!=NULL){
    ptr=first;//first为头指针
    first=first->next;//逐一遍历
    free(ptr);
}
  • 实例
#include<stdio.h>
#include<stdlib.h>

int main(void){
	struct student {
		char name[20];
		int age;
		struct student*next;
	};
	typedef struct student data;
	data*ptr;
	data*head;
	data*new_data;
	head=(data*)malloc(sizeof(data));
	ptr=head;
	ptr->next=NULL;
	int select;
	do{
		printf("(1)新增 (2)离开 =>");
		scanf("%d",&select);
		if(select!=2){
			printf("姓名 年龄:");
			scanf("%s %d",ptr->name,&ptr->age);
			new_data=(data*)malloc(sizeof(data));
			ptr->next=new_data;
			new_data->next=NULL;
			ptr=ptr->next; 
		}
		}while(select!=2);
	ptr=head;
	while(ptr->next!=NULL){
		printf("姓名:%s\t 年龄:%d\n",ptr->name,ptr->age);
		ptr=ptr->next;
		ptr=head;
	}
	printf("========================================\n");
	while(head!=NULL){
		ptr=head;
		head=head->next;
		free(ptr);
	}
	printf("所有节点释放完毕!"); 
}
	
(1)新增 (2)离开 =>1
姓名 年龄:张三 20
(1)新增 (2)离开 =>1
姓名 年龄:李四 18
(1)新增 (2)离开 =>2
姓名:张三       年龄:20
姓名:李四       年龄:18
========================================
所有节点释放完毕!

3、单项链表插入新的节点

单向链表插入新节点有三种方式。加到第一个节点之前,加到最后一个节点之后以及加到此链表中间任意位置。

  • 新节点插入第一个节点之前,即成为此链表的首节点:只需要把新节点的指针指向链表原来的第一个节点,再把链表头指针指向新节点即可。
newnode->next=first;
first=newnode;
  • 新节点插入最后一个节点之后:只需要把链表的最后一个节点的指针指向新节点,新节点的指针再指向NULL即可。
ptr->next=newnode;
newnode->next=NULL;
  • 将新节点插入链表中间的位置:例如插入的节点是在X与Y之间,只要将X节点的指针指向新节点,新节点的指针指向Y节点即可。
newnode->next=x->next;
x->next=newnode;
  • 实例
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void){
	struct employee{
		int num,score;
		char name[10];
		struct employee *next;
	};
	typedef struct employee data;
	data*ptr;
	data*head;
	data*new_data;
	head=(data*)malloc(sizeof(data));
	ptr=head;
	ptr->next=NULL;
	int select;
	do{
		printf("(1) 新增 (2) 离开 =>");
		scanf("%d",&select);
		if(select!=2){
			new_data=(data*)malloc(sizeof(data));
			new_data->next=NULL;
			printf("姓名 序号 薪资:");
			char name1[10];
			int num1;
			int score1;
			scanf("%s %d %d",name1,&num1,&score1);
			strcpy(ptr->name,name1);
			ptr->num=num1;
			ptr->score=score1;
			ptr->next=new_data;
			ptr=ptr->next; 
		} 
	}while(select!=2);
		for(int i=0;i<3;i++){
		printf("员工编号 薪水\t");
	}
	printf("\n--------------------------------------------\n");
	ptr=head;
	int cnt=0;
	ptr=head;
	while(ptr->next!=NULL){
		printf("[%d]\t ¥%d\t",ptr->num,ptr->score);
		cnt++;
		if(cnt%3==0){
			printf("\n");
		}
		
		ptr=ptr->next;
	}
	printf("\n--------------------------------------------\n");
	int se;
	data*new_e;
	do{
		printf("\n请输入要插入其后的员工编号,如输入的编号不在此链表中,\n");
		printf("\n新输入的员工节点将视为此链表的链表头部,要结束输入过程请输入-1:\n");
		scanf("%d",&se);
		if(se!=-1){
			new_e=(data*)malloc(sizeof(data));
			new_e->next=NULL;
			printf("请输入新插入的员工编号:");
			scanf("%d",&new_e->num);
			printf("请输入新插入的员工薪水:");
			scanf("%d",&new_e->score);
			printf("请输入新插入的员工姓名:");
			scanf("%s",new_e->name); 
			new_e->next=head;
			head=new_e; 
		}
	}while(se!=-1);
	printf("员工编号 姓名 薪水\t");
	printf("\n================================\n");
	ptr=head;
	while(ptr->next!=NULL){
		printf("[%d] [%s] [%d]\t\n",ptr->num,ptr->name,ptr->score);
		head=head->next;
		ptr=head;	
	}	 
}
(1) 新增 (2) 离开 =>1
姓名 序号 薪资:张三 01 5000
(1) 新增 (2) 离开 =>1
姓名 序号 薪资:李四 02 6500
(1) 新增 (2) 离开 =>1
姓名 序号 薪资:赵四 04 5800
(1) 新增 (2) 离开 =>2
员工编号 薪水   员工编号 薪水   员工编号 薪水
--------------------------------------------
[1]      ¥5000 [2]      ¥6500 [4]      ¥5800

--------------------------------------------

请输入要插入其后的员工编号,如输入的编号不在此链表中,

新输入的员工节点将视为此链表的链表头部,要结束输入过程请输入-1:
01
请输入新插入的员工编号:03
请输入新插入的员工薪水:3500
请输入新插入的员工姓名:王麻子

请输入要插入其后的员工编号,如输入的编号不在此链表中,

新输入的员工节点将视为此链表的链表头部,要结束输入过程请输入-1:
-1
员工编号 姓名 薪水
================================
[3] [王麻子] [3500]
[1] [张三] [5000]
[2] [李四] [6500]
[4] [赵四] [5800]

--------------------------------

4、单向链表删除节点

若要在链表中删除节点,根据所删除的节点位置会有三种情况

  • 删除链表的第一个节点将链表的头指针指向第二个节点即可。
ptr=head;
head=head->next;
free(ptr);
  • 删除链表的最后一个节点,只要指向最后一个节点的指针指向NULL即可
ptr->next = tail;
ptr->next = NULL;
free(tail);
  • 删除链表内的中间节点,只要将删除节点的前一个结点的指针,指向将要被删除的下一个节点即可。
Y = ptr->next;
ptr->next = Y->next;
free(Y);
  • 实例
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void){
	struct employee{
		int num,score;
		char name[10];
		struct employee *next;
	};
	typedef struct employee data;
	data*ptr;
	data*head;
	data*new_data;
	head=(data*)malloc(sizeof(data));
	ptr=head;
	ptr->next=NULL;
	int select;
	do{
		printf("(1) 新增 (2) 离开 =>");
		scanf("%d",&select);
		if(select!=2){
			new_data=(data*)malloc(sizeof(data));
			new_data->next=NULL;
			printf("姓名 序号 薪资:");
			char name1[10];
			int num1;
			int score1;
			scanf("%s %d %d",name1,&num1,&score1);
			strcpy(ptr->name,name1);
			ptr->num=num1;
			ptr->score=score1;
			ptr->next=new_data;
			ptr=ptr->next; 
		} 
	}while(select!=2);
		for(int i=0;i<3;i++){
		printf("员工编号 薪水\t");
	}
	printf("\n--------------------------------------------\n");
	ptr=head;
	int cnt=0;
	ptr=head;
	while(ptr->next!=NULL){
		printf("[%d]\t ¥%d\t",ptr->num,ptr->score);
		cnt++;
		if(cnt%3==0){
			printf("\n");
		}
		
		ptr=ptr->next;
	}
	printf("\n--------------------------------------------\n");
	
	int se;
	do{
		printf("\n请输入要删除的员工编号,要结束删除过程请输入-1。(默认删除第一个)\n");
		scanf("%d",&se);
		if(se!=-1){
			if(se==1){
				ptr=head;
				head=head->next;
				free(ptr);
			}
		}
		
	}while(se!=-1);
	printf("员工编号 姓名 薪水\t");
	printf("\n================================\n");
	ptr=head;
	while(ptr->next!=NULL){
		printf("[%d] [%s] [%d]\t\n",ptr->num,ptr->name,ptr->score);
		head=head->next;
		ptr=head;	
	}	 
}
(1) 新增 (2) 离开 =>1
姓名 序号 薪资:1 1 1
(1) 新增 (2) 离开 =>1
姓名 序号 薪资:2 2 2
(1) 新增 (2) 离开 =>2
员工编号 薪水   员工编号 薪水   员工编号 薪水
--------------------------------------------
[1]      ¥1    [2]      ¥2
--------------------------------------------

请输入要删除的员工编号,要结束删除过程请输入-1。(默认删除第一个)
1

请输入要删除的员工编号,要结束删除过程请输入-1。(默认删除第一个)
-1
员工编号 姓名 薪水
================================
[2] [2] [2]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值