线性结构——线性表
一、简介
数据结构中的逻辑结构分为线性结构和非线性结构,简单地说,线性结构是n个数据元素的有序(次序)集合,它有下列几个特征:
1)集合中必存在唯一的一个"第一个元素";
2)集合中必存在唯一的一个"最后的元素";
3)除最后元素之外,其它数据元素均有唯一的"后继";
4)除第一元素之外,其它数据元素均有唯一的"前驱"。
一个线性表可以表示成一个线性序列:k1,k2,…,kn,其中k1是开始结点,kn是终端结点。
一般线性表包含下列基本操作:初始化、销毁、重置为空表、判断是否为空、获取长度、根据位置获取对应元素、查找元素、获取指定元素的前驱和后继元素、插入元素、删除元素、遍历元素。
二、线性表的顺序表示和实现
线性表的顺序表示指的是用物理上的一段连续的地址来存储数据元素,如下图所示。如果第一个元素的在内存上的地址为a1,每个元素占用的空间是1,那么第n个元素的地址就是a1+(n-1) x 1。
只要确定了第一个元素的地址,那么我们可以对线性表中的任一元素随机存取,由于编程语言中的数组也有随机存取的特点,下面就用数组来描述线性表的顺序存储结构。
三、代码实现
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define TRUE 1 5 #define FALSE 0 6 #define OK 1 7 #define ERROR 0 8 #define INIT_SIZE 10 //初始化表长 9 #define INCREMENT_SIZE 5 //分配增量 10 11 typedef int Status; 12 typedef int Elemtype; 13 14 /* 15 * 存储结构 16 */ 17 typedef struct 18 { 19 Elemtype *elem; //存储空间基址 20 int length; //当前长度 21 int size; //当前分配的表长大小 22 } SqList; 23 24 /* 25 * 初始化一个空的线性表 26 */ 27 Status InitList(SqList *L) 28 { 29 L->elem = (Elemtype *) malloc(INIT_SIZE * sizeof(Elemtype)); 30 if (!L->elem) 31 { 32 return ERROR; 33 } 34 L->length = 0; 35 L->size = INIT_SIZE; 36 return OK; 37 } 38 39 /* 40 * 销毁线性表 41 */ 42 Status DestroyList(SqList *L) 43 { 44 free(L->elem); 45 L->length = 0; 46 L->size = 0; 47 return OK; 48 } 49 50 /* 51 * 清空线性表 52 */ 53 Status ClearList(SqList *L) 54 { 55 L->length = 0; 56 return OK; 57 } 58 59 /* 60 * 判断线性表是否为空 61 */ 62 Status isEmpty(const SqList L) 63 { 64 if (0 == L.length) 65 { 66 return TRUE; 67 } 68 else 69 { 70 return FALSE; 71 } 72 } 73 74 /* 75 * 获取长度 76 */ 77 Status getLength(const SqList L) 78 { 79 return L.length; 80 } 81 82 /* 83 * 根据位置获取元素 84 */ 85 Status GetElem(const SqList L, int i, Elemtype *e) 86 { 87 if (i < 1 || i > L.length) 88 { 89 return ERROR; 90 } 91 *e = L.elem[i - 1]; 92 return OK; 93 } 94 95 /* 96 * 比较两个元素是否相等 97 */ 98 Status compare(Elemtype e1, Elemtype e2) 99 { 100 if (e1 == e2) 101 { 102 return 0; 103 } 104 else if (e1 < e2) 105 { 106 return -1; 107 } 108 else 109 { 110 return 1; 111 } 112 } 113 114 /* 115 * 查找元素 116 */ 117 Status FindElem(const SqList L, Elemtype e, Status (*compare)(Elemtype, Elemtype)) 118 { 119 int i; 120 for (i = 0; i < L.length; i++) 121 { 122 if (!(*compare)(L.elem[i], e)) 123 { 124 return i + 1; 125 } 126 } 127 if (i >= L.length) 128 { 129 return ERROR; 130 } 131 } 132 133 /* 134 * 查找前驱元素 135 */ 136 Status PreElem(const SqList L, Elemtype cur_e, Elemtype *pre_e) 137 { 138 int i; 139 for (i = 0; i < L.length; i++) 140 { 141 if (cur_e == L.elem[i]) 142 { 143 if (i != 0) 144 { 145 *pre_e = L.elem[i - 1]; 146 } 147 else 148 { 149 return ERROR; 150 } 151 } 152 } 153 if (i >= L.length) 154 { 155 return ERROR; 156 } 157 } 158 159 /* 160 * 查找后继元素 161 */ 162 Status NextElem(const SqList L, Elemtype cur_e, Elemtype *next_e) 163 { 164 int i; 165 for (i = 0; i < L.length; i++) 166 { 167 if (cur_e == L.elem[i]) 168 { 169 if (i < L.length - 1) 170 { 171 *next_e = L.elem[i + 1]; 172 return OK; 173 } 174 else 175 { 176 return ERROR; 177 } 178 } 179 } 180 if (i >= L.length) 181 { 182 return ERROR; 183 } 184 } 185 186 /* 187 * 插入元素 188 */ 189 Status InsertElem(SqList *L, int i, Elemtype e) 190 { 191 Elemtype *new; 192 if (i < 1 || i > L->length + 1) 193 { 194 return ERROR; 195 } 196 if (L->length >= L->size) 197 { 198 new = (Elemtype*) realloc(L->elem, (L->size + INCREMENT_SIZE) * sizeof(Elemtype)); 199 if (!new) 200 { 201 return ERROR; 202 } 203 L->elem = new; 204 L->size += INCREMENT_SIZE; 205 } 206 Elemtype *p = &L->elem[i - 1]; 207 Elemtype *q = &L->elem[L->length - 1]; 208 for (; q >= p; q--) 209 { 210 *(q + 1) = *q; 211 } 212 *p = e; 213 ++L->length; 214 return OK; 215 } 216 217 /* 218 * 删除元素并返回其值 219 */ 220 Status DeleteElem(SqList *L, int i, Elemtype *e) 221 { 222 if (i < 1 || i > L->length) 223 { 224 return ERROR; 225 } 226 Elemtype *p = &L->elem[i - 1]; 227 *e = *p; 228 for (; p < &L->elem[L->length]; p++) 229 { 230 *(p) = *(p + 1); 231 } 232 --L->length; 233 return OK; 234 } 235 236 /* 237 * 访问元素 238 */ 239 void visit(Elemtype e) 240 { 241 printf("%d ", e); 242 } 243 244 /* 245 * 遍历线性表 246 */ 247 Status TraverseList(const SqList L, void (*visit)(Elemtype)) 248 { 249 int i; 250 for (i = 0; i < L.length; i++) 251 { 252 visit(L.elem[i]); 253 } 254 return OK; 255 } 256 257 /* 258 * 主函数测试 259 */ 260 int main() 261 { 262 SqList L; 263 if (InitList(&L)) 264 { 265 Elemtype e; 266 printf("init_success\n"); 267 int i; 268 for (i = 0; i < 10; i++) 269 { 270 InsertElem(&L, i + 1, i); 271 } 272 printf("length is %d\n", getLength(L)); 273 if (GetElem(L, 1, &e)) { 274 printf("The first element is %d\n", e); 275 } 276 else 277 { 278 printf("element is not exist\n"); 279 } 280 if (isEmpty(L)) 281 { 282 printf("list is empty\n"); 283 } 284 else 285 { 286 printf("list is not empty\n"); 287 } 288 printf("The 5 at %d\n", FindElem(L, 5, *compare)); 289 PreElem(L, 6, &e); 290 printf("The 6's previous element is %d\n", e); 291 NextElem(L, 6, &e); 292 printf("The 6's next element is %d\n", e); 293 DeleteElem(&L, 1, &e); 294 printf("delete first element is %d\n", e); 295 printf("list:"); 296 TraverseList(L, visit); 297 if (DestroyList(&L)) 298 { 299 printf("\ndestroy_success\n"); 300 } 301 } 302 return 0; 303 }
四、线性表的链式表示和实现
线性表的顺序存储结构是逻辑位置和物理位置都相邻,而链式存储结构是逻辑位置相邻,但物理位置不一定相邻,相比顺序存储结构,它不能随机存取,但在插入和删除操作时不需要移动元素,大大提高了增加和删除元素的效率。
通常链式存储结构会有一个个结点组成,结点中包含两个域一个是数据域,一个是指针域,数据域中存储数据,指针域中存储下一个后继元素的地址,如下图所示,这一个个结点组成链表,也称线性链表或单链表。

