C语言链表基础知识和例题

C语言线性链表

什么是线性链表?

链式存储的通俗解释:
首先先介绍一下链式存储,链式存储就是当C语言储存数据时,数据不是在内存上一个挨着一个存储的,而是跳跃着存储的,在存储一个数据时,同时也存着下一个数据的地址(就是告诉你下一个数据在哪)。
什么是线性呢:
一根线上的一小段前后都只有一个链接的地方,在数据中的线性的意思就是一个数据,前面和他直接有关的只有一个数据,后面和他直接有关也只有一个数据,数据像穿成了一条线一样。
线性链表也叫单向链表。

线性链表的实现

一.链表中每一个元素是如何定义的
定义:结构体和指针/自引用结构

struct node{
	ElemType data;
	struct node *link;
};

说明:
结构体里面的元素:
第一行elemtype是数据类型,data是变量名,
第二行是一个指针,这个指针的类型是指向一个这样结构体的指针。这就是一个自引用结构体,也就是结构体里的一个指针,这个指针指向的数据是由他所在的结构体定义的。
建议使用typedef来定义这个结构体的名字和结构体类型指针的名字。

struct node{
	ElemType data;
	struct node *link;
	}
typedef struct node Node;
typedef struct node* Nodeptr;
Nodeptr list,p;

说明:
最后一行就是定义了两个指向struct node类型结构体的指针。

几种定义结构体的方法:

struct student{
	int id;
	char *name;
	struct student* link;
	};
struct student a1,a2;

这是最简单的一种方法,定义一个结构体类型,然后定义两个变量a1,a2。
方法二:

struct student{
	int id;
	char *name;
	struct student *link;
	};
typedef struct student Student;
typedef struct srudent* Studentptr;
Student a1,a2; //定义两个变量
Studentptr p1,p2; //定义两个指针

说明:这就是上文建议使用的typedef,他就是把这个数据结构体的名字用你自定义的符号代替。比如合理Student就是代替struct student;
倒数第二行定义了两个变量,a1,a2;
最后一行定义了两个指向这种结构体的指针,p1,p2;

二. 创建一个链表
我们需要考虑的就是创建元素(结构体),在元素里面输入数据,把元素连接起来形成链表;

Noteptr createList(int n)
{
	Nodeptr p,q,list=NULL;
	int i;
	for(i=0;i<n;i++)
	{
		q=(Nodeptr)malloc(sizeof(Node));
		q->data=read();
		q->link=NULL;
		
		if(list==NULL)
		list=p=q;
		else
		p->link=q;
	p=q;
	}
	return list;	
}

代码的理解:


