数据结构(C语言版)全程更新第二篇震撼来袭

单链表的使用

      在数据结构中,根据元素之间的不同特性,通常有下列4类基本结构:集合、线性结构、树形结构、图状结构或网状结构。本次,笔者将会奉上豪华的链表套餐,希望新学者可以更快的掌握单链表的插入、删除操作。
线性结构中有两种存储结构,一种是顺序存储结构,一种是链式存储结构,本次,只会接受链式存储结构,之后会补更顺序存储结构。
顺序存储结构查找比较方便,但是插入和删除操作需要移动大量元素,因此插入和删除较为麻烦,而单链表却刚好与之相反,它插入和删除比较方便,但查找却麻烦。因此何时使用链式和顺序,需要根据自己的需求合理使用,从而达到效率最大化。
对于链表,之前我们介绍过了,它的插入和删除比较方便,因此在实际应用中,我们常常会使用到这两个操作,因为笔者不怎么会使用编辑器,不会画动态图,所以可能很难把这个问题给大家演示清楚,但是笔者还是会尽力在代码中多加注释,方便大家的理解。如果大家有课本的话,对照课本中的动态图,再结合笔者的注释和通俗易懂的代码,相信大家都能很快的掌握对于单链表的使用。加油数据结构(C语言版)全程更新第二篇震撼来袭
/*
1、程序设计规划(实现的功能、分几个模块、子函数)
2、 编写单链表创建子函数
3、 编写单链表插入某个结点的子函数                提示:在插入到第i个位置时,要先找到第i-1的结点,然后操作
4、 编写单链表删除某个结点的子函数                提示:在删除到第i个位置时,也要先找到第i-1的结点,然后操作
5、 编写单链表查询某个结点的子函数
6、 编写单链表显示子函数
7、 编写主函数Main(),通过功能菜单调用子函数
8、 编译调试程序
*/
//本程序中函数名的定义规则:多单次组合时第一个单次首字母小写,第二个单词开始每个单次首字母大写(采用了java中的方法的命名规则)
#include<stdio.h>
#include<stdlib.h>

typedef int Status;			//在这里给int起别名Status
typedef int ElemType;		//在这里给int起别名ElemType

