单链表——带头节点

一、链表简介

  1 数据结构中,链表是最基础的。然而链表根据不同的需求分成的种类很多,单向或双向链表,循环或非循环链表,带头节点或者不带头节点的链表。

  2 本文实现——带头节点的单链表。

  3 由于仅仅是学习链表的基本操作,所以在数据字段仅仅设置一个字段;

     由于仅仅是学习基本操作,不涉及复杂的算法思想,所以不会很难,主要以代码为主,附上必要的解释即可。

二、具体实现

  整体分析:带有头节点的单链表的操作很方便,主要体现在插入和删除时不需要判断是否是第一个元素。

  1) 头文件定义如下:

    

 1 #ifndef LinearList_LinkList_h
 2 #define LinearList_LinkList_h
 3
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6
 7 #define OK 1
 8 #define ERROR 0
 9
10
11 typedef int Status;
12 typedef int ElemType;
13
14
15 typedef struct LNode {
16     ElemType data;
17     struct LNode *pNext;
18 }LNode, *LinkList;
19
20 //LinkList with head
21 Status CreateLinkListFromHead(LinkList *L, int nInputLength);
22 Status CreateLinkListFromRear(LinkList *L, int nInputLength);
23 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem);
24 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem);
25 Status DestroyLinkList(LinkList *L);
26 Status PrintLinkList(LinkList L);
27
28 #endif

LinkList.h

     2)具体实现:

    1 建立单链表:

      思路: a)校验参数

        b)校验长度是否合法(不校验也行。这里主要是为了防止非法输入,快速返回)

        c)建立头节点。(也有一种方法:单独写一个建立头节点的函数。此处直接嵌入在建表的过程中。)

        d)循环建立节点,然后选择不同的插入方法插入。

          

     详解插入:

        a)插入代码:

        设pNode指向待插入位置的前一个插入点,pInsertNode指向待插入的节点本身,则插入代码如下:

        pInsertNode->pNext = pNode->pNext;

        pNode->pNext = pInsertNode;

        b)插入的方法:

          头插法:每次插入点都选择在头节点的后一个节点,也就是整个有效链表的第一个位置。

              pNode指向待插入节点本身,L指向表头节点。

              此时代码如下:

              (在建立头节点时,一定要先设置头节点的指针域为空 L->pNext = NULL,否则到最后就无法设置表尾为空了。)

              pNode->pNext = L->pNext;

              L->pNext = pNode;

          代码如下:

            

 1 Status CreateLinkListFromHead(LinkList *L, int nInputLength) {
 2     if (NULL == L) {
 3         printf("Error parament.");
 4         return ERROR;
 5     }
 6
 7     *L = (LinkList)malloc(sizeof(LNode));
 8     if (NULL == *L) {
 9         printf("Out of memory.");
10         return ERROR;
11     }
12     (*L)->pNext = NULL; //because list is created from head. So make the list empty
13
14     if (nInputLength < 1) {
15         printf("Error length.");
16         return ERROR;
17     }
18
19     LNode *pNode;
20     int nValue;
21
22     printf("Input the values:");
23     for (int i = 1; i <= nInputLength; i++) {
24         scanf("%d", &nValue);
25
26         pNode = (LinkList)malloc(sizeof(LNode));
27         if (NULL == pNode) {
28             printf("Out of memory.");
29             return ERROR;
30         }
31         pNode->data = nValue;
32
33         pNode->pNext = (*L)->pNext;
34         (*L)->pNext = pNode;
35     }
36
37     return OK;
38 }

CreateLinkListFromHead

          尾插法:每次插入点都选择在链表的表尾处。因此需要额外增加一个指针(pRear)用于标示链表表尾。

              起初,pRear指向表头节点pRear = L;pNode指向待插入节点本身

              插入代码如下:

              pRear->pNext = pNode;

              pRear = pRear->pNext;

              到最后一定要设置表尾为空:pRear->pNext = NULL;

          代码如下:

            

 1 Status CreateLinkListFromRear(LinkList *L, int nInputLength) {
 2     if (NULL == L) {
 3         printf("Error parament.");
 4         return ERROR;
 5     }
 6
 7     //build the head
 8     *L = (LinkList)malloc(sizeof(LNode));
 9     if (NULL == *L) {
10         printf("Out of memory.");
11         return ERROR;
12     }
13     (*L)->pNext = NULL; //make the list empty
14
15     if (nInputLength < 1) {
16         printf("Invalid input length.");
17         return ERROR;
18     }
19
20     int nValue;
21     LNode *pNode;
22     LNode *pRear = *L;
23
24     printf("Input the values:");
25     for (int i = 1; i <= nInputLength; i++) {
26         scanf("%d", &nValue);
27
28         pNode = (LinkList)malloc(sizeof(LNode));
29         if (NULL == pNode) {
30             printf("Out of memory.");
31             return ERROR;
32         }
33         pNode->data = nValue;
34
35         pRear->pNext = pNode;
36         pRear = pRear->pNext;
37     }
38
39     pRear->pNext = NULL;
40
41     return OK;
42 }

