数据结构算法C语言实现(五)---2.3重新定义线性链表及其基本操作
一.简述
...由于链表在空间的合理利用上和插入、删除时不需要移动等的优点,因此在很多场合下,它是线性表的首选存储结构。然而,它也存在着实现某些基本操作,如求线性表的长度时不如顺序存储结构的缺点;另一方面,由于在链表中,结点之间的关系用指针来表示,则数据元素在线性表中的“位序”的概念已经淡化,而被数据元素在线性链表中的“位置”所代替。为此,从实际应用的角度重新定义线性链表及其基本操作。....(Page37)
此外,书上一些地方我认为存在错误,所以写代码时做了修改和注释。并且,由于一些基本操作书中没有具体使用的实例,所以我只实现了算法2.20和算法2.21中出现的基本操作,其他操作会在需要的时候 再做实现。
二.头文件
1 //LinkList.h 2 /** 3 《数据结构(C语言版)》 Page 37 4 ....为此,从实际应用角度出发重新定义线性链表及其基本操作.... 5 */ 6 /** 7 author:zhaoyu 8 email:zhaoyu1995.com@gmail.com 9 date:2016-6-4 10 note:realize my textbook <<数据结构(C语言版)>> 11 */ 12 #ifndef _LINKLIST_H_ 13 #define _LINKLIST_H_ 14 #include "head.h" 15 #include <cstdlib> 16 #define ElemType int 17 typedef struct LNode{ 18 ElemType data; 19 struct LNode *next; 20 }*Link, *Position; 21 typedef struct{//链表类型 22 Link head, tail;//分别指向线性链表中的头结点和最后一个结点 23 int len;//指示线性链表中数据元素的个数 24 }LinkList; 25 Status MakeNode(Link &p, ElemType e) 26 { 27 //分配由 p 指向的值为 e 的结点,并返回 OK;若分配失败,则返回 ERROR 28 p = (Link)malloc(sizeof(struct LNode)); 29 if (!p) 30 { 31 return ERROR; 32 } 33 p->data = e; 34 p->next = NULL; 35 return OK; 36 } 37 Status FreeNode(Link &p) 38 { 39 //释放 p 所指节点 40 p = NULL;//便于回收???释放后再次使用 41 free(p); 42 } 43 Status InitList(LinkList &L) 44 { 45 //构造一个空的线性表L 46 L.head = (Link)malloc(sizeof(struct LNode)); 47 L.head->data = 100; 48 L.head->next = NULL; 49 L.tail = L.head->next; 50 L.len = 0; 51 } 52 Status ClearList(LinkList &L) 53 { 54 //将线性链表置为空表并释放原链表的结点空间 55 } 56 Status InsFirst(LinkList &L, Link &h, Link &s) 57 { 58 //已知 h 指向线性链表的头结点,将 s 所指节点插入在第一个结点之前 59 //note:这里的参数与书上有些不一样,LinkList &L是我添加的 60 //因为插入新的元素之后,链表长度应该同步变化 61 //链表的插入是基于地址的,所以我全部加了引用 62 //这一个函数与书上参数完全不同 63 //更新 2016-5-5 16:46 64 //通过分析算法2.20发现 h 不是绝对的头结点,而是接受一个 h 65 //就以它为头结点作参考操作其它元素 66 //这个函数花了不少时间,值得反复思考 67 if (NULL == h) 68 { 69 return ERROR; 70 } 71 else 72 { 73 s->next = h->next; 74 h->next = s; 75 } 76 L.len++; 77 return OK; 78 } 79 Status DelFirst(Link &h, Link &q) 80 { 81 //已知 h 指向线性链表的头结点,删除链表中的第一个节点并以 q 返回 82 if (h == NULL) 83 { 84 return ERROR; 85 } 86 q = h->next; 87 h->next = q->next; 88 q->next = NULL; 89 return OK; 90 } 91 Status Append(LinkList &L, Link &s) 92 { 93 //将指针 s 所指(彼此以指针相链)的一串结点 94 //链接在线性链表 L 最后一个结点 95 Link q = L.head; 96 while (q->next != NULL) 97 { 98 q = q->next; 99 } 100 q->next = s; 101 int cnt = 0; 102 Link temp = s; 103 while (temp != NULL) 104 { 105 cnt++; 106 if (NULL == temp->next) 107 { 108 L.tail = temp;//注意更新尾指针 109 } 110 temp = temp->next; 111 } 112 113 L.len += cnt; 114 //注意要根据这一串结点长度增加链表长度 115 return OK; 116 } 117 Status Remove(LinkList &L, Link &q) 118 { 119 //删除线性链表 L 中的尾结点并以 q 返回,改变链表的尾指针指向新的尾结点 120 } 121 Status InsBefore(LinkList &L, Link &p, Link s) 122 { 123 //已知 p 指向线性链表 L 中的一个结点,将 s 所指结点插入 p 所指节点之前 124 //并修改指针 p 指向新插入的节点 125 } 126 Status InsAfter(LinkList &L, Link &p, Link s) 127 { 128 //已知 p 指向线性链表 L 中的一个结点,将 s 所指结点插入 p 所指节点之后 129 //并修改指针 p 指向新插入的节点 130 } 131 Status SetCurElem(Link &p, ElemType e) 132 { 133 //已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 134 } 135 ElemType GetCurElem(Link p) 136 { 137 //已知 p 指向线性链表中的一个节点,返回 p 所指结点中数据元素的值 138 if (p != NULL) 139 { 140 return p->data; 141 } 142 else 143 { 144 exit(ERROR); 145 } 146 } 147 Status ListEmpty(LinkList L) 148 { 149 //若线性链表 L 为空,则返回 TRUE,否则返回 FALSE 150 } 151 int ListLength(LinkList L) 152 { 153 //返回线性链表 L 中元素的个数 154 155 } 156 Position GetHead(LinkList L) 157 { 158 //返回线性链表 L 中头结点的位置 159 return L.head; 160 } 161 Position GetLast(LinkList L) 162 { 163 //返回线性链表 L 中最后一个结点的位置 164 } 165 Position PriorPos(LinkList L, Link p) 166 { 167 //已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点直接前驱的位置 168 //若无前驱,则返回NULL 169 } 170 Position NextPos(LinkList L, Link p) 171 { 172 //已知 p 指向线性链表 L 中的一个节点,返回 p 所指结点 173 //的直接后继的位置,若无后继,返回 NULL 174 Link q = L.head; 175 while (q != NULL) 176 { 177 if(q == p) 178 { 179 return p->next; 180 } 181 q = q->next; 182 } 183 return NULL; 184 } 185 Status LocatePos(LinkList L, int i, Link &p) 186 { 187 //返回 p 指示线性链表 L 中第 i 个结点的位置并返回 OK 188 //i 值不合法时返回ERROR 189 int cnt = 2; 190 p = L.head; 191 if (i > L.len || i < 1) 192 { 193 return ERROR; 194 } 195 while (cnt <= i) 196 { 197 p = p->next; 198 cnt++; 199 } 200 return OK; 201 202 } 203 Position LocateElem(LinkList L, ElemType e, Status (* compare)(ElemType, ElemType)) 204 { 205 //返回线性链表中第一个与 e 满足函数compare() 判定关系的元素的位置 206 //若不存在这样的元素,返回 NULL 207 } 208 Status ListTraverse(LinkList L, Status (* visit)()) 209 { 210 //依次对 L 中每个元素调用函数 visit(),一旦visit() 失败,则操作失败。 211 } 212 /** 213 My Code 214 */ 215 void PrintList(LinkList L) 216 { 217 Link p = L.head->next; 218 int cnt = 1; 219 while (p != NULL && cnt <= L.len) 220 { 221 printf("%d\t", p->data); 222 p = p->next; 223 cnt++; 224 } 225 printf("\n"); 226 } 227 int compare(ElemType a, ElemType b) 228 { 229 if (a < b) 230 { 231 return -1; 232 } 233 else if (a == b) 234 { 235 return 0; 236 } 237 else 238 { 239 return 1; 240 } 241 } 242 #endif
三.CPP文件
1 //LinkList.h 2 /** 3 《数据结构(C语言版)》 Page 37 4 ....为此,从实际应用角度出发重新定义线性链表及其基本操作.... 5 */ 6 /** 7 author:zhaoyu 8 email:zhaoyu1995.com@gmail.com 9 date:2016-6-4 10 note:realize my textbook <<数据结构(C语言版)>> 11 */ 12 #ifndef _LINKLIST_H_ 13 #define _LINKLIST_H_ 14 #include "head.h" 15 #include <cstdlib> 16 #define ElemType int 17 typedef struct LNode{ 18 ElemType data; 19 struct LNode *next; 20 }*Link, *Position; 21 typedef struct{//链表类型 22 Link head, tail;//分别指向线性链表中的头结点和最后一个结点 23 int len;//指示线性链表中数据元素的个数 24 }LinkList; 25 Status MakeNode(Link &p, ElemType e) 26 { 27 //分配由 p 指向的值为 e 的结点,并返回 OK;若分配失败,则返回 ERROR 28 p = (Link)malloc(sizeof(struct LNode)); 29 if (!p) 30 { 31 return ERROR; 32 } 33 p->data = e; 34 p->next = NULL; 35 return OK; 36 } 37 Status FreeNode(Link &p) 38 { 39 //释放 p 所指节点 40 p = NULL;//便于回收???释放后再次使用 41 free(p); 42 } 43 Status InitList(LinkList &L) 44 { 45 //构造一个空的线性表L 46 L.head = (Link)malloc(sizeof(struct LNode)); 47 L.head->data = 100; 48 L.head->next = NULL; 49 L.tail = L.head->next; 50 L.len = 0; 51 } 52 Status ClearList(LinkList &L) 53 { 54 //将线性链表置为空表并释放原链表的结点空间 55 } 56 Status InsFirst(LinkList &L, Link &h, Link &s) 57 { 58 //已知 h 指向线性链表的头结点,将 s 所指节点插入在第一个结点之前 59 //note:这里的参数与书上有些不一样,LinkList &L是我添加的 60 //因为插入新的元素之后,链表长度应该同步变化 61 //链表的插入是基于地址的,所以我全部加了引用 62 //这一个函数与书上参数完全不同 63 //更新 2016-5-5 16:46 64 //通过分析算法2.20发现 h 不是绝对的头结点,而是接受一个 h 65 //就以它为头结点作参考操作其它元素 66 //这个函数花了不少时间,值得反复思考 67 if (NULL == h) 68 { 69 return ERROR; 70 } 71 else 72 { 73 s->next = h->next; 74 h->next = s; 75 } 76 L.len++; 77 return OK; 78 } 79 Status DelFirst(Link &h, Link &q) 80 { 81 //已知 h 指向线性链表的头结点,删除链表中的第一个节点并以 q 返回 82 if (h == NULL) 83 { 84 return ERROR; 85 } 86 q = h->next; 87 h->next = q->next; 88 q->next = NULL; 89 return OK; 90 } 91 Status Append(LinkList &L, Link &s) 92 { 93 //将指针 s 所指(彼此以指针相链)的一串结点 94 //链接在线性链表 L 最后一个结点 95 Link q = L.head; 96 while (q->next != NULL) 97 { 98 q = q->next; 99 } 100 q->next = s; 101 int cnt = 0; 102 Link temp = s; 103 while (temp != NULL) 104 { 105 cnt++; 106 temp = temp->next; 107 if (NULL == temp->next) 108 { 109 L.tail = temp;//注意更新尾指针 110 } 111 } 112 113 L.len += cnt; 114 //注意要根据这一串结点长度增加链表长度 115 return OK; 116 } 117 Status Remove(LinkList &L, Link &q) 118 { 119 //删除线性链表 L 中的尾结点并以 q 返回,改变链表的尾指针指向新的尾结点 120 } 121 Status InsBefore(LinkList &L, Link &p, Link s) 122 { 123 //已知 p 指向线性链表 L 中的一个结点,将 s 所指结点插入 p 所指节点之前 124 //并修改指针 p 指向新插入的节点 125 } 126 Status InsAfter(LinkList &L, Link &p, Link s) 127 { 128 //已知 p 指向线性链表 L 中的一个结点,将 s 所指结点插入 p 所指节点之后 129 //并修改指针 p 指向新插入的节点 130 } 131 Status SetCurElem(Link &p, ElemType e) 132 { 133 //已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 134 } 135 ElemType GetCurElem(Link p) 136 { 137 //已知 p 指向线性链表中的一个节点,返回 p 所指结点中数据元素的值 138 if (p != NULL) 139 { 140 return p->data; 141 } 142 else 143 { 144 exit(ERROR); 145 } 146 } 147 Status ListEmpty(LinkList L) 148 { 149 //若线性链表 L 为空,则返回 TRUE,否则返回 FALSE 150 } 151 int ListLength(LinkList L) 152 { 153 //返回线性链表 L 中元素的个数 154 155 } 156 Position GetHead(LinkList L) 157 { 158 //返回线性链表 L 中头结点的位置 159 return L.head; 160 } 161 Position GetLast(LinkList L) 162 { 163 //返回线性链表 L 中最后一个结点的位置 164 } 165 Position PriorPos(LinkList L, Link p) 166 { 167 //已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点直接前驱的位置 168 //若无前驱,则返回NULL 169 } 170 Position NextPos(LinkList L, Link p) 171 { 172 //已知 p 指向线性链表 L 中的一个节点,返回 p 所指结点 173 //的直接后继的位置,若无后继,返回 NULL 174 Link q = L.head; 175 while (q != NULL) 176 { 177 if(q == p) 178 { 179 return p->next; 180 } 181 q = q->next; 182 } 183 return NULL; 184 } 185 Status LocatePos(LinkList L, int i, Link &p) 186 { 187 //返回 p 指示线性链表 L 中第 i 个结点的位置并返回 OK 188 //i 值不合法时返回ERROR 189 int cnt = 2; 190 p = L.head; 191 if (i > L.len || i < 1) 192 { 193 return ERROR; 194 } 195 while (cnt <= i) 196 { 197 p = p->next; 198 cnt++; 199 } 200 return OK; 201 202 } 203 Position LocateElem(LinkList L, ElemType e, Status (* compare)(ElemType, ElemType)) 204 { 205 //返回线性链表中第一个与 e 满足函数compare() 判定关系的元素的位置 206 //若不存在这样的元素,返回 NULL 207 } 208 Status ListTraverse(LinkList L, Status (* visit)()) 209 { 210 //依次对 L 中每个元素调用函数 visit(),一旦visit() 失败,则操作失败。 211 } 212 /** 213 My Code 214 */ 215 void PrintList(LinkList L) 216 { 217 Link p = L.head->next; 218 int cnt = 1; 219 while (p != NULL && cnt <= L.len) 220 { 221 printf("%d\t", p->data); 222 p = p->next; 223 cnt++; 224 } 225 printf("\n"); 226 } 227 int compare(ElemType a, ElemType b) 228 { 229 if (a < b) 230 { 231 return -1; 232 } 233 else if (a == b) 234 { 235 return 0; 236 } 237 else 238 { 239 return 1; 240 } 241 } 242 #endif
四.测试