链表C 链表Linked List
是一种在物理上非非连续,非顺序的数据结构,由若干个节点组成
链表在内存中的存储与数组的存储方式并不相同
链表:每一个节点都位于内存中不同的位置,依靠指针在逻辑上是他们串连起来
数组:在创建的时候会直接分配一段连续内存空间
以一串连续的数字来举例子{1,2,3,4,5,6,7,8,9,0}
将这串数字分别采用链表 和数组 的方式存储,可以看到在给数组分配内存时是直接分配了一整块内存用来存储,在链表中是链表中是每一个数据都随机分配一个内存地址,使用连接线 (指针)连接起来
数组:
链表: 这里只用单向链表做一个示例
如果将单向链表抽象出来,在逻辑上就像这张图一样一个节点 连接一个节点
链表的组成
节点:一个链表由若干个节点组成
头节点指针:链表需要一个头指针,用来指向链表的起始位置
尾节点指针:不必须,本文只是将最后一个节点的next
指向NULL
,如有需要可自行添加
链表的优势和劣势 优势:链表能够灵活地插入,删除节点,所以更适用于插入,更新,删除操作更多场景。 劣势:链表的查找操作,需要从头遍历整个链表,性能低下,不适用与读操作多的场景。
链表的操作 链表单个节点的结构体
1 2 3 4 typedef struct LNode { int data; struct LNode * next ; } LNode,*LinkList;
初始化 链表初始化了一个空的头节点 ,这个头节点一般不存储任何数值或者可以用来存储链表长度 等一些信息,有头节点的情况下可以方便我们做数据插入 和删除
1 2 3 4 void InitLink (LinkList *head) { *head = (LinkList)malloc (sizeof (LNode)); (*head)->next = NULL ; }
初始化后的链表如图所示,只有一个头节点,没有后继节点
查 在链表中做查找并不能像数组一样,给定指定下标就可以直接获取下标对应的数据,必须从头节点开始,一个一个查找,直到到达指定位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int GetElem (LinkList head, int index, Elemtype* data) { LinkList p; int i; p = head->next; for (i = 1 ; i < index; i++) { p = p->next; } if (i > index || !p) { return 0 ; } else { *data = p->data; return 1 ; } }
增 添加节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int InsertLink (LinkList head, int index, int data) { LinkList p = head; int i = 0 ; while (i < post-1 ) { p = p->next; i++; } if (!p || i > post) { return 0 ; } else { LinkList s = (LinkList)malloc (sizeof (LNode)); s->data = elem; s->next = p->next; p->next = s; return 1 ; } }
添加的节点的过程如下过程所示:
创建一个空的节点s
,给他添加数据
将p
的后继节点都存储到s
的后继节点中
p->next
不再指向原来的后继节点
p->next
指向新的s
节点
最后结果如下:
删 删除某个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int LinkDelete (LinkList head, int index, int * data) { LinkList p = head,tmp_node; int i = 0 ; while (p->next && i < index - 1 ) { p = p->next; i++; } if (!p->next || i > index - 1 ) { return ERROR; } else { tmp_node = p->next; p->next = tmp_node->next; *elem = tmp_node->data; free (tmp_node); return OK; } }
删除的节点的过程如下过程所示:
将p->next
节点存储再一个临时节点tmp_node
中
将临时节点tmp_node
的下一个节点存储在p->next
节点中,就相当于再链表中跳过了tmp_node
节点
如果有需要可以将tmp_node
中的数据提取出来,返回到主函数中
释放掉tmp_node
最后结果如下:
改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int ChanegElem (LinkList head, int index, int data) { LinkList p; int i; p = head->next; for (i = 1 ; i < index; i++) { p = p->next; } if (i > index || !p) { return 0 ; } else { p->data = data; return 1 ; } }