```c
Nodeptr createList(int n)//定义含有n个元素的链表
{//p是链表最后一个元素,q是新增的,list是第一个
	Nodeptr p,q,list=NULL; 
	int i;
	for(i=0;i<n;i++)
	{
		p=(Nodeptr)malloc(sizeof(Node));//开空间
		p->data=read();//附上数据
		p->link=NULL;//指针初始化
		
		if(list==NULL) //如果链表为空
		list=p=q;
		else //把q连进链表
		p->link=q;
	p=q;	//把q连在p之后,p就不是组后一个了,q是最后一个
		//把p赋给q,让p再变成最后一个
	}
	return list;
}

总结一下,三步走战略
新增元素,空间数据和链接
链接元素
控制末尾

线性表的操作

一. 求链表的长度
中心思想:遍历,计数

int getlength(Nodeptr list)
{
	Nodeptr p;
	int n=0;
	for(p=list;p!=NULL;p=p->link)
		n++;
	return n;
}

代码解释:

int getlength(Nodeptr list)//传进来需要计数的链表
{
	Nodeptr p;//创造一个结点用来遍历
	int n=0;	//计数器
	for(p=list;P!=NUll;p=p->link)	//让p指向下一个结点
		n++;	//循环用来计数,如果不是最后一个就加1
	return n;	//返回元素的个数
}

总结一下:三步走:
结点,计数器,遍历

二. 插入
1.在第一个节点前插入

list=insertFirst(list,item);

Nodeptr insertFirst(Nodeptr list,Elemtpye item)
{
	Nodeptr p;
	p=(Nodeptr)malloc(sizeof(Node))
	p->data=item;
	p->link=list;
	return p;
}
list=insertFirst(list,item)
//传入的时候,相当于传入了指针的值,无法改变值,所以return p
Nodeptr insertFirst(Nodeptr list,Elemtype item)
{
	Nodeptr p;
	p=(Nodeptr)malloc(sizeof(Node);
	p->data=item;
	p->link=list;
	return p;
}

2.在p指针所指的结点之后插入一个数据

void insertNode(Nodeptr p;ELemType item)
{
	Nodeptr q;
	q=(Nodeptr)malloc(sizeof(Node));
	q->data=item;
	q->link=p->link;
	p->link=q;
}

这个不用把list传进来,因为p是个指针,直接对p下手就行,不涉及遍历和链表中的其他元素。

3.在第n个结点后面插入
这个就可以看出来跟上一个不一样,需要传进来list来数出第n个节点

void insertNode(Nodeptr list;int n;ELemType item)
{
	Nodeptr q,p=list;
	int i;
	for(i=1;i<=n-1;i++){
		if(p->link==NULL)
			break;
		p=p->link;
	}
	q=(Nodeptr)malloc(sizeof(Node))
	q->data=item;
	q->link=p->link;
	p->link=q;
}

出循环的时候p指向的是第n个结点

4.有序链表中,插入一个结点
比如递增链表

list=insertNode(list,tiem);
Nodeptr insertNode(Nodeptr list,ElemTpye item)
{
	Nodeptr p,q,r;
	r=(Nodeptr)malloc(sizeof(Node));
	r->data=item;
	r->link=NULL;
	if(list==NULL)
		return r;
	for(p=list;item>p->data&&p!=NULL;q=p,p=p->link)
		;
	if(p==list){
		r->link=list;
		return r;
	}
	else{
	q->link=r;
	r->link=p;
	}
	return list;
}

代码理解:

list=insertNode(list,item);
Nodeptr insertNode(Nodeptr list,ElemType item)
{//p是r后面的元素,q是r前面的元素。
	Nodeptr p,q,r;
	r=(Nodeptr)malloc(sizeof(Node))
	r->data=item;
	r->link=NULL;
	if(list==NULL) //如果list中还没有元素
		return r;
	for(p=list;item>p->data&&p!=NULL;q=p,p=p->link)
		;
	if(p==list){
		r->link=p;
		return r;	
	}
	else{
		q->link=r;
		r->link=p;
		}
		return list;
}

三. 删除

  1. 从非空线性表中删除p指向的连接点
    a.假设r是p的前驱节点(前面的那一个结点)
Nodeptr deletep(Nodeptr list,Nodeptr r,Nodeptr p)
{
	if(p==list)
		list=p->link;
	else
		r->link=p->link;
	free(p);
	return list;
}

提示:一定要考虑要删除的结点是第一个结点这种情况。
b.我们不知道p的前驱指针时,就要先找到p的前驱指针,再对p下手;

Nodeptr deletep(Nodeptr list,Nodeptr p)
{
	Nodeptr r;
	if (p==list){
		list=p->link;
		free(p);
		}
	else{
		for(r=list;r->link!=p&&r->link!=NULL;r=r->link)
			;
		if(r->link!=NULL){
		r->link=p->link;
		free(p);
		}
	}
	return list;
}

要注意的是,p结点不一定在这里,所以for循环里的判断和后面的条件语句都要特别注意。
删除的时候不要忘记free!!!

链表中注意事项总结:
1.上一个结点
2.插入到第一个元素
3.链表为空
4.一个节点后面第k个节点需要将p=p->link执行k-1遍。
5.传入list如果list变了的话,在函数中是没有办法修改的,所以要return地址。
6.涉及到查找的操作(查找删除)一定要考虑没找到这种情况。

总结:首,无,空(三个特殊情况)

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值