建议使用带头结点的版本,因为不带头结点的版本有一些缺陷:
在插入或删除操作中,如果返回类型不是链表,那么形参类型一定是指向头结点的指针(也就是二级指针),因为传递指针实参时,形参实际上是实参的副本,所以实参的指向是一直不变的,如果在插入过程中插在了头指针的下一个位置(也就是第一个结点的位置),如果返回类型不是链表或形参不是二级指针,那么插入操作对原链表时无效的,而如果删除释放了第一个结点,那么由于实参的指向始终是第一个结点的那块内存,那么实参指针就是指向一块不知名的内存,成为野指针。总的来说,不带头结点的版本,在删除或插入操作中,如果返回类型不是链表或形参不是二级指针,那么该操作不能作用在第一个结点上
关于指针作为形参的问题可以参考:https://blog.51cto.com/u_14201949/2831834
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 }