C语言创建一个单链表实现键盘输入数据以及增删查

作为一个编程小白,在这里发表自己的第一篇博客,心里还是有点紧张。

写博客的目的主要还是在于让自己对所学的知识进行充分的掌握,企图在写分析的过程当中可以达到“柳暗花明又一村”的境界,当然如果可以帮助到别人的话自己心里也肯定有辣么一点点成就感。

因为刚接触到链表,所以今天就想和大家一起探讨一下链表。想弄清楚链表,对指针的概念一定要很清楚。我们都知道,链表是由一个又一个节点构成,每个节点又分为数据域和指针域(eg:p->data/p->next)。

举一个简单的例子来描述节点:
在这里插入图片描述
这是一个简单的线性表的节点(无序的),当我们把它整理成一个又一个的节点是它是这样的:
在这里插入图片描述
画的不是很好看,但应该很好理解吧。图一中数据域就相当于p->data,指针域相当于p->next。

假设p是指向线性表中的第i个数据元素(a(i))的指针,那么p->next就是指向第i+1个数据元素,即a(i+1)。也就是说,p->data=a(i),p->next->data=a(i+1)

简单搞清楚指针的用法后我们来完成一个动态单链表的创建(键盘输入)。如果还是没有搞懂的同学一定要多画几次图来理解,因为博主也是一个小白,可能讲的不是很清楚。

我们先来分析一下,第一步,我们先写头文件,然后定义结构体变量

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
	
	int data;
	
	node *next;
	
}node;

接下来就可以写创建单链表的函数了,我用的是尾插法

node *create(){//尾插法创建链表 
	
	node *p,*q,*head;//定义三个指针,其中p类似一个辅助指针吧,用来存放所输入的链表数据 
	
	int n;//表示想要输入的链表数据个数 
	
	int i = 0;//表示已经输入的链表数据个数
	
	head = (node*)malloc(sizeof(node));//创建头节点
	
	head->next = NULL;//可以理解为初始化链表,让它为空表 
	
	q = head;//q始终指向尾节点,开始时头节点和尾节点时同一个
	
	printf("你想要输入多少个数据:");
	
    scanf("%d",&n);
	
	printf("请输入数字:");
	
	for(i = 0;i < n;i++){
		
		p = (node*)malloc(sizeof(node));//创建数据节点,让输入的数据先存放在p里面 
		
		scanf("%d",&p->data);// 输入链表数据 
		
		p->next = q->next;
	
	    q->next = p;
	
	    q = p;
	}
	
	return head;

}

创建了单链表,当然得让它输出了,接下来写一个输出函数

void print(node *head){//输出链表 
	
	node *p; 
	
	p = (node*)malloc(sizeof(node));//给p分配内存空间,使它可以存放数据 

	p = head->next;//让p指向开始节点 

	printf("输出单链表里的所有元素:"); 
	
	while(p != NULL){//当p不是空表时执行下面语句 
		
		printf("%d ",p->data);
		
		p = p->next;
	}
	
}

计算单链表的长度

int ListLength(node *head){//计算单链表的长度 

    printf("\n"); 
	
	node *p = head;//让p指向头节点 
	
	int i = 0;
	
	while(p->next != NULL){//当p的后继节点不为空时执行下面语句 
		
		p = p->next;
		
		i++;//执行一次循环加1,直到p的后继节点为空时跳出循环 
		
	}
	
	printf("单链表的长度:%d",i);

	return 0;
}

按照数据值查找(数据n所在的位置是第几个)

int LoacteElem(node *head){//按元素值查找 

    printf("\n"); 
	
	node *p = head;//让p指向头节点 
	
	int j = 0,e;
	
	printf("输入要查找的数据值:");
	
	scanf("%d",&e);
	
	while(p->next != NULL && p->data != e){
		
		p = p->next;
		
		j++;
		
	}
	
	printf("查找的数据的位置:%d",j);
	
	return 0;
}

