C语言数据结构-链表的基本操作(超详细)

目录:

1.单链表;

2.双链表;

3.循环链表;

一:单链表:

目录:

(1).单链表的分类;
(2).单链表的操作;
(3).代码详解;
(4).完整代码展示;

1:单链表的分类

总体上来说,单链表可以分为两大类,一种是带头结点的,另一种是不带头结点的(由于带头结点的链表实用性更强,所以以下讲解和代码均为带头结点的链表);如果分细来讲,链表的插入可以分为两种:头插法和尾插法。

2:单链表的操作

1.单链表的初始化;
2.单链表的头插法;
3.单链表的尾插法;
4.删除元素;
5.遍历链表;

3:代码详解

1.首先应该列出需要用的头文件:

#include<stdio.h>
#include<stdlib.h>  //可以用#include<malloc.h>代替

因为需要分配动态存储空间,所以要用到stdlib函数;

2.定义结构体;

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

 data为数据域,next为指针域。

3.初始化链表:

node *listcreat()
{
    node *list=(node *)malloc(sizeof(node));
    list->data=0;     //头结点的数据域存储的是该链表中的结点的个数
    list->next=NULL;
    return list;
}

建立一个返回值为node类型的指针函数,定义一个node类型的list指针并分配内存空间,

list的数据域为该链表的结点数,使头结点的next指向空,并返回该头结点的地址。

4头插法增加一个结点:

void addlist(node *list,int data)
{
    node *p=(node *)malloc(sizeof(node));
    p->data=data;
    p->next=list->next;
    list->next=p;
    list->data++;
}

建立一个新的结点p,使p的next指向原来list的next指向的位置,并使list的next指向p,list的data加一。

5.尾插法增加一个结点:

void tailadd(node *list,int data)
{
    node *p=list->next;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    node *q=(node *)malloc(sizeof(node));
    q->data=data;
    q->next=p->next;
    p->next=q;
    list->data++;
}

先使p指向该链表的最后一个结点,增加一个结点,使p的next指向新的结点,list的data加一。

6.删除一个结点:

void deletee(node *list,int n)
{
    int flag=1;
    node *p=list,*q;
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        if(n==p->data)
        {
            q->next=p->next;
            free(p);
            list->data--;
            flag=0;
            break;
        }
    }
    if(flag)
    printf("未找到该数据,请重新输入.\n");
}

这一步的难点为while循环中的q指针和p指针的纠缠,总之,q一直指向p的上一个结点,这样就好理解了。

7.遍历链表:

void print(node *list)
{
    node *p=list;
    while(p!=NULL)
    {
        printf("%d->",p->data);
        p=p->next;
    }
    printf("NULL");
}

遍历链表就是输出链表中的所有结点的数据,只需使p指向list然后while循环让p指向链表的下一个节点就OK了。注:输出的第一个数是list->data,也就是链表结点的个数。

8.main函数:

int main()
{
    node *list=listcreat();
    addlist(list,3);
    addlist(list,2);
    addlist(list,1);
    tailadd(list,4);
    tailadd(list,5);
    tailadd(list,6);
    deletee(list,4);
    print(list);
    return 0;
}

main函数中对所有函数功能都检查一遍,确保没有错误。

4:完整代码展示:

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
    int data;
    struct Node *next;
}node; 
node *listcreat()
{
    node *list=(node *)malloc(sizeof(node));
    list->data=0;     //头结点的数据域存储的是该链表中的结点的个数
    list->next=NULL;
    return list;
}
void addlist(node *list,int data)
{
    node *p=(node *)malloc(sizeof(node));
    p->data=data;
    p->next=list->next;
    list->next=p;
    list->data++;
}
void tailadd(node *list,int data)
{
    node *p=list->next;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    node *q=(node *)malloc(sizeof(node));
    q->data=data;
    q->next=p->next;
    p->next=q;
    list->data++;
}
void deletee(node *list,int n)
{
    int flag=1;
    node *p=list,*q;
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        if(n==p->data)
        {
            q->next=p->next;
            free(p);
            list->data--;
            flag=0;
            break;
        }
    }
    if(flag)
    printf("未找到该数据,请重新输入.\n");
}
void print(node *list)
{
    node *p=list;
    while(p!=NULL)
    {
        printf("%d->",p->data);
        p=p->next;
    }
    printf("NULL");
}
int main()
{
    node *list=listcreat();
    addlist(list,3);
    addlist(list,2);
    addlist(list,1);
    tailadd(list,4);
    tailadd(list,5);
    tailadd(list,6);
    deletee(list,4);
    print(list);
    return 0;
}

 