//建立结构体,通过结构体建立链表指针、next域、data域
typedef struct LNode{
	ElemType data;             //data域,用来存放结点中的值
	struct LNode *next;		   //用来指向下一个结点
}LNode,*LinkList;
//打印链表
void printList(LinkList &L)
{
    LinkList p;	//定义变量p
	p=L->next;		//将p变为第一个结点(头结点的下一个)
    while(p!=NULL){  //当p不空时候循环
        printf("%d\t",p->data);	//输出结点p对应的值;
        p=p->next;				//令p指向p的下一个结点
    }
    printf("\n");
}
//建立一个创造链表并初始化的函数
void creatList(LinkList &L,Status number)	//number为链表中结点的个数
{      
	L=(LinkList)malloc(sizeof(LNode));	   //为L动态分配内存空间
	if(L==NULL){						   //增强代码的健壮性
		printf("动态内存分配失败\n");
		exit(0);                           //退出程序
	}
	L->next=NULL;    //将表置空
	LinkList p=L;	  //创建将结点p并指向L
	LinkList q;      //创建结点q
	 //初始化链表
 	for(int i=1;i<=number;i++){        //循环语句
		q=(LinkList)malloc(sizeof(LNode));      //给结点q动态开辟空间
		if(q==NULL){                    		//同理在增强代码健壮性
			printf("为结点动态分配空间失败:");
			exit(0);
		}
		p->next=q;              // 将结点p的下一个结点指向q
		p=q;					// 将结点p移动到q的位置
		printf("data:");
		scanf("%d",&(q->data)); // 给q的data域赋值
		q->next=NULL;			// 将q的下一个置空
	}
}
//该函数的功能为给第location个位置插入元素data
void insertList(LinkList &L,int location,int data)//location表示链表的第location个结点 , data表示为插入的数为data
{     
	LinkList p=L;					//创建将结点p并指向L
	LinkList q;						//创建结点q
	q=(LinkList)malloc(sizeof(LNode));     //为q动态分配内存空间
	if(q==NULL){                    		//同理在增强代码健壮性
		printf("为结点动态分配空间失败:");
		exit(0);
	}
	//思考为什么是要找到location-1的位置,将p放到location-1这个位置 可以画图方便理解
	for(int i=1;i<=location-1;i++){
		p=p->next;
	}
	q->next=p->next;		//将q的指向的下一个结点指向p目前的下一个结点,这样就断开了p和p的下一个结点直接的线
	p->next=q;				//接着将p的下一个结点指向q这样q就插入进来了
	q->data=data;              //这里的=后边的data是函数传来的data,别混淆了
	printList(L);      //打印新的链表
}
//删除链表中的某个值的函数
void deleteList(LinkList &L,int location) //location表示要删除的第某个结点
{     
	LinkList p=L;	     //创建将结点p并指向L
	LinkList q;			 //创建结点q
	//为什么在q没有分配内存空间时候一样可以使用?
	q=(LinkList)malloc(sizeof(LNode));
	if(q==NULL){                    		//同理在增强代码健壮性
		printf("为结点动态分配空间失败:");
		exit(0);
	}
	Status data;		 //用于存储被删除的结点所对应的值,并输出
	for(int i=1;i<=location-1;i++){        //还是找位置location-1
		p=p->next;
	}
	q=p->next;                   //将q放在p的下一个结点
	p->next=q->next;			//断开p指向q的线,使线指到q的next的位置。相当于把q架空了
	q->next=NULL;				//使结点q的下一个指向置空
	data=q->data;				//用data存储将要被删除的结点q中的值
	free(q);					//释放q
	printf("删除的结点的数值为:%d",data);
	printf("\n");
	printf("删除后的链表为:");   //打印新的链表
	printList(L);
}
//查找链表中某个位置结点的值,因为之前的插入和删除操作的使用,链表的值已经变化了,不再是第一你输入的时候的链表值了。所以别混淆了
void seekList(LinkList L,int location)           //location表示的是查找的那个元素的位置
{ 
	LinkList p=L;								 //创建将结点p并指向L
	for(int i=1;i<=location;i++){                //遍历链表找到你想操作的那个结点
		p=p->next;
	}
	printf("你所查找的位置的结点的值为:");
	printf("%d",p->data);                       //输出该结点data域中的值
	printf("\n");
}
//销毁链表
void destoryList(LinkList &L)
{
	LinkList p=L;             //到了这里相信大家都发现了这个。每个函数都出来一次,为什么?自己思考下
	while(L){	 //销毁链表时候要一个结点一个结点的删除,所以这里采用了循环语句
		p=L->next;			 //一个结点一个结点的删除
		free(L);
		L=p;
	}
	printf("线性表已被成功销毁\n");
}
//菜单函数,实现各项功能
void Menu(void)
{
	LinkList L;
	Status a;				//用于控制开关
	Status number;				//用于指示链表中结点的个数
	Status location,data;			//用于函数传参时候数据的操作

	while(1){
		printf("\t\t---------实现单链表的创建请按1:---------\n");
	 	printf("\t\t---------插入单链表的结点请按2:---------\n");
	 	printf("\t\t-------删除单链表的某个结点请按3:-------\n");
	 	printf("\t\t---------实现单链表的查找请按4:---------\n");
	 	printf("\t\t---------实现单链表的销毁请按5:---------\n");
	 	printf("\t\t---------------退出请按6:---------------\n");
	 	scanf("%d",&a);
        switch(a){
	 		case 1:
	 		  	printf("请输入链表的结点个数:");
				scanf("%d",&number);
	 		  	creatList(L,number);					//创建链表      //在调用函数时参数切不可写成这样creatList(&L,number);注意下初学者比较容易犯错
	 		    printf("是否需要打印出链表中的值:(是请按T,否请按N)\n");
	 	 	    char flag;                //在判断时候用的变量
				scanf("%s",&flag);		//scanf("%c",&flag) 会被跳过scanf不信你试试 (*^__^*) ……
				if(flag=='T'){
				    printList(L);
				}
				else{       //这里的代码是为了增强健壮性,觉得太冗长的人可以注释掉
					if(flag=='N'){
					    break;
					}
					else{
						printf("输入有误请按要求输入\n");
						break;
					}
				}
			  	break;
		    case 2:
		     	printf("请输入你想在第几个位置插入元素:");
		     	scanf("%d",&location);
		     	printf("\n");
				printf("请输入你要插入的元素:");
				scanf("%d",&data);
				insertList(L,location,data);                    //插入链表,在第location个位置插入元素data
			  	break;
			case 3:
			 	printf("请输入你想删除第几个位置的元素:");
				scanf("%d",&location);
			  	deleteList(L,location);						//删除链表中的某个位置的元素
			  	break;
			case 4:
			 	printf("输入你想查看的第几个结点的值:");
			 	scanf("%d",&location); 						//查找第location个结点
			 	seekList(L,location);					   //寻找链表中的某个结点
			  	break;
			case 5:
			 	destoryList(L); 					   //使用完了销毁链表
			  	break;
			case 6:
			 	exit(0);
			  	break;
        }
    }
}
//主函数 用来调用子函数
Status main(void)
{
	Menu();
	system("pause");
	return 0;
}



;