按照数据的位置查找(第n个位置的数据为)

int GetElem(node *head){//查找元素 
	
	printf("\n");
	
	int i,e,m;//i的作用相当于指针遍历到第几个节点、e表示想要查找第几个位置的元素、m表示找到想要查找的数据的值) 
	
	node *p = head;//同样的套路 
	
	printf("请输入要查找数据的位置:"); 
	
	scanf("%d",&e);
	
	for(i=0;i<e;i++){
	
		p = p->next;

	}
	
	m = p->data;
	
	printf("位置为%d的数据是%d",e,m);

	return 0;
}

接下来是插入函数

node *Insert(node *head){//插入元素 

    printf("\n");
	
	node *p = head;
	
	node *q;
	
	int j = 0,i,e;//同样,j表示指针p遍历到链表数据的位置,i、e表示要在第i个位置插入数据e) 
	
	printf("输入要插入的元素的位置:");
	
	scanf("%d",&i);
	
	printf("输入要插入的元素的值:");
	
	scanf("%d",&e); 
	
	for(j = 0;j < i-1;j++){
		
		p = p->next;
		
	} 
	
	q = (node*)malloc(sizeof(node));
	
	q->data = e;//最后几步大家如果理解不了可以试着画图,多画几次,搞清楚数据域和指针域到底指的是什么就变得很简单了 
	
	q->next = p->next;
	
	p->next = q;
	
	return head;

}

然后是删除函数

node *Delete(node *head){//删除元素 

    printf("\n");
	
	node *p = head;//老套路 
	
	node *q,*r;
	
	int j = 0,i;//j表示指针p遍历到链表的第几个数据,i表示删除第i个位置的数据 
	
	printf("请输入删除数据的位置:");
	
	scanf("%d",&i);
	
	for(j = 0;j < i-1;j++){
		
		p = p->next;
		
	}
	
	q = (node *)malloc(sizeof(node));

	q = p;
	
	r=q->next;
	
	p->next=r->next;

	return head;
}

然后是主函数

int main(){
	
	node *head;
	
	head = create();//调用创建链表函数(尾插法) 
	
	print(head);//调用输出函数,输出链表的所有数据 

	ListLength(head);//求链表的长度 

	LoacteElem(head);//按数据值查找(第n个数据的位置是第m个) 
	
	GetElem(head);//查找数据(第n个位置的数据是第m个) 
	
	Insert(head);//调用插入函数 
	
	print(head);//输出插入后的链表 
	
	Delete(head);//调用删除函数 
	
	print(head);//输出删除后的链表 
	
	printf("\n");
	
	return 0;
}

最后,总结部分:以上函数按顺序添加至编译器(Dev-C++和vs2019)中可以正常运行,但是根据vs2019编译器的规则需将(”scanf“改为”scanf_s“),而且会出现警告(不是错误是警告)(eg:取消NULL指针对p的应用),该解决方法是在出现问题的函数中添加