单链表的逻辑结构如下图所示

除了单链表之外还有循环链表和双向链表,循环链表的特点是最后一个结点的指针指向头结点,形成一个环,双向链表的特点是结点中多了一个指向前驱元素的指针,这两种链表的逻辑结构如下面两张图所示
循环链表

双向链表

单链表实现代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define TRUE 1 5 #define FALSE 0 6 #define OK 1 7 #define ERROR 0 8 #define OVERFLOW -2 9 10 typedef int ElemType; 11 typedef int Status; 12 13 /* 14 * 存储结构 15 */ 16 typedef struct LNode 17 { 18 ElemType data; 19 struct LNode *next; 20 }LNode, *LinkList; 21 22 /* 23 * 初始化线性表 24 */ 25 void InitList(LinkList *L) 26 { 27 *L = (LinkList) malloc(sizeof(LNode)); 28 if (!L) 29 { 30 exit(OVERFLOW); 31 } 32 (*L)->next = NULL; 33 } 34 35 /* 36 * 销毁线性表 37 */ 38 void DestroyList(LinkList *L) 39 { 40 LinkList temp; 41 while (*L) 42 { 43 temp = (*L)->next; 44 free(*L); 45 *L = temp; 46 } 47 } 48 49 /* 50 * 清空线性表 51 */ 52 void ClearList(LinkList L) 53 { 54 LinkList p = L->next; 55 L->next = NULL; 56 DestroyList(&p); 57 } 58 59 /* 60 * 判断是否为空 61 */ 62 Status isEmpty(LinkList L) 63 { 64 if (L->next) 65 { 66 return FALSE; 67 } 68 else 69 { 70 return TRUE; 71 } 72 } 73 74 /* 75 * 获取长度 76 */ 77 int GetLength(LinkList L) 78 { 79 int i = 0; 80 LinkList p = L->next; 81 while (p) 82 { 83 i++; 84 p = p->next; 85 } 86 return i; 87 } 88 89 /* 90 * 根据位置获取元素 91 */ 92 Status GetElem(LinkList L, int i, ElemType *e) 93 { 94 int j = 1; 95 LinkList p = L->next; 96 while (p && j < i) 97 { 98 j++; 99 p = p->next; 100 } 101 if (!p || j > i) 102 { 103 return ERROR; 104 } 105 *e = p->data; 106 return OK; 107 } 108 109 /* 110 * 比较两个元素是否相等 111 */ 112 Status compare(ElemType e1, ElemType e2) 113 { 114 if (e1 == e2) 115 { 116 return 0; 117 } 118 else if (e1 < e2) 119 { 120 return -1; 121 } 122 else 123 { 124 return 1; 125 } 126 } 127 128 /* 129 * 查找指定元素的位置 130 */ 131 int FindElem(LinkList L, ElemType e, Status (*compare)(ElemType, ElemType)) 132 { 133 int i = 0; 134 LinkList p = L->next; 135 while (p) 136 { 137 i++; 138 if (!compare(p->data, e)) 139 { 140 return i; 141 } 142 p = p->next; 143 } 144 return 0; 145 } 146 147 /* 148 * 获取前驱元素 149 */ 150 Status PreElem(LinkList L, ElemType cur_e, ElemType *pre_e) 151 { 152 LinkList q, p = L->next; 153 while (p->next) 154 { 155 q = p->next; 156 if (q->data == cur_e) 157 { 158 *pre_e = p->data; 159 return OK; 160 } 161 p = q; 162 } 163 return ERROR; 164 } 165 166 /* 167 * 获取后继元素 168 */ 169 Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e) 170 { 171 LinkList p = L->next; 172 while (p->next) 173 { 174 if (p->data == cur_e) 175 { 176 *next_e = p->next->data; 177 return OK; 178 } 179 p = p->next; 180 } 181 return ERROR; 182 } 183 184 /* 185 * 插入元素 186 */ 187 Status InsertElem(LinkList L, int i, ElemType e) 188 { 189 int j = 0; 190 LinkList s, p = L; 191 while (p && j < i - 1) 192 { 193 j++; 194 p = p->next; 195 } 196 if (!p || j > i - 1) 197 { 198 return ERROR; 199 } 200 s = (LinkList) malloc(sizeof(LNode)); 201 s->data = e; 202 s->next = p->next; 203 p->next = s; 204 return OK; 205 } 206 207 /* 208 * 删除元素并返回值 209 */ 210 Status DeleteElem(LinkList L, int i, ElemType *e) 211 { 212 int j = 0; 213 LinkList q, p = L; 214 while (p->next && j < i - 1) 215 { 216 j++; 217 p = p->next; 218 } 219 if (!p->next || j > i - 1) 220 { 221 return ERROR; 222 } 223 q = p->next; 224 p->next = q->next; 225 *e = q->data; 226 free(q); 227 return OK; 228 } 229 230 /* 231 * 访问元素 232 */ 233 void visit(ElemType e) 234 { 235 printf("%d ", e); 236 } 237 238 /* 239 * 遍历线性表 240 */ 241 void TraverseList(LinkList L, void (*visit)(ElemType)) 242 { 243 LinkList p = L->next; 244 while (p) 245 { 246 visit(p->data); 247 p = p->next; 248 } 249 } 250 251 int main() 252 { 253 LinkList L; 254 InitList(&L); 255 ElemType e; 256 int i; 257 if (L) 258 { 259 printf("init success\n"); 260 } 261 262 if (isEmpty(L)) 263 { 264 printf("list is empty\n"); 265 } 266 267 for (i = 0; i < 10; i++) 268 { 269 InsertElem(L, i + 1, i); 270 } 271 272 if (GetElem(L, 1, &e)) { 273 printf("The first element is %d\n", e); 274 } 275 276 printf("length is %d\n", GetLength(L)); 277 278 printf("The 5 at %d\n", FindElem(L, 5, *compare)); 279 280 PreElem(L, 6, &e); 281 printf("The 6's previous element is %d\n", e); 282 283 NextElem(L, 6, &e); 284 printf("The 6's next element is %d\n", e); 285 286 DeleteElem(L, 1, &e); 287 printf("delete first element is %d\n", e); 288 289 printf("list:"); 290 TraverseList(L,visit); 291 292 DestroyList(&L); 293 if (!L) { 294 printf("\ndestroy success\n"); 295 } 296 }
总结
顺序存储结构中的元素在逻辑位置和物理位置上都相邻,链式存储结构中的元素在逻辑位置上相邻,但在物理位置上不一定相邻,顺序存储结构读取元素的效率比较高,链式存储结构添加和删除元素的效率比较高。链式存储结构除了单链表之外,还有循环链表和双向链表。

浙公网安备 33010602011771号