CreateLinkListFromRear

     2 插入节点:

          思路:a)校验参数

             b)建立待插入节点

             c)找到待插入点

             d)插入

          分析:纯粹的插入就是那两行代码,然而重点在于插入点的合法性判断以及插入点的定位。

             合法:当插入点大于表长时应该返回错误;

             插入点:一般来讲,会定位到当前带插入点的前一个位置;

            (其实也可以直接定位到带插入点处,插入后交换前后元素即可。但当数据元素过多时不适合。)    

          代码如下:

            

 1 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem) {
 2     if (NULL == L) {
 3         printf("Error parament.");
 4         return ERROR;
 5     }
 6
 7     if (nIndex < 1) {
 8         printf("Invalid insert point.");
 9         return ERROR;
10     }
11
12     int i = 0;
13     LNode *pNode;
14     LNode *qNode;
15
16     qNode = (LinkList)malloc(sizeof(LNode));
17     if (NULL == qNode) {
18         printf("Out of memory.");
19         return ERROR;
20     }
21     qNode->data = eElem;
22
23     //the key--find the insert point
24     pNode = *L;
25     while (NULL != pNode->pNext && i < nIndex - 1) {
26         pNode = pNode->pNext;
27         i++;
28     }
29
30     //invalid insert point
31     if (i < nIndex - 1) {
32         printf("Invalid Insert point.");
33         return ERROR;
34     }
35
36     //insert
37     qNode->pNext = pNode->pNext;
38     pNode->pNext = qNode;
39
40
41     return OK;
42 }

InsertToLinkList

     3 删除节点:(删除和插入类似)

          思路:a)校验参数

             b)判断链表是否为空

             c)找到待插入点

             d)取出待删除元素中的值

             e)删除

          分析:纯粹的删除代码也就固定的两行,然而重点在于删除点的定位和删除后指针的处理

            删除点定位:也是需要定位到待删除点的前一个位置;

            删除后处理:free和置空

          代码如下:

            

 1 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem) {
 2     if (NULL == L || NULL == pElem) {
 3         printf("Error parament.");
 4         return ERROR;
 5     }
 6
 7     if (NULL == (*L)->pNext) {
 8         printf("The list is empty.");
 9         return ERROR;
10     }
11
12     if (nIndex < 1) {
13         printf("Invalid delete point.");
14         return ERROR;
15     }
16
17     int i = 0;
18     LNode *qNode;
19     LNode *pNode = *L;  //  in case of the first valid delete point, the pNode should point to the head.
20
21     while (NULL != pNode->pNext && i < nIndex - 1) {
22         pNode = pNode->pNext;
23         i++;
24     }
25
26     if (NULL == pNode->pNext) { // nIndex - 1 == i
27         printf("Invalid delete point.");
28         return ERROR;
29     }
30
31     qNode = pNode->pNext;
32     pNode->pNext = qNode->pNext;
33
34     *pElem = qNode->data;
35     free(qNode);
36     qNode = NULL;
37
38     return OK;
39 }

DeleteFromLinkList

      4 销毁链表

          思路:a)校验参数

             b)判断是否为空

             c)遍历链表,依次删除。

          分析:销毁链表比较简单,只需要完整遍历这条链表即可。

         代码如下:

          

 1 Status DestroyLinkList(LinkList *L) {
 2     if (NULL == L) {
 3         printf("Error parament.");
 4         return ERROR;
 5     }
 6
 7     if (NULL == (*L)->pNext) {
 8         printf("The list is empty.");
 9         return ERROR;
10     }
11
12     LNode *pNode = (*L)->pNext;
13     while (NULL != pNode) {
14         (*L)->pNext = pNode->pNext;
15         free(pNode);
16
17         pNode = (*L)->pNext;
18     }
19
20     (*L)->pNext = NULL;
21     pNode = NULL;
22
23     return OK;
24 }

DestroyLinkList

