2.单向链表

Posted codemagiciant

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.单向链表相关的知识,希望对你有一定的参考价值。

1.为什么需要链表?

  链表是一种灵活的数据结构,它允许在内存中动态地存储和操作元素。以下是一些需要使用链表的原因:

1. 动态数组的缺点:数组的大小是在程序运行时固定的,如果需要添加或删除元素,就需要重新分配内存并复制数据。这会导致大量的内存浪费和性能问题。而链表可以动态地调整大小,只需要增加或删除节点即可。

2. 插入和删除操作的效率:在链表中插入和删除元素比在数组中高效得多。因为链表中的节点不需要移动整个数组,只需要修改指针指向即可。这使得链表在处理大量数据时具有更高的效率。

3. 随机访问的效率:链表的随机访问效率较低,因为需要从头结点开始遍历整个链表才能找到目标元素。但是,链表可以通过哈希表等数据结构来优化随机访问效率。

4. 适用场景:链表适用于那些需要频繁插入和删除元素的场景,例如缓存、队列、栈等数据结构。此外,链表还可以用于实现一些高级算法,如广度优先搜索(BFS)和深度优先搜索(DFS)。

总之,链表是一种非常有用的数据结构,它可以在某些情况下提供比数组更好的性能和灵活性。

2.链表基本概念

链表(Linked List)是一种数据结构,它允许在单个内存位置中存储多个元素。与数组不同,链表中的元素不是连续存储的,而是通过指针相互连接。链表的主要优点是动态地添加和删除元素,而不需要像数组那样需要预先分配固定大小的空间。

链表的基本概念包括:

1. 头结点(Head Node):链表的第一个节点通常被称为头结点。头结点本身不存储实际的数据,它仅用于指向链表的下一个节点。

2. 尾节点(Tail Node):链表的最后一个节点通常被称为尾节点。尾节点同样不存储实际的数据,它仅用于指向链表的第一个节点的前一个节点。

3. 指针(Pointer):指针是一个变量,它存储另一个变量的地址。在链表中,指针用于连接各个节点。每个节点包含一个指向下一个节点的指针,这样可以形成一个单向链。

4. 访问(Accessing):要访问链表中的某个元素,首先需要找到该元素所在的节点。然后,可以通过解引用指针来访问该节点的数据部分。例如,如果有一个指向头结点的指针p,可以使用*p来访问头结点的数据部分。

5. 插入(Insertion):在链表中插入一个新元素时,需要先找到合适的位置。可以将新元素插入到头结点之后、尾节点之前的位置,或者尾节点之后的位置。具体操作取决于所选位置是否为空闲状态。插入操作可能涉及修改指针的指向,以便将新元素链接到正确的位置。

6. 删除(Deletion):从链表中删除一个元素时,需要找到该元素所在的节点。然后,可以将该节点的指针设置为其后继节点的指针,从而删除该节点。删除操作可能涉及修改指针的指向,以便更新整个链表的结构。

3.链表的形成方式

链表是由包含数据的多个结点前后连接形成的链式结构
1.结点:
每一个结点是一小片连续的、存放了数据的内存空间,是形成链表的基本单元
2.结点的构成:
每个结点由两方面内容组成:
(1)数据域:
真正要处理的数据,可以是单个基本类型的数据,也可以是多个不同类型的数据共同构成。
(2)指针域:
一般用来存放另一结点的首地址,即指针域是用来指向另一个结点的(或者赋值为NULL,即不指向任何结点。)

4.简单链表的形成

链表正是通过指针域将各个结点有机地联接成一个整体。设有A、B、C、D四个结点,按下图形成链表
注意
(1)head:设计的头部指针, 指向链表头结点
(2)NULL:尾部结点的指针域为空,不指向任何结点。
(3)前一节点的指针域存放下一结点的首地址

节点结构体:

//节点结构体
struct LinkNode

	//数据域
	void * data;
	//指针域
	struct LinkNode * next;
;

链表结构体

//链表结构体
struct LList

	//头节点
	struct LinkNode pHeader;
	//链表长度
	int m_size;
;
//不让用户直接访问LList
typedef void * LinkList;

————————————————
版权声明:本文为CSDN博主「刘鑫磊up」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liu17234050/article/details/110502629