二:双链表:

目录:

(1).双链表与单链表的区别;
(2).双链表的基本操作;
(3).代码详解;
(4).完整代码展示;

1:双链表与单链表的区别:

单链表的每一个结点中只有指向下一个结点的指针,不能进行回溯;

双链表的每一个结点中既有指向下一个结点的元素,也有指向上一个结点的指针,可以快速地找到当结点的前一个结点。

2:双链表的基本操作:

1.链表的初始化;
2.头插法加入一个元素;
3.尾插法加入一个元素;
4.删除一个元素;
5.求表长;
6.遍历链表;

*注:本篇讲解的双链表是有头结点的,无头结点的链表代码操作不同。

3:代码详解:

1.头文件:

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

2.结构体的定义:

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

next指针指向该节点的下一个结点,pre指针指向该节点的上一个结点。

3.初始化链表:

node *listcreat()
{
	node *list=(node *)malloc(sizeof(node));
	if(list==NULL)
	printf("error\n");
	list->data=0;  //用来存储单链表的结点数
	list->next=NULL;
	list->pre=NULL;
	return list;
}

初始化时将next和pre指针都指向NULL,并返回头结点。

4.头插法添加元素

void addlist(node *list,int n)
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	p->pre=list;
	list->next=p;
	list->data++;
}

注意pre指针的操作。

5.尾插法添加元素:

void addtail(node *list,int n)
{
	node *p=list->next;
	while(p->next!=NULL)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=p->next;
	q->pre=p;
	p->next=q;
	list->data++;
}

定义node类型的p,并使p指向链表的最后一个节点,然后进行插入操作,注意pre指针的转换。

6.删除一个结点:

void deletee(Node *list,int n)
{
	int flag=1;
	Node *p=list,*q;
	while(p!=NULL)
	{
		q=p;
		p=p->next;
		if(n==p->data){
			if(p->next!=NULL)		   
		{
			q->next=p->next;
			p->next->pre=q;
			free(p);
			flag=0;
			list->data--;
			printf("删除成功\n");
			break;
		}
		else
		{
			free(p);
			q->next=NULL;
			flag=0;
			list->data--;
			printf("删除成功\n");
			break;
		}
	}
}	
		if(flag)
		{
			printf("删除失败\n");
		}
}

定义的p指针必须指向list用来删除第一个结点的数据,while中的需要有一个判断,判断要删除的元素是否在最后一个结点,分两种情况,以确保能删除最后一个结点。这里与单链表不同,因为单链表只需考虑next指针。而双链表不同,它需要考虑两个指针,当删除一个结点时,需要把下一个结点的pre指针指向前一个结点,因为NULL没有pre指针,所以需要分开考虑。

7.求表长:

void lengthlist(node *list)
{
	printf("该双链表的长度为:%d\n",list->data);
}

8.遍历链表:

void print(node *list)
{
	node *p=list;
	while(p!=NULL)
	{
		printf("%d->",p->data);
		p=p->next;
	}
	printf("NULL\n");
}

同样的,第一个数是链表的结点数,如果不需要,可以把list->next赋值给p,while中的p不能换成p->next,这样会导致最后一个节点的元素不能正常输出。

9.main函数:

int main(void)
{
	node *list=listcreat();
	addlist(list,3);
	addlist(list,2);
	addlist(list,1);
	addtail(list,4);
	addtail(list,5);
	addtail(list,6);
	deletee(list,1);
	lengthlist(list);
	print(list);
	return 0;
}

main函数对所有功能检查。