三、附上完整版链表实现代码,方便完整拷贝,测试等。

  1 #include "LinkList.h"
  2
  3
  4 /*-----------------LinkList withhead-------------------------------------------------*/
  5
  6 Status CreateLinkListFromHead(LinkList *L, int nInputLength) {
  7     if (NULL == L) {
  8         printf("Error parament.");
  9         return ERROR;
 10     }
 11
 12     *L = (LinkList)malloc(sizeof(LNode));
 13     if (NULL == *L) {
 14         printf("Out of memory.");
 15         return ERROR;
 16     }
 17     (*L)->pNext = NULL; //because list is created from head. So make the list empty
 18
 19     if (nInputLength < 1) {
 20         printf("Error length.");
 21         return ERROR;
 22     }
 23
 24     LNode *pNode;
 25     int nValue;
 26
 27     printf("Input the values:");
 28     for (int i = 1; i <= nInputLength; i++) {
 29         scanf("%d", &nValue);
 30
 31         pNode = (LinkList)malloc(sizeof(LNode));
 32         if (NULL == pNode) {
 33             printf("Out of memory.");
 34             return ERROR;
 35         }
 36         pNode->data = nValue;
 37
 38         pNode->pNext = (*L)->pNext;
 39         (*L)->pNext = pNode;
 40     }
 41
 42     return OK;
 43 }
 44
 45 Status CreateLinkListFromRear(LinkList *L, int nInputLength) {
 46     if (NULL == L) {
 47         printf("Error parament.");
 48         return ERROR;
 49     }
 50
 51     //build the head
 52     *L = (LinkList)malloc(sizeof(LNode));
 53     if (NULL == *L) {
 54         printf("Out of memory.");
 55         return ERROR;
 56     }
 57     (*L)->pNext = NULL; //make the list empty
 58
 59     if (nInputLength < 1) {
 60         printf("Invalid input length.");
 61         return ERROR;
 62     }
 63
 64     int nValue;
 65     LNode *pNode;
 66     LNode *pRear = *L;
 67
 68     printf("Input the values:");
 69     for (int i = 1; i <= nInputLength; i++) {
 70         scanf("%d", &nValue);
 71
 72         pNode = (LinkList)malloc(sizeof(LNode));
 73         if (NULL == pNode) {
 74             printf("Out of memory.");
 75             return ERROR;
 76         }
 77         pNode->data = nValue;
 78
 79         pRear->pNext = pNode;
 80         pRear = pRear->pNext;
 81     }
 82
 83     pRear->pNext = NULL;
 84
 85     return OK;
 86 }
 87
 88
 89 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem) {
 90     if (NULL == L) {
 91         printf("Error parament.");
 92         return ERROR;
 93     }
 94
 95     if (nIndex < 1) {
 96         printf("Invalid insert point.");
 97         return ERROR;
 98     }
 99
100     int i = 0;
101     LNode *pNode;
102     LNode *qNode;
103
104     qNode = (LinkList)malloc(sizeof(LNode));
105     if (NULL == qNode) {
106         printf("Out of memory.");
107         return ERROR;
108     }
109     qNode->data = eElem;
110
111     //the key--find the insert point
112     pNode = *L;
113     while (NULL != pNode->pNext && i < nIndex - 1) {
114         pNode = pNode->pNext;
115         i++;
116     }
117
118     //invalid insert point
119     if (i < nIndex - 1) {
120         printf("Invalid Insert point.");
121         return ERROR;
122     }
123
124     //insert
125     qNode->pNext = pNode->pNext;
126     pNode->pNext = qNode;
127
128
129     return OK;
130 }
131
132 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem) {
133     if (NULL == L || NULL == pElem) {
134         printf("Error parament.");
135         return ERROR;
136     }
137
138     if (NULL == (*L)->pNext) {
139         printf("The list is empty.");
140         return ERROR;
141     }
142
143     if (nIndex < 1) {
144         printf("Invalid delete point.");
145         return ERROR;
146     }
147
148     int i = 0;
149     LNode *qNode;
150     LNode *pNode = *L;  //  in case of the first valid delete point, the pNode should point to the head.
151
152     while (NULL != pNode->pNext && i < nIndex - 1) {
153         pNode = pNode->pNext;
154         i++;
155     }
156
157     if (NULL == pNode->pNext) { // nIndex - 1 == i
158         printf("Invalid delete point.");
159         return ERROR;
160     }
161
162     qNode = pNode->pNext;
163     pNode->pNext = qNode->pNext;
164
165     *pElem = qNode->data;
166     free(qNode);
167     qNode = NULL;
168
169     return OK;
170 }
171
172 Status DestroyLinkList(LinkList *L) {
173     if (NULL == L) {
174         printf("Error parament.");
175         return ERROR;
176     }
177
178     if (NULL == (*L)->pNext) {
179         printf("The list is empty.");
180         return ERROR;
181     }
182
183     LNode *pNode = (*L)->pNext;
184     while (NULL != pNode) {
185         (*L)->pNext = pNode->pNext;
186         free(pNode);
187
188         pNode = (*L)->pNext;
189     }
190
191     (*L)->pNext = NULL;
192     pNode = NULL;
193
194     return OK;
195 }
196
197 Status PrintLinkList(LinkList L) {
198     if (NULL == L) {
199         printf("Error parament.");
200         return ERROR;
201     }
202
203     if (NULL == L->pNext) {
204         printf("The list is empty.");
205         return ERROR;
206     }
207
208     LNode *pNode = L->pNext;
209
210     while (NULL != pNode) {
211         printf("%d ", pNode->data);
212         pNode = pNode->pNext;
213     }
214
215     return OK;
216 }

