数据结构与算法学习(day7)——链表操作

前言

本章我们学习链表。

(1)温故而知新。数据结构这块,我们前面已经学了队列和栈的操作了,并且也有简单的实例可供大家练习,不知道大家在学习和复习之中有没有对知识有新的理解;我的感触是颇多的,刚开始学习队列和链表的时候,敲程序很陌生,思路不连贯,但是思考清楚和多加练习之后,我对知识点就更加熟悉了,代码敲得也更加流畅,感觉爽了不少,但是还是需要继续努力学习。

(2)今天的内容会比以前难一些(不过也不难),在存储一大波数的时候,我们通常用的是数组,但有的时候数组显得不够灵活,比如在对一组数中按大小顺序插入其他数时……

(3)但是我们用链表的方法,就会以一种比较简单方式解决问题。

本章的学习目标:

(1)理解指针和动态分配内存函数malloc

(2)理解链表操作的原理

(3)能够使用链表解决简单的实际问题

题目场景

有一串已经从小到打排好序的数2 3 5 8 9 10 18 26 32。现需要往这串数中插入6使其得到的新序列仍符合从小到大的顺序。

如何实现链表呢?

在C语言中可以使用指针和动态分配内存函数malloc来实现。

1-malloc动态存储

回想一下,我们想在程序中存储一个整数10,除了使用int a;这种方式在内存中申请一块区域来存储,还有另外一种动态储存方法。

malloc(4);

malloc函数的作用就是从内存中申请分配指定字节大小的内存空间。上面这行代码就申请了4个字节。如果你不知道int类型是4个字节的,还可以用sizeof(int)获取int类型所占用的字节数,如下:

malloc(sizeof(int));

现在你已经成功从内存中申请了4个字节的空间来准备存放一个整数,可是如何来对这个空间进行操作呢?这里我们就需要用一个指针来指向这个空间,即存储这个空间的首地址。

int *p;
p = (int *)malloc(sizeof(int));

需要注意,malloc函数的返回类型是void*类型。void星表示未确定类型的指针。在C和C++中,

void*

类型可以强制转换为任何其他类型的指针。上面我们将其强制转化为整型指针,以便告诉计算机这里的4个字节作为一个整体用来存放整数。

ok,现在我们可以通过指针p对刚才申请的4个字节的空间进行操作了,例如我们向这个空间中存入整数10,如下

*p = 10;

完整代码如下,注意当在程序中使用malloc函数时需要用到stdlib.h头文件。

#include 
#include 

int main()
{
	int *p; //定义一个指针p
	p = (int *)malloc(sizeof(int)); //指针p获取动态分配的内存空间地址
    *p = 10;  //向指针p所指向的内存空间存入10
    printf("%d",*p); //输出指针p所指向的内存中的值
    
    return 0}

运行结果是

10

2-链表的每一个结点应该如何储存?

每一个链表都由两部分组成。左边的部分用来存放具体的数值,右边的部分需要村粗下一个结点的地址,可以用指针来实现(也称为后继指针)。那么,左边用一个整型变量就可以。

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

上面的代码中,我们定义了一个叫做node(结点)的结构体类型,这个结构体类型有两个成员。第一个是整型data,用来存储具体的数值;第二个成员是一个指针,用来村粗下一个结点的地址。因为下一个结点的类型也是struct node,所以这个指针的类型也必须是

struct node *

类型的指针。

3-如何建立链表?

首先我们需要一个头指针head指向链表的最开始。当链表还没有建立的时候头指针head为空(也可以理解为指向空结点)。

struct node *head;
head = NULL;  //头指针初始为空

现在我们来创建第一个结点,并用临时指针p指向这个结点。

struct node *p;

//动态申请一个空间,用来存放一个结点,并用临时指针指向这个结点
p = (struct node *)malloc(sizeof(struct node));

接下来分别设置新创建的这个结点的左半部分和右半部分。

scanf("%d",&a);
p->data = a;  //将数据存储到当前结点的data域中
p->next = NULL;  //设置当前结点的后继指针向空,也就是当前结点的下一个结点为空

上面的代码中,->叫做结构体指针运算符,也是用来访问内部成员的。

因为p是一个指针,所以不能使用.号访问内部成员,而是使用->。