5.链表的操作

5.1初始化链表

LinkList init_LinkList()

	 struct LList * myList = malloc(sizeof(struct LList));

	 if (myList == NULL)
	 
		 return NULL;
	 

	 myList->pHeader.data = NULL;
	 myList->pHeader.next = NULL;
	 myList->m_size = 0;

	 return myList;


//初始化链表
LinkList mylist = init_LinkList();

5.2链表的插入操作

//需要做插入操作的链表是list,插入位置是pos,插入的数据是data
void insert_LinkList(LinkList list, int pos, void * data)
	if (list == NULL)
	
		return;
	
	if ( data == NULL)
	
		return;
	
	//将list还原成 struct LList数据类型
	struct LList * myList = list;
	if (pos < 0 || pos > myList->m_size)
	
		//无效位置 强制做尾插
		pos = myList->m_size;
	

	//找到插入节点的前驱节点位置
	struct LinkNode * pCurrent = &myList->pHeader;

	for (int i = 0; i < pos;i++)
	
		pCurrent = pCurrent->next;
	
	//pCurrent 要插入节点的前驱

	//创建新节点

    struct LinkNode * newNode = malloc(sizeof(struct LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	//建立节点关系
	newNode->next = pCurrent->next;
	pCurrent->next = newNode;

	//更新链表长度
	myList->m_size++;


//准备数据
struct Person p1 =  "亚瑟", 18 ;
struct Person p2 =  "妲己", 20 ;
struct Person p3 =  "安琪拉", 19 ;
struct Person p4 =  "凯", 21 ;
struct Person p5 =  "孙悟空", 999 ;
struct Person p6 =  "李白", 999 ;

//插入数据
insert_LinkList(mylist, 0, &p1);
insert_LinkList(mylist, 0, &p2);
insert_LinkList(mylist, -1, &p3);
insert_LinkList(mylist, 0, &p4);
insert_LinkList(mylist, 1, &p5);
insert_LinkList(mylist, 0, &p6);

5.3链表的遍历操作

//遍历链表
void foreach_LinkList(LinkList list, void(*myForeach)(void *))

	if (list ==NULL)
	
		return;
	

	struct LList * mylist = list;

	struct LinkNode* pCurrent = mylist->pHeader.next;

	for (int i = 0; i < mylist->m_size;i++)
	
		myForeach(pCurrent->data);
		pCurrent = pCurrent->next;
	


//遍历
struct Person

	char name[64];
	int age;
;

void myPrintPerson(void * data)

	struct Person * p = data;
	printf("姓名:%s  年龄:%d\\n", p->name, p->age);


foreach_LinkList(mylist, myPrintPerson);

5.4链表的删除操作

//删除链表  按位置
void removeByPos_LinkList(LinkList list, int pos)

	if ( list == NULL)
	
		return;
	

	struct LList * mylist = list;

	if (pos < 0 || pos > mylist->m_size - 1)
	
		return;
	

	//找到待删除节点的前驱节点
	struct LinkNode * pCurrent = &mylist->pHeader;
	for (int i = 0; i < pos;i++)
	
		pCurrent = pCurrent->next;
	

	//记录待删除的节点
	struct LinkNode * pDel = pCurrent->next;

	//重新建立节点关系
	pCurrent->next = pDel->next;

	free(pDel);
	pDel = NULL;

	//更新链表长度
	mylist->m_size--;

//按照值删除链表
void removeByValue_LinkList(LinkList list, void* data, int(*myCompare)(void*, void*))

	if (list == NULL)
	
		return;
	
	if (data == NULL)
	
		return;
	

	struct LList* mylist = list;
	//创建两个辅助指针
	struct LinkNode* pPrev = &mylist->pHeader;
	struct LinkNode* pCurrent = pPrev->next;

	for (int i = 0; i < mylist->m_size; i++)
	
		//pCurrent->data  data 将两个指针比较利用回调 交给用户
		if (myCompare(pCurrent->data, data))
		
			pPrev->next = pCurrent->next;

			free(pCurrent);
			pCurrent = NULL;

			mylist->m_size--;
			break;
		

		//没找到 辅助指针后移
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	

5.5清空链表

//清空链表
void clear_LinkList(LinkList list)

	if (list == NULL)
	
		return;
	

	struct LList* mylist = list;

	struct LinkNode* pCurrent = mylist->pHeader.next;

	for (int i = 0; i < mylist->m_size; i++)
	
		struct LinkNode* pNext = pCurrent->next;
		free(pCurrent);
		pCurrent = pNext;
	

	mylist->pHeader.next = NULL;
	mylist->m_size = 0;

5.6返回链表长度

//返回链表长度
int  size_LinkList(LinkList list)

	if (list == NULL)
	
		return -1;
	

	struct LList* mylist = list;

	return mylist->m_size;

5.7销毁链表

//销毁链表
void destroy_Linklist(LinkList list)

	if (list == NULL)
	
		return;
	

	//清空链表
	clear_LinkList(list);
	free(list);
	list = NULL;

5.8完整代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//节点结构体
struct LinkNode

	//数据域
	void* data;
	//指针域
	struct LinkNode* next;
;

//链表结构
struct LList

	//头结点
	struct LinkNode pHeader;
	//链表长度
	int m_size;
;

typedef void* LinkList;

//初始化链表
LinkList init_LinkList()

	struct LList* myList = malloc(sizeof(struct LList));

	if (myList == NULL)
	
		return NULL;
	

	myList->pHeader.data = NULL;
	myList->pHeader.next = NULL;
	myList->m_size = 0;

	return myList;


//插入链表
void insert_LinkList(LinkList list, int pos, void* data)

	if (list == NULL)
	
		return;
	
	if (data == NULL)
	
		return;
	

	//将list还原成struct LList数据类型
	struct LList* myList = list;
	if (pos < 0 || pos > myList->m_size)
	
		//无效位置 强制做尾插
		pos = myList->m_size;
	

	//找到插入节点的前驱节点位置
	struct LinkNode* pCurrent = &myList->pHeader;
	
	for (int i = 0; i < pos; i++)
	
		pCurrent = pCurrent->next;//通过循环找到待插入位置的前驱节点
	
	//pCurrent要插入节点的前驱
	
	//创建新节点
	struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	//建立节点关系
	newNode->next = pCurrent->next;
	pCurrent->next = newNode;

	//更新链表长度
	myList->m_size++;


//遍历链表
void foreach_LinkList(LinkList list, void(* myForeach)(void *))

	if (list == NULL)
	
		return;
	

	struct LList* mylist = list;

	struct LinkNode* pCurrent = mylist->pHeader.next;//头结点数据域没必要访问

	for (int i = 0; i < mylist->m_size; i++)
	
		myForeach(pCurrent->data);
		pCurrent = pCurrent->next;
	


//测试
struct Person

	char name[64];
	int age;
;

void myPrintPerson(void* data)

	struct Person* p = data;
	printf("姓名:%s,年龄:%d\\n", p->name, p->age);

void test01()

	//准备数据
	struct Person p1 =  "亚瑟", 18 ;
	struct Person p2 =  "妲己", 20 ;
	struct Person p3 =  "安其拉", 19 ;
	struct Person p4 =  "凯", 21 ;
	struct Person p5 =  "孙悟空", 899 ;
	struct Person p6 =  "李白", 899 ;

	//初始化链表
	LinkList mylist = init_LinkList();

	//插入数据insert
	insert_LinkList(mylist, 0, &p1);
	insert_LinkList(mylist, 0, &p2);
	insert_LinkList(mylist, -1, &p3);
	insert_LinkList(mylist, 0, &p4);
	insert_LinkList(mylist, 1, &p5);
	insert_LinkList(mylist, 0, &p6);

	//李白 凯 孙悟空 妲己 亚瑟 安其拉
	//遍历
	foreach_LinkList(mylist, myPrintPerson);


int main()

	test01();
	system("pause");
	return EXIT_SUCCESS;

参考资料来源:

黑马程序员

单向链表

  单向链表每个节点由两个成员组成:一个是数据域,一个是指向自身结构的指针类型成员。如:

    struct slist

    {

      int data;

      struct slist *next;

    };

    typedef struct slist SLIST;

  单向链表的基本算法包括:链表的建立、节点数据域的输出、节点的插入和删除。

  (1) 建立带有头结点的单向链表

  建立单向链表的主要步骤如下:

  1. 读取数据
  2. 生成新节点
  3. 将数据存入节点的成员变量中
  4. 将新节点插入到链表中,重复上述操作直至输入结束。

  例1 编写函数 creat_slist,建立带有头结点的单向链表。节点数据域中的数值从键盘输入,以 -1 作为输入结束标志。链表头结点的地址由函数值返回。

program

技术分享
 1 //建立带有头结点的单向链表
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 
 5 //声明结构体类型 SLIST
 6 struct slist
 7 {
 8     int data;
 9     struct slist *next;
10 };
11 typedef struct slist SLIST;
12 
13 //声明链表建立函数
14 SLIST* creat_slist();
15 void print_slist(SLIST* head);
16 void insert_snode(SLIST* head, int x, int y);
17 
18 int main()
19 {
20     SLIST *head;
21     head = creat_slist();    //调用链表建立函数,得到头结点地址
22     print_slist(head);
23     return 0;
24 }
25 
26 //定义链表建立函数
27 SLIST* creat_slist()
28 {    
29     //s指向新生成的节点;r指向链表的尾节点
30     SLIST *h, *s, *r;
31     int c;
32 
33     h = (SLIST*)malloc(sizeof(SLIST));
34     r = h;
35     scanf("%d", &c);        //读入数据
36     
37     while (c != -1)            //-1作为输入结束标志
38     {
39         s = (SLIST*)malloc(sizeof(SLIST));        //生成新节点
40         s->data = c;        //将读入的数据存入新节点的数据域
41         r->next = s;        //新节点链接到表尾
42         r = s;                //r指向当前表尾
43         scanf("%d", &c);    //读入数据
44     }
45     r->next = \\0;            //链表结束标志
46     return h;
47 }
48 
49 //顺序输出链表各节点数据
50 void print_slist(SLIST* head)
51 {
52     SLIST* p;
53     p = head->next;            //p指向头结点后的第一个节点
54     
55     if (p == \\0)
56     {
57         printf("Linklist is null !\\n");    //链表为空(只有头结点)
58     }
59     else
60     {
61         printf("head");
62         do
63         {
64             printf("->%d", p->data);    //输出当前节点数据域中的值
65             p = p->next;                //p指向下一节点
66         } while (p != \\0);            //未到链表尾,循环继续
67     }
68 }
69 
70 //在值为x的节点前插入值为y的节点
71 void insert_snode(SLIST* head, int x, int y)
72 {
73     SLIST *s, *p, *q;
74     s = (SLIST*)malloc(sizeof(SLIST));    //生成新节点
75     s->data = y;                        //在新节点中存入y值
76     q = head;
77     p = head->next;                        //工作指针初始化,p指向第一个节点
78     while ( (p != \\0) && (p->data != x) )
79     {
80         q = p;
81         p = p->next;                    //q指向p的前趋节点
82     }
83     //x存在,插在x之前;x不存在,p的值为NULL,插在表尾
84     s->next = p;
85     q->next = s;
86 }
View Code

  技术分享

  我们在函数中定义了一个名为 h 的指针变量,用于存放头结点的地址;另外还定义了两个工作指针:s 和 r,其中指针 s 用来指向新生成的节点,指针 r 总是指向当前链表的尾节点。每当把 s 所指的新开辟的节点连接到表尾后,r 便移向这一新的表尾节点,这时再用 s 去指向下一个新开辟的节点,就是使 r 承上,用 s 启下。

  链表最后一个节点的指针域中置 ‘\\0‘(NULL),作为单向链表的结束标志。链表建成后,头结点的地址作为 creat_slist 函数的返回值由 return 语句返回并赋给 main 函数中的指针变量 head,因此函数的类型应该是基类型为 SLIST 的指针类型。

  调用 creat_slist1 函数时,若一开始就输入-1,控制流程不会进入 while 循环,而直接执行循环之后的 r->next = ‘\\0‘; 语句。这时建立的是一个空链表,其结构如图1所示。由此可见,判断此链表是否为空链表,可用条件:h->next == ‘\\0‘;

技术分享

图1 带有头结点的空链表

  (2) 顺序访问链表中各节点的数据域

  所谓“访问”,可以理解为取各节点的数据域中的值进行各种运算、修改各节点的数据域中的值等一系列的操作。

  输出单向链表各节点数据域中的内容的算法比较简单,只需利用一个工作指针 p ,从头到尾依次指向链表中的每个节点;当指针指向某个节点时,就输出该节点数据域中的内容,直到遇到链表结束标志为止。如果是空链表,就只输出相关信息并返回调用函数。

  例2 编写函数 print_slist,顺序输出单向链表各节点数据域中的内容。

program

技术分享
 1 //建立带有头结点的单向链表
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 
 5 //声明结构体类型 SLIST
 6 struct slist
 7 {
 8     int data;
 9     struct slist *next;
10 };
11 typedef struct slist SLIST;
12 
13 //声明链表建立函数
14 SLIST* creat_slist();
15 void print_slist(SLIST* head);
16 void insert_snode(SLIST* head, int x, int y);
17 
18 int main()
19 {
20     SLIST *head;
21     head = creat_slist();    //调用链表建立函数,得到头结点地址
22     print_slist(head);
23     return 0;
24 }
25 
26 //定义链表建立函数
27 SLIST* creat_slist()
28 {    
29     //s指向新生成的节点;r指向链表的尾节点
30     SLIST *h, *s, *r;
31     int c;
32 
33     h = (SLIST*)malloc(sizeof(SLIST));
34     r = h;
35     scanf("%d", &c);        //读入数据
36     
37     while (c != -1)            //-1作为输入结束标志
38     {
39         s = (SLIST*)malloc(sizeof(SLIST));        //生成新节点
40         s->data = c;        //将读入的数据存入新节点的数据域
41         r->next = s;        //新节点链接到表尾
42         r = s;                //r指向当前表尾
43         scanf("%d", &c);    //读入数据
44     }
45     r->next = \\0;            //链表结束标志
46     return h;
47 }
48 
49 //顺序输出链表各节点数据
50 void print_slist(SLIST* head)
51 {
52     SLIST* p;
53     p = head->next;            //p指向头结点后的第一个节点
54     
55     if (p == \\0)
56     {
57         printf("Linklist is null !\\n");    //链表为空(只有头结点)
58     }
59     else
60     {
61         printf("head");
62         do
63         {
64             printf("->%d", p->data);    //输出当前节点数据域中的值
65             p = p->next;                //p指向下一节点
66         } while (p != \\0);            //未到链表尾,循环继续
67     }
68 }
69 
70 //在值为x的节点前插入值为y的节点
71 void insert_snode(SLIST* head, int x, int y)
72 {
73     SLIST *s, *p, *q;
74     s = (SLIST*)malloc(sizeof(SLIST));    //生成新节点
75     s->data = y;                        //在新节点中存入y值
76     q = head;
77     p = head->next;                        //工作指针初始化,p指向第一个节点
78     while ( (p != \\0) && (p->data != x) )
79     {
80         q = p;
81         p = p->next;                    //q指向p的前趋节点
82     }
83     //x存在,插在x之前;x不存在,p的值为NULL,插在表尾
84     s->next = p;
85     q->next = s;
86 }
View Code

  技术分享

  (3)在单向链表中插入节点

  在单向链表中插入节点,首先要确定插入的位置。当待插节点插在指针 p 所指的节点之前称为“前插”;当待插节点插在指针 p 所指节点之后称为“后插”。图2示意了“前插”操作过程中各指针的指向。

技术分享

图2 单向链表中节点的插入

  当进行“前插”操作时,需要三个工作指针:图中s 用来指向新开辟的节点;用 p 指向插入的位置;q 指向 p 的前趋节点(由于是单向链表,没有指针 q,就无法通过 p 去指向它前趋节点)。

  例3 编写函数 insert_snode,它的功能是:在值为 x 的节点前插入值为 y 的节点,若值为 x 的节点不存在,则插在表尾。

program

技术分享
 1 //建立带有头结点的单向链表
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 
 5 //声明结构体类型 SLIST
 6 struct slist
 7 {
 8     int data;
 9     struct slist *next;
10 };
11 typedef struct slist SLIST;
12 
13 //声明链表建立函数
14 SLIST* creat_slist();
15 void print_slist(SLIST* head);
16 void insert_snode(SLIST* head, int x, int y);
17 
18 int main()
19 {
20     SLIST *head;
21     head = creat_slist();    //调用链表建立函数,得到头结点地址
22     print_slist(head);
23     return 0;
24 }
25 
26 //定义链表建立函数
27 SLIST* creat_slist()
28 {    
29     //s指向新生成的节点;r指向链表的尾节点
30     SLIST *h, *s, *r;
31     int c;
32 
33     h = (SLIST*)malloc(sizeof(SLIST));
34     r = h;
35     scanf("%d", &c);        //读入数据
36     
37     while (c != -1)            //-1作为输入结束标志
38     {
39         s = (SLIST*)malloc(sizeof(SLIST));        //生成新节点
40         s->data = c;        //将读入的数据存入新节点的数据域
41         r->next = s;        //新节点链接到表尾
42         r = s;                //r指向当前表尾
43         scanf("%d", &c);    //读入数据
44     }
45     r->next = \\0;            //链表结束标志
46     return h;
47 }
48 
49 //顺序输出链表各节点数据
50 void print_slist(SLIST* head)
51 {
52     SLIST* p;
53     p = head->next;            //p指向头结点后的第一个节点
54     
55     if (p == \\0)
56     {
57         printf("Linklist is null !\\n");    //链表为空(只有头结点)
58     }
59     else
60     {
61         printf("head");
62         do
63         {
64             printf("->%d", p->data);    //输出当前节点数据域中的值
65             p = p->next;                //p指向下一节点
66         } while (p != \\0);            //未到链表尾,循环继续
67     }
68 }
69 
70 //在值为x的节点前插入值为y的节点
71 void insert_snode(SLIST* head, int x, int y)
72 {
73     SLIST *s, *p, *q;
74     s = (SLIST*)malloc(sizeof(SLIST));    //生成新节点
75     s->data = y;                        //在新节点中存入y值
76     q = head;
77     p = head->next;                        //工作指针初始化,p指向第一个节点
78     while ( (p != \\0) && (p->data != x) )
79     {
80         q = p;
81         p = p->next;                    //q指向p的前趋节点
82     }
83     //x存在,插在x之前;x不存在,p的值为NULL,插在表尾
84     s->next = p;
85     q->next = s;
86 }
View Code

  由于本例中的单向链表采用了带有头结点的结构,不需要单独处理新节点插在表头的情况,从而简化了操作。函数 insert_snode 中综合运用了“查找”和“前插”的算法。在进行插入操作的过程中,可能遇到三种情况,函数 insert_snode 将对这三种情况进行处理:

  (1)链表非空,值为 x 的节点存在,新节点应插在该节点之前。

  (2)链表非空,但值为 x 的节点不存在,新节点应插在表尾。

  (3)链表为空表,这种情况相当于 x 的节点不存在,新节点应插在表尾,即插在头结点之后,作为表的第一个节点。

  在函数中,对于空表,在执行 p = head->next; 后,p 的值就为 NULL ,因此不会进入循环;当链表非空时进入循环,在 while 循环中,当出现 p == ‘\\0‘ 时,将退出循环,这意味着查找结束,在链表中不存在值为 x 的节点。对于这两种情况,语句 s->next = p; q->next = s; 将新节点插在表尾。当链表中存在值为 x 的节点时,p 的值一定部位 ‘\\0‘,语句 s->next = p; q->next = s; 将新节点插在 x 节点之前。

  需要注意的是:while 循环中两个条件的顺序不能对调。当 p == NULL 时,C 编译程序将“短路”掉第二个条件,不做判断;否则如果先判断 p->data != x 的条件,由于 p 中的值已为 NULL,这时再要访问 p->data, 就会发生访问虚地址的错误操作。

  (4)删除单向链表中的节点

  为了删除单项链表中的某个节点,首先要找到待删除节点的前趋节点,然后将此前趋节点的指针域去指向待删除节点的后续节点(q->next = p->next),最后释放被删除节点所占空间 free(p) 即可。图3示意了节点的删除操作。

技术分享

图3 单向链表节点的删除

以上是关于2.单向链表的主要内容,如果未能解决你的问题,请参考以下文章

2.5 反转部分单向链表

用Java语言实现单向链表

2.单向链表

反转部分单向链表

05-2_单向链表

单向链表和双向链表简介