LinkList.h

    

时间: 2024-10-19 03:06:19

单链表——带头节点的相关文章

C语言实现单链表的节点插入(带头结点)

我在之前一篇博客<C语言实现单链表(不带头结点)节点的插入>中具体实现了怎样在一个不带头结点的单链表中进行节点的插入.可是在实际应用中,带头结点的链表更为经常使用.更为方便.今天我们就要来使用带头结点的单链表进行节点的插入.演示样例代码上传至 https://github.com/chenyufeng1991/InsertList_HeadNode  . 核心代码例如以下: Node *InsertNode(Node *pNode,int pos,int x){ int i = 0; Node

单链表删除节点

单链表删除节点 node *del(node *head, int num) { node *p1, *p2; p1 = head; while (num != p1->data && p1->next != NULL) { p2 = p1; p1 = p1->next; } if (num == p1->data) { if (p1 == head) head = p1->next; else p2->next = p1->next; free(

单链表的节点内数据值的删除问题(携程网笔试题)

问题描述:给定一个单链表,链表中存储的数据都为整数,给定一个整数x,将单链表中所有与x相等的元素删除. 例如:单链表为(1,2,3,4,2,4),x=2,则删除节点后链表为(1,3,4,4) 分析:这是链表的基本操作问题,具体的Java代码如下: 1 import java.util.*; 2 class Node{ //链表节点的结构 3 int data; 4 Node next=null; 5 } 6 7 public class Main { 8 public static void c

删除单链表中间节点

题目:(先思考,再往下看) 从无头链表中删除节点.假设有一个没有头指针的单链表.一个指针指向此链表中间的一个节点(不是第一个,也不是最后一个节点),请将该节点从单链表中删除---<编程之美> 参考:单链表反转  <算法之美>の链表问题の从链表中删除节点 代码待续 ......

[LeetCode系列] 双单链表共同节点搜索问题

找到两个单链表的共同节点. 举例来说, 下面两个链表A和B: A: a1 → a2 c1 → c2 → c3 B: b1 → b2 → b3 共同节点为c1. 分析: 共同节点距离A,B的起点headA, headB的距离差为定值, 等于它们的各自总长的差值, 我们只需要求出这个差值, 把两个链表的头移动到距离c1相等距离的起点处即可. 代码: /** * Definition for singly-linked list. * struct ListNode { * int val; * Li

给定数组,创建(带头节点)的单链表(头插法、尾插法)

一般有两种常用的方法来建立单链表:头插法与尾插法. (1)头插法:每次将新申请的节点插在头节点的后面: 简单来说,就是把新加进的元素放在表头后的第一个位置: 首先,让新节点的next指向头节点之后:然后,让表头的next指向新节点. (2)尾插法:每次将新申请的节点插在终端节点的后面. #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node *next; } NODE; /

删除单链表中第i个节点

单链表的删除操作是将单链表的第i个节点删去.具体步骤如下: (1)找到节点ai-1的存储位置p,因为在单链表中节点ai的存储地址是在其直接前趋节点ai-1的指针域next中: (2)令p->next指向ai的直接后继节点ai+1: (3)释放节点ai的空间: #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node *next; } NODE; // 尾插法创建单链表(

Java带头节点单链表的增删融合以及是否有环

带头节点单链表 1.优势: 1)当链表为空时,指针指向头结点,不会发生null指针异常 2)方便特殊操作(删除第一个有效节点或者插入一个节点在表头) 3)单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会. 4)代码更加简单易懂 2.相关操作 1)建立单链表 即建立一个头结点 static class Entry<T> { T data; // 链表节点的数据域 Entry<T>

C语言实现单链表(带头结点)的基本操作

我在之前一篇博客<C语言实现单链表(不带头结点)的基本操作>中具体实现了不带头结点的单链表的11种操作:如计算链表长度.初始化.创建链表.清空链表等等.但是在实际使用中,带头结点的单链表往往比不带头结点的单链表用的更多,使用也更为方便.因为不用单独考虑第一个节点的情况了,第一个节点和其他后续节点的处理全都一样了,简化操作.这篇博客将会来实现带头结点的单链表的11种操作.代码上传至: https://github.com/chenyufeng1991/LinkedList_HeadNode  .