下面设置头指针和新创建的结点。

		if (head == NULL)
			head = p;  //如果这是第一个创建的结点,则头指针指向这个结点
		else
			q->next = p;  //如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点

最后要将指针q也指向当前结点,因为待会临时指针p将会指向新创建的结点。

q = p;  //指针q也指向当前结点

完整代码如下

#include 
#include 

//这里创建一个结构体用来表示链表的结点类型
struct node
{
	int data;
	struct node *next;
};

int main()
{
	struct node *head, *p, *q, *t;
	int i, n, a;
	
	scanf("%d",&n);
	head = NULL;  //头指针初试为空
	q = NULL;  
	//读取一串从小到大的数字并存入链表
	for (i = 1; i <= n; i++)
	{
		scanf("%d",&a);

		//申请一个动态空间,用来存放一个结点,并用临时指针p指向这个结点 
		p = (struct node *)malloc(sizeof(struct node));  //每次申请的都不同
		p->data = a;  //将数据存储到当前结点的data域中
		p->next = NULL;  //设置当前结点的后继指针向空,也就是当前结点的下一个结点为空

		if (head == NULL)
			head = p;  //如果这是第一个创建的结点,则头指针指向这个结点
		else
			q->next = p;  //如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点

		q = p;  //指针q也指向当前结点
	}
	
	//输出链表中的所有数
	t = head;
	while (t != NULL)
	{
		printf("%d ",t->data);
		t = t->next;  //继续下一个结点
	}

	return 0;
}

4-下面往链表中插入6

首先用一个临时指针t从链表的头部开始遍历。

t = head;  //从链表头部开始遍历

等到指针t的下一个结点的值比6大的时候,将6插入到中间。即t ->next->data大于6时进行插入,代码如下

scanf("%d",&a);  //读入待插入的数
while (t != NULL)  //当没有到达链表尾部的时候循环
	{
		if (t->next->data > a) //如果当前结点的下一个结点的值大于待插入数,将数插入到中间
		{
			p = (struct node*)malloc(sizeof(struct node)); //动态申请一个空间,用来存放新增结点
			p->data = a;
			p->next = t->next;  //新增结点的后继指针指向当前结点的后继指针
			t->next = p;  //当前结点的后继指针指向新增结点
			break;  //插入完毕,退出循环
		}
		t = t->next; //继续下一个结点
	}

完整代码如下

#include 
#include 

//这里创建一个结构体用来表示链表的结点类型
struct node
{
	int data;
	struct node *next;
};

int main()
{
	struct node *head, *p, *q, *t;
	int i, n, a;
	
	scanf("%d",&n);
	head = NULL;  //头指针初试为空
	q = NULL;  
	//读取一串从小到大的数字并存入链表
	for (i = 1; i <= n; i++)
	{
		scanf("%d",&a);

		//申请一个动态空间,用来存放一个结点,并用临时指针p指向这个结点 
		p = (struct node *)malloc(sizeof(struct node));  //每次申请的都不同
		p->data = a;  //将数据存储到当前结点的data域中
		p->next = NULL;  //设置当前结点的后继指针向空,也就是当前结点的下一个结点为空

		if (head == NULL)
			head = p;  //如果这是第一个创建的结点,则头指针指向这个结点
		else
			q->next = p;  //如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点

		q = p;  //指针q也指向当前结点
	}

	scanf("%d",&a);  //读入待插入的数
	t = head;  //从链表头部开始遍历
	while (t != NULL)  //当没有到达链表尾部的时候循环
	{
		if (t->next->data > a) //如果当前结点的下一个结点的值大于待插入数,将数插入到中间
		{
			p = (struct node*)malloc(sizeof(struct node)); //动态申请一个空间,用来存放新增结点
			p->data = a;
			p->next = t->next;  //新增结点的后继指针指向当前结点的后继指针
			t->next = p;  //当前结点的后继指针指向新增结点
			break;  //插入完毕,退出循环
		}
		t = t->next; //继续下一个结点
	}

	//输出链表中的所有数
	t = head;
	while (t != NULL)
	{
		printf("%d ",t->data);
		t = t->next;  //继续下一个结点
	}

	return 0;
}

你可能感兴趣的:(数据结构与算法(C语言),学习,链表,数据结构)