建议使用带头结点的版本,因为不带头结点的版本有一些缺陷:
在插入或删除操作中,如果返回类型不是链表,那么形参类型一定是指向头结点的指针(也就是二级指针),因为传递指针实参时,形参实际上是实参的副本,所以实参的指向是一直不变的,如果在插入过程中插在了头指针的下一个位置(也就是第一个结点的位置),如果返回类型不是链表或形参不是二级指针,那么插入操作对原链表时无效的,而如果删除释放了第一个结点,那么由于实参的指向始终是第一个结点的那块内存,那么实参指针就是指向一块不知名的内存,成为野指针。总的来说,不带头结点的版本,在删除或插入操作中,如果返回类型不是链表或形参不是二级指针,那么该操作不能作用在第一个结点上
关于指针作为形参的问题可以参考:https://www.cnblogs.com/hi3254014978/p/9478307.html
LinkList.h
1 #ifndef LINKLIST_H_INCLUDE
2 #define LINKLIST_H_INCLUDE
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdbool.h>
7
8 #define ERROR -1
9
10 typedef int ElementType;
11
12 typedef struct LNode* PtrToNode;
13 struct LNode{
14 ElementType Data;
15 PtrToNode Next;
16 };
17
18 typedef PtrToNode Position; //这里的位置某个结点的结点指针
19 typedef PtrToNode List;
20
21 List L;
22
23 //链表长度
24 int Length(List L);
25
26 //根据位序查找元素
27 ElementType FindKth(List L, int K);
28
29 //根据元素查找相应的结点
30 Position Find(List L, ElementType X);
31
32 //在指定位置之后插入元素
33 List Insert(List L, ElementType X, int i);
34
35 bool Delete(List *L, int i);
36
37 void printList(List L);
38
39
40
41
42 #endif
LinkList.c
1 #include "LinkList.h"
2
3 int Length(List L)
4 {
5 int cnt = 0;
6 List p = L; //当前p指向第一个结点
7 while (p)
8 {
9 cnt++;
10 p = p->Next;
11
12 }
13 return cnt;
14
15 }
16
17 ElementType FindKth(List L, int K)
18 {
19 Position p = L;
20 int cnt = 1;
21 while (p && cnt < K)
22 {
23 p = p->Next;
24 cnt++;
25 }
26 if ((cnt == K) && p)
27 return p->Data;
28 else
29 return ERROR;
30 }
31
32 Position Find(List L, ElementType X)
33 {
34 Position p = L;
35 while (p && p->Data != X)
36 p = p->Next;
37 if (p)
38 return p;
39 else
40 return NULL;
41 }
42
43 List Insert(List L, ElementType X, int i)
44 {
45 Position tmp, pre;
46 tmp = (Position)malloc(sizeof(struct LNode));
47 if (i == 1)
48 {
49 tmp->Data = X;
50 tmp->Next = L;
51 return tmp;
52 }
53
54 else
55 {
56 int cnt = 1;
57 pre = L;
58 while (pre && cnt < i - 1)
59 {
60 pre = pre->Next;
61 cnt++;
62 }
63 if (pre == NULL || cnt != i - 1) //如果所招结点不再L中
64 {
65 printf("Insert position parameter error!\n");
66 free(tmp);
67 return L;
68 }
69 else
70 {
71 tmp->Data = X;
72 tmp->Next = pre->Next;
73 pre->Next = tmp;
74 return L;
75 }
76 }
77 }
78
79 bool Delete(List *L, int i)
80 {
81 Position tmp, pre;
82 int cnt = 1;
83 pre = *L;
84 if (i == 1 && pre)
85 {
86 *L = (*L)->Next; //如果这里没有传*L,而是L,那么这里的L只是实参的副本,
87 //所以如果删除并释放第一个结点后,因为实参的L还是指向第一个结点的,所以第一个结点被释放后,
88 //实参L会只想一块不知名的内存,成为野指针,所以建议使用带头结点的链表
89 free(pre);
90 return true;
91 }
92 else
93 {
94 while (pre && cnt < i - 1)
95 {
96 pre = pre->Next;
97 cnt++;
98 }
99 if (pre == NULL || cnt != i - 1 || pre->Next == NULL) //i出错或链表为空
100 {
101 printf("Delete position parameter error!\n");
102 return false;
103 }
104 else
105 {
106 tmp = pre->Next;
107 pre->Next = tmp->Next;
108 free(tmp);
109 return true;
110 }
111 }
112
113 }
114
115 void printList(List L)
116 {
117 Position p;
118 p = L;
119 while (p)
120 {
121 printf("%d ", p->Data);
122 p = p->Next;
123 }
124 printf("\n");
125 return ;
126 }