4:完整代码展示:

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node *next;
	struct Node *pre;
}node;
node *listcreat()//双链表的初始化
{
	node *list=(node *)malloc(sizeof(node));
	if(list==NULL)
	printf("error\n");
	list->data=0;  //用来存储单链表的结点数
	list->next=NULL;
	list->pre=NULL;
	return list;
}
void addlist(node *list,int n)//头插法
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	p->pre=list;
	list->next=p;
	list->data++;
}
void addtail(node *list,int n)//尾插法
{
	node *p=list->next;
	while(p->next!=NULL)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=p->next;
	q->pre=p;
	p->next=q;
	list->data++;
}
void deletee(Node *list,int n)//删除函数
{
	int flag=1;
	Node *p=list,*q;
	while(p!=NULL)
	{
		q=p;
		p=p->next;
		if(n==p->data){
			if(p->next!=NULL)		   
		{
			q->next=p->next;
			p->next->pre=q;
			free(p);
			flag=0;
			list->data--;
			printf("删除成功\n");
			break;
		}
		else
		{
			free(p);
			q->next=NULL;
			flag=0;
			list->data--;
			printf("删除成功\n");
			break;
		}
	}
}	
		if(flag)
		{
			printf("删除失败\n");
		}
}
void lengthlist(node *list)//求表长
{
	printf("该双链表的长度为:%d\n",list->data);
}
void print(node *list)//遍历
{
	node *p=list;
	while(p!=NULL)
	{
		printf("%d->",p->data);
		p=p->next;
	}
	printf("NULL\n");
}
int main(void)
{
	node *list=listcreat();
	addlist(list,3);
	addlist(list,2);
	addlist(list,1);
	addtail(list,4);
	addtail(list,5);
	addtail(list,6);
	deletee(list,1);
	lengthlist(list);
	print(list);
	return 0;
}

 

 三:循环链表:

目录:

(1).循环链表的定义;
(2).循环链表的分类;
(3).详细代码展示;
(4).完整代码展示;

1:循环链表的定义:

循环链表是另一种形式的链式存储结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。由此,可以从表中任一结点出发均可找到表中其他元素。

循环链表的操作和线性表基本一致,差别仅在于算法中的循环条件不是p或p->next是否指向空,而是它们是否指向头指针。

2:循环链表的分类:

循环链表可以分为单循环链表和双循环链表,接下来,将同时进行两种循环链表的代码详解,可以作比较进而更好的理解。

3:代码详解:

1.头文件:

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

两种循环链表的头文件一样;

2.结构体定义:

typedef struct Node//单循环链表结构体定义
{
	int data;
	struct Node *next;
}node;

上方是单循环链表的结构体定义,可以看出结构体有两个成员,分别是data和next指针;

typedef struct node//双循环链表的结构体定义
{
	int data;
	struct Node *next;
	struct Node *pre;
}node;

结构体有三个成员的是双循环链表;

3.初始化:

node *listcreat()//单
{
	node *list=(node *)malloc(sizeof(node));
	list->data=0;
	list->next=list;
	return list;
}

同样的定义list头结点,使它的data用来计算结点数,没有其他元素,所以list的next指针指向它本身。

node *listcreat()//双
{
	node *list=(node *)malloc(sizeof(node));
	list->data=0;
	list->next=list;
	list->pre=list;
	return list;
}

与单循环链表不同,双循环链表多了一个pre指针,因为没有其他元素,使list的next和pre指针都指向本身。

4.头插法添加元素:

void addlist(node *list,int n)//单
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	list->next=p;
	list->data++;
}

和单链表的操作一样,不需要考虑表中有没有元素。

void addlist(node *list,int n)//双
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	p->pre=list;
	list->next=p;
	list->data++;
}

相对于单循环,只是多了一个指针操作,其他原理一样。

5.尾插法添加元素:

void addtail(node *list,int n)//单
{
	node *p=list->next;
	while(p->next!=list)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=list;
	p->next=q;
	list->data++;
}

因为循环,所以while中的条件不能是NULL了,尾插元素时,新插入的元素的next指针应该指向头结点。

void addtail(node *list,int n)//双
{
	node *p=list->next;
	while(p->next!=list)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=list;
	q->pre=p;
	p->next=q;
	list->pre=q;
	list->data++;
}

双循环链表的尾插时容易忽略的一点是list的pre指针需要指向最后一个结点。