if(p == NULL{

    return 0;

}

该警告的出现是基于vs编译器的严谨,如果不添加该代码块那么万一p为空表的话运行将会出现问题(vs知道p是空表,它不知道应该运行出什么样的结果)但是我们知道我们运行的时候一定会输入数据,所以它不会为空。(ps:vs好严谨)

不足之处:不能实现删除重复项以及排序问题

自我评价:个人感觉代码没有讲的很清楚,因为插入和删除应该再仔细讲讲,但是由于自身也是一个小白,很多东西可以用嘴说出来,写却不知道怎么写,如果大家把开头的两幅图搞明白的话,写这些函数的时候多画图理解,根据自己的想法写代码,不要按照书上或者网上的代码按部就班,多练习几次就会很熟悉了。

个人心得:这次写博客让我自己对指针的理解确实是提升了一个档次(不是瞎说),写之前脑海里有画图的概念,却不知道怎么画。在写的过程中因为要将这些东西清楚的说明出来,那么自己一定要很熟悉。于是就多看书,查资料,在上传每一段代码前自己又在本子上过了一遍,确认无误后才复制上去。所以还是建议大家对于自己不会的东西一定要持之以恒,花时间搞清楚它,当自己真的从朦胧到熟悉,那种感觉真的很舒服。

最后,就希望路过的大佬可以提一点建议,也希望许多和我一样的小萌新可以共同努力,一起在编程这条路”走到黑“

  • 70
    点赞
  • 282
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
抱歉,我只是一名语言模型,无法提供运行代码。但我可以为您提供一个单链表的创建、增删的示例代码,并详细注释,供您参考。 ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点的结构体 typedef struct node { int data; // 数据域 struct node *next; // 指针域,用于指向下一个节点 } Node, *LinkedList; // 创建链表 LinkedList createLinkedList() { LinkedList head = (LinkedList)malloc(sizeof(Node)); // 创建头结点 head->next = NULL; return head; } // 插入节点 void insertNode(LinkedList head, int data, int pos) { int i = 0; LinkedList p = head; while (p != NULL && i < pos-1) { // 找到要插入位置的前一个节点 p = p->next; i++; } if (p == NULL || i > pos-1) { // 未找到要插入位置的前一个节点,或插入位置不合法 printf("插入位置不合法\n"); return; } LinkedList newNode = (LinkedList)malloc(sizeof(Node)); // 创建新节点 newNode->data = data; newNode->next = p->next; p->next = newNode; } // 删除节点 void deleteNode(LinkedList head, int pos) { int i = 0; LinkedList p = head; while (p != NULL && i < pos-1) { // 找到要删除位置的前一个节点 p = p->next; i++; } if (p == NULL || p->next == NULL || i > pos-1) { // 未找到要删除位置的前一个节点,或删除位置不合法 printf("删除位置不合法\n"); return; } LinkedList q = p->next; // 要删除的节点 p->next = q->next; free(q); } // 修改节点 void modifyNode(LinkedList head, int data, int pos) { int i = 0; LinkedList p = head->next; while (p != NULL && i < pos-1) { // 找到要修改的节点 p = p->next; i++; } if (p == NULL || i > pos-1) { // 未找到要修改的节点,或修改位置不合法 printf("修改位置不合法\n"); return; } p->data = data; } // 找节点 int searchNode(LinkedList head, int data) { int pos = 0; LinkedList p = head->next; while (p != NULL && p->data != data) { // 找到数据为data的节点 p = p->next; pos++; } if (p == NULL) { // 未找到数据为data的节点 return -1; } return pos; } // 输出链表 void printLinkedList(LinkedList head) { LinkedList p = head->next; while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } int main() { LinkedList L = createLinkedList(); // 创建链表 int data, pos, choice; while (1) { printf("\n请选择要进行的操作:\n"); printf("1. 插入节点\n"); printf("2. 删除节点\n"); printf("3. 修改节点\n"); printf("4. 找节点\n"); printf("5. 输出链表\n"); printf("6. 退出\n"); scanf("%d", &choice); switch (choice) { case 1: // 插入节点 printf("请输入要插入的数据和位置(用空格隔开):\n"); scanf("%d %d", &data, &pos); insertNode(L, data, pos); break; case 2: // 删除节点 printf("请输入要删除的位置:\n"); scanf("%d", &pos); deleteNode(L, pos); break; case 3: // 修改节点 printf("请输入要修改的数据和位置(用空格隔开):\n"); scanf("%d %d", &data, &pos); modifyNode(L, data, pos); break; case 4: // 找节点 printf("请输入要找的数据:\n"); scanf("%d", &data); pos = searchNode(L, data); if (pos == -1) { printf("未找到数据为%d的节点\n", data); } else { printf("数据为%d的节点在第%d个位置\n", data, pos+1); } break; case 5: // 输出链表 printf("链表:"); printLinkedList(L); break; case 6: // 退出 exit(0); default: printf("输入不合法,请重新输入!\n"); break; } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值