链表的概念与单链表的深度解析

链表的概念:
链表的引入是为了解决数组的插入和删除的窘境,可以把链表看作为一个动态扩展(实时变大/缩小)的数组。链表是由多个节点相互连接组成的,连接是通过指针实现的;所说的节点并非一点,而是一段,包括有效数据区和指针,有效数据局用来存储数据,指针用来指向下个节点整体。
单链表解析:
单链表结构框图
对于链表(包括单链表和双链表)而言,重点理解概念,学会怎么用和实现原理。重点理解链表的插入、遍历、删除。
单链表实现举例:

#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
/***********用来构建链表中的一个节点************/

// 构建一个链表的节点
struct node
{
	int data;				// 有效数据
	struct node *pNext;		// 指向下一个节点的指针
};

/***************************************************/


/****************用来创建链表中的一个节点*************/

// 返回值:指针,指针指向我们本函数新创建的一个节点的首地址
//data为节点中存储的数据
struct node * create_node(int data)
{
	struct node *p = (struct node *)malloc(sizeof(struct node));
	if (NULL == p)
	{
		printf("malloc error.\n");
		return NULL;
	}
	// 清理申请到的堆内存
	memset(p, 0,sizeof(struct node));
	// 填充节点
	p->data = data;
	p->pNext = NULL;	
	return p;
}

/************************************************/

/********************用来实现链表节点的尾插入***************/

void insert_tail(struct node *pH, struct node *new)
{
	// 思路:由头指针向后遍历,直到走到原来的最后一个节点。原来最后一个节点里面的pNext是NULL,现在我们只要将它改成new就可以了。添加了之后新节点就变成了最后一个。
   // 计算添加了新的节点后总共有多少(cnt)个节点,然后把这个数写进头节点中
  //pH为头指针,new为新节点的整体首地址
	int cnt = 0;
	// 分两步来完成插入
	// 第一步,先找到链表中最后一个节点
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;				// 往后走一个节点
		cnt++;
	}
	// 第二步,将新节点插入到最后一个节点尾部
	p->pNext = new;
	pH->data = cnt + 1;  //表示的为头结点中的数据区(也就是存储节点个数的地方)
}

/************************************************/


/********************用来实现链表节点的头插入***************/

void insert_head(struct node *pH, struct node *new)
{
	// 第1步: 新节点的next指向原来的第一个节点
	new->pNext = pH->pNext;
	// 第2步: 头节点的next指向新节点的地址
	pH->pNext = new;
	// 第3步: 头节点中的计数要加1
	pH->data += 1;
}

/************************************************/


/********************用来实现链表的遍历***************/

// 遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来
void traversing_list(struct node*pH)
{
	//pH->data			// 头节点数据,不是链表的常规数据,不要算进去了
	struct node *p = pH;		// 头指针后面是头节点
	printf("-----------开始遍历-----------\n");
	while (NULL != p->pNext)  //p->pNext表示的是跳过头节点(也就是第一个有效节点) 是不是最后一个节点
	{
		p = p->pNext;		// 走到下一个节点,也就是循环增量
		printf("node data: %d.\n", p->data);
	}
	printf("-------------遍历完成-------------\n");
}

/************************************************/


/********************用来实现从链表中删除节点***************/

// 从链表pH中删除节点,data为待删除的节点的是数据区中的数据
int delete_node(struct node*pH, int data)
{
	// 找到这个待删除的节点,通过遍历链表来查找
	struct node *p = pH;			// 用来指向当前节点
	struct node *pPrev = NULL;		// 用来指向当前节点的前一个节点
	while (NULL != p->pNext)		// 是不是最后一个节点
	{
		pPrev = p;					// 在p走向下一个节点前先将其保存
		p = p->pNext;				// 走到下一个节点,也就是循环增量
		// 判断这个节点是不是我们要找的那个节点
		if (p->data == data)
		{
			// 找到了节点,处理这个节点
			// 分为2种情况,一个是找到的是普通节点,另一个是找到的是尾节点
			/*删除节点的困难点在于:通过链表的遍历依次访问各个节点,找到这个节点后p指向了这个节点,但是要删除这个节点关键要操作前一个节点,但是这时候已经没有指针指向前一个节点了,所以没法操作。解决方案就是增加一个指针指向当前节点的前一个节点(pPrev)*/
			if (NULL == p->pNext)
			{
				// 尾节点
				pPrev->pNext = NULL;		// 原来尾节点的前一个节点变成新尾节点
				free(p);					// 释放原来的尾节点的内存
			}
			else
			{
				// 普通节点
				pPrev->pNext = p->pNext;	// 要删除的节点的前一个节点和它的后一个节点相连,这样就把要删除的节点给摘出来了
				free(p);
			}
			// 处理完成之后退出程序
			return 0;
		}
	}
	// 到这里还没找到,说明链表中没有我们想要的节点
	printf("没找到这个节点.\n");
	return -1;
}

/************************************************/
int main(void)
{
	struct node *pHeader = create_node(0); //创建一个头节点
	insert_head(pHeader, create_node(11));
	insert_head(pHeader, create_node(22));
	insert_head(pHeader, create_node(33))
	printf("------------------删除前遍历-------------\n");
	traversing_list(pHeader);
	delete_node(pHeader, 22);
	printf("------------------删除后遍历-------------\n");
	traversing_list(pHeader)
	return 0;
}

注:程序的主函数功能为,利用头插的方式创建具有3个节点的链表,然后对链表进行遍历,在将链表中的一个节点进行删除,然后再进行遍历。
对于单链表来说只要能掌握本程序的内容即可,因为程序中包含了头指针、头节点、节点的创建、节点的头插入、节点的尾插入、链表的遍历、链表节点的删除重点内容。

发布了19 篇原创文章 · 获赞 11 · 访问量 3400

猜你喜欢

转载自blog.csdn.net/m0_46204326/article/details/104224036
今日推荐