6.删除元素:

void deletee(node *list,int n)//单
{
	node *p=list,*q;
	while(p->next!=list)
	{
		q=p;
		p=p->next;
		if(n==p->data)
		{
			q->next=p->next;
			free(p);
			list->data--;
			break;
		}
	}
}

while循环中的条件list。

void detelee(node *list,int n)//双
{
	node *p=list,*q;
	while(p->next!=list)
	{
		q=p;
		p=p->next;
		if(n==p->data)
		{
			q->next=p->next;
			p->pre=q;
			free(p);
			list->data--;
			break;
		}
	}
}

这里不用考虑需要删除的结点是否位于最后一个,因为最后一个节点的next指向list而不是NULL.

7.遍历链表:

void print(node *list)
 {
 	node *p=list->next;
 	while(p!=list)
 	{
	 	printf("%d->",p->data);
	 	p=p->next;
	 }
	 printf("NULL\n");
 }

单循环双循环的遍历操作都是一样的,注意这里输出的第一个元素并不是头结点的data,定义的p指针只能指向list的next,否则while中的循环无法进行。

8.main函数:

 int main()
 {
 	node *list=listcreat();
 	addlist(list,3);
 	addlist(list,2);
 	addlist(list,1);
  	addtail(list,4);
 	addtail(list,5);
  	addtail(list,6);
 	deletee(list,4);
 	print(list);
 	return 0;
 }

用相同的main函数来检验操作是否正确。

4:完整代码展示:

单循环链表:

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node *next;
}node;
node *listcreat()//单
{
	node *list=(node *)malloc(sizeof(node));
	list->data=0;
	list->next=list;
	return list;
}
void addlist(node *list,int n)
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	list->next=p;
	list->data++;
}
void addtail(node *list,int n)
{
	node *p=list->next;
	while(p->next!=list)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=list;
	p->next=q;
	list->data++;
}
void deletee(node *list,int n)
{
	node *p=list,*q;
	while(p->next!=list)
	{
		q=p;
		p=p->next;
		if(n==p->data)
		{
			q->next=p->next;
			free(p);
			list->data--;
			break;
		}
	}
}
 void print(node *list)
 {
 	node *p=list->next;
 	while(p!=list)
 	{
	 	printf("%d->",p->data);
	 	p=p->next;
	 }
	 printf("NULL\n");
 }
 int main()
 {
 	node *list=listcreat();
 	addlist(list,3);
 	addlist(list,2);
 	addlist(list,1);
  	addtail(list,4);
 	addtail(list,5);
  	addtail(list,6);
 	deletee(list,4);
 	print(list);
 	return 0;
 }

双循环链表:

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node *next;
	struct Node *pre;
}node;
node *listcreat()
{
	node *list=(node *)malloc(sizeof(node));
	list->data=0;
	list->next=list;
	list->pre=list;
	return list;
}
void addlist(node *list,int n)
{
	node *p=(node *)malloc(sizeof(node));
	p->data=n;
	p->next=list->next;
	p->pre=list;
	list->next=p;
	list->data++;
}
void addtail(node *list,int n)
{
	node *p=list->next;
	while(p->next!=list)
	{
		p=p->next;
	}
	node *q=(node *)malloc(sizeof(node));
	q->data=n;
	q->next=list;
	q->pre=p;
	p->next=q;
	list->pre=q;
	list->data++;
}
void detelee(node *list,int n)
{
	node *p=list,*q;
	while(p->next!=list)
	{
		q=p;
		p=p->next;
		if(n==p->data)
		{
			q->next=p->next;
			p->pre=q;
			free(p);
			list->data--;
			break;
		}
	}
}
void print(node *list)
{
	node *p=list->next;
	while(p!=list)
	{
		printf("%d->",p->data);
		p=p->next;
	}
	printf("NULL");
}
int main()
{
	node *list=listcreat();
	addlist(list,4);
	addlist(list,3);
	addlist(list,2);
	addlist(list,1);
	addtail(list,5);
	addtail(list,6);
	addtail(list,7);
	addtail(list,8);
	detelee(list,8);
	print(list);
	return 0;
}
  • 32
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值