用C语言完成一个链表,同时又有好多知识点需要复习

2024-04-17 03:58

本文主要是介绍用C语言完成一个链表,同时又有好多知识点需要复习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 
//接着写链表,想当初,大二的时候学习数据结构,那真是痛苦的要命
//现在想想,其实当时不理解没关系,后来慢慢就理解了。
//以至于后来一度以为自己不适合学计算机,加油!一起努力学习吧。
//废话不多说,今天实现的是一个“基于链表的功能”
//仍然模拟一种场景,学生姓名存储表。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>//后续使用malloc函数,需要包含此头文件
//如果编译器支持,也可以直接包含malloc.h文件
#define OK 1
#define ERROR 0
typedef char string[20]; //定义一个字符串数组类型的,长度为20;相当于char XXX[20]
struct LinkNode{
string data;
struct LinkNode * next;
};//链表,一个数据域,一个指针域。指针域指向的是本结构体
typedef struct LinkNode *LinkList;//这个熟悉吧,数据类型重定义,不懂得可以自己百度
typedef struct LinkNode ListNode;
//在此处我不禁问一下自己,能否用define定义结构体呢?
//小伙伴们知道吗?那我就再次普及一下吧:
/********************typedef和define区别**********************
**************************************************************
1、执行时间不同
#define定义的是宏定义,为可读的常量以及一些宏语句的任务
,而typedef是类型的别名,用来给类型重新起名字。typedef在编译阶段有效,由于是
在编译阶段,所以typedef有类型检查的功能。define则是宏定义,发生在预处理阶段,
编译之前,它只进行简单的字符串替换,不进行任何检查。
2、功能不同
typedef用来定义类型的别名,定义与平台无关的数据,与struct的结合使用等。
define可以为类型取别名,还可以定义常量,变量等。
3、作用域不同
define没有作用域的限制,不管在函数内,还是函数外都是从定义开始直到整个文件的结尾,
直到文件的结尾;而typedef有自己的作用域。如果定义在所有函数外,它的作用域
就是从它定义开始到文件结尾,如果放在函数内,则是从定义开始,到函数结束为位置。
相同点就是:
1、不管是typedef还是define都不能在定义之前使用!
2、typedef受函数范围的影响,而define不受
3、不管是typedef还是define,其作用域都不会扩展到别的文件,即使同一个程序的其它文件!!!
***************************************************************************************
************************完美分割线,别跑神,赶紧回来继续写链表************************/
//定义了链表的结构,下面来声明如下实现的函数:
int Link_init(LinkList *L);
//Function:初始化一个线性链表。
int Link_add(LinkList L,string data,int place);
//Function: 在头指针L指向的链表中,增加一个元素data,位置为place
int Link_delete(LinkList L,int place,string data);
//Function: 删除线性表L中,第place位置上的元素,返回给data
int Link_modify(LinkList L,int place,string data);
//Function: 修改线性表L中,第place位置上的元素,使其数据为data
void Link_trival(LinkList L);
//Function: 遍历一个线性表,此处并没有修改数据,所以不用传地址,也就不需要指针类型
int Link_len(LinkList L);
//Function: 计算线性表的长度
int Link_empty(LinkList L);
//Funciton: 判断线性表是否为空
void Link_destroy(LinkList L);
//Function: 销毁一个链表操作
LinkList Link_get_elem(LinkList L,int place);
//Function: 获取线性表L的,第place个元素的结点
//先来实现第一个函数:为什么不直接写实现,防止上下层函数之间有相互调用,所以先在此处声明
int Link_init(LinkList *L)//定义一个结构体指针的指针,因为需要修改结构体指针的指向,即结构体指针的内容
{
*L = (LinkList)malloc(sizeof(ListNode));//如果分配地址成功,则返回地址,否则为NULL
if(*L == NULL)//最好写成 NULL == *L 如果少写一个=不至于会报错。左边为符号常量,不能进行改变!
{
perror("fail to locate the memory!");//perror定义在stdio.h中,如果出错会输出参数+失败的原因。
return ERROR;
}
(*L)->next = NULL;
return OK;
}
int Link_add(LinkList L,string data,int place)
{
// printf("Link_add");此处不能放在第一行,否则会报错
LinkList pre,p;
int j = 0;
pre = L;//L此时是头指针
while(pre->next!=NULL && j<place -1)
{
pre = pre->next;
j++;//想要插入正确的位置,要找到插入之前的一个元素
}
if(j != place-1)
{
printf("没有找到元素!@_@\n");
return ERROR;
}
if((p = (LinkList)malloc(sizeof(ListNode))) == NULL)
return ERROR;
 strcpy(p->data,data);
// p->data = data;
p->next = pre->next;//插入操作
pre->next = p;
return OK;
}
int Link_delete(LinkList L,int place,string data)
{
LinkList  pre,q;
pre = L;
if(pre->next == NULL)
{
printf("链表长度为空,无法删除");
return ERROR;
}
if(place >= Link_len(pre) + 1)
{
printf("需要删除的位置比链表长度还大,不科学啊!@_@\n");
return ERROR;
}
pre = Link_get_elem(L,place-1);//获取头指针L指向的链表的元素,传给data
//data = pre->data;
strcpy(data,pre->next->data);//由于涉及字符串操作,所以要用到字符串拷贝函数
q = pre->next;    //字符串的拷贝,不是简单的赋值。
pre->next = pre->next->next;
free(q);
return OK;
}
/*特别提醒自己一下:还有一个函数memcpy(void *dest,const *src,Size_t n)
它与strcpy的区别如下:
1、复制内容不同,strcpy只能复制字符串,而后者可以复制任意内容,例如字符数组,整型,结构体等
2、复制方法不同,strcpy不需要指定长度,遇到\0才结束,所以容易溢出。后者根据给定的长度进行复制。
3、通常字符串的拷贝用前者,需要复制别的类型数据用memcpy()函数
要注意内存重叠的情况,比如以下例子:(网上找的)
char test[] = "abcdefghi";
memcpy(test+3,test,6);
如果从头到尾的复制,结果是abcabcabc,正确的结果其实应该是abcabcdef。这种情况下应该从尾向头复制。
*/
int Link_modify(LinkList L,int place,string data)
{
LinkList pre;
pre = L;
if(pre == NULL)
{
printf("链表长度为空,无法修改");
return ERROR;
}
if(place >= Link_len(pre) + 1)
{
printf("需要修改的位置比链表长度还大,不科学啊!@_@\n");
return ERROR;
}
pre = Link_get_elem(L,place);
// pre->data = data;//获取位置place结点指针,并对数据域修改即可
strcpy(pre->data,data);
return OK;
}
void Link_trival(LinkList L)
{
//在这里我要说明一下自己的错误
//我其实传进去的是头指针,那么头指针指向头结点的数据域需要访问么?
//很显然是不能访问的,因为没有意义。
/*
while(L->next!=NULL){
printf("%s",L->data);//我这样写,当然不对啦!第一个先访问的就是头结点的数据域,没有意义。
L = L->next;
*/
LinkList pre = L->next;
int times = 0;
if(pre == NULL){
printf("线性表为空,无法遍历!");
return ;
}
while(pre != NULL)
{
printf("遍历的第%d个数据为%s\n",++times,pre->data);
pre = pre->next;
}
}
int Link_len(LinkList L)
{
int len = 0;
while(L->next != NULL){
++len;
L = L->next;
}
return len;
}
int Link_empty(LinkList L)
{
if(L->next == NULL)
{
printf("Link_empty:为空@-@\n");
return OK;  //1
}
else
{
return ERROR; //0
}
}
void Link_destroy(LinkList L)
{
LinkList pre,q;
pre = L;
while(pre != NULL)
{
q = pre;
pre = pre->next;
free(q);
}
printf("销毁完成!");
}
LinkList Link_get_elem(LinkList L,int place)
{
LinkList pre;
pre = L;
if(pre == NULL)
{
printf("链表长度为空,无法修改");
return ERROR;
}
if(place >= Link_len(pre) + 1)
{
printf("需要修改的位置比链表长度还大,不科学啊!@_@\n");
return ERROR;
}
while(place-- && pre != NULL){
pre = pre->next;
}
return pre;
}
int main(void){
//如下测试,先增加元素,遍历元素,再修改元素,遍历元素,最后删除元素
string data= "";
LinkList L = NULL;
Link_init(&L);
Link_add(L,"小文",1);
Link_modify(L,1,"小李");
Link_trival(L);
Link_add(L,"wang",2);
Link_trival(L);
Link_delete(L,2,data);
printf("%s\n",data);
Link_trival(L);
Link_destroy(L);
 /* char test[] = "abcdefghi";
memcpy(test+3,test,6);
printf("%s",test);测试结果为从尾向前复制*/
}

这篇关于用C语言完成一个链表,同时又有好多知识点需要复习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910737

相关文章

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

python安装完成后可以进行的后续步骤和注意事项小结

《python安装完成后可以进行的后续步骤和注意事项小结》本文详细介绍了安装Python3后的后续步骤,包括验证安装、配置环境、安装包、创建和运行脚本,以及使用虚拟环境,还强调了注意事项,如系统更新、... 目录验证安装配置环境(可选)安装python包创建和运行Python脚本虚拟环境(可选)注意事项安装

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl