链表(linked list)
来源:互联网 发布:基于java的毕业论文 编辑:程序博客网 时间:2024/06/05 07:18
链表的特点
- 根据线性表元素多少,长度可变化。
- 动态申请内存空间:元素删除或插入时,对应申请新的存储空间或释放原来占有的存储空间。
- 使用指针来链接各个结点,按照线性表的前驱关系将结点连接起来,结点之间内存地址不必相邻。
- 链表的访问:不能像数组那样根据下标直接访问某一个结点
i
,而需要从头结点开始,沿着link指针一个一个地计数(遍历),才能找到。 - 通常有一个first指针变量和一个last指针遍历,用来存储头结点地址和末尾结点的地址(便于快速在链表末尾添加结点)。
单链表(single link list)
结点数据结构
单链表结点的数据结构通常分为两部分:数据和指向后继结点的指针。
struct ListNode { ELEM data; ListNode * link;};ListNode *first, *last;
空链表的表示:
为了表示空链表,引入一个特殊的“首结点”,该节点中data的值被忽略,该节点不被看做列表中的实际元素,只是为了方便空链表操作和表头操作。当链表为空时,first和last均指向首结点,如下:
ListNode * headNode = new ListNode;first = headNode;last = headNode;
相关算法
查找结点
查找位置为i的结点。
ListNode * find(const int i) { // 返回“首结点”, if (-1 == i) { return first; } // 指向第一个结点; ListNode * p = first->link; int j=0; // 遍历到链表末尾或j==i时停止。 while(p != NULL && j<i) { p = p->link; j++; } // 当链表结点数量小于i时,返回NULL return p; }
新增结点
关键点:
- 找到前序结点,注意前序结点为NULL的情况;
- 先将新增结点的data和link设置好,新增结点的link指向前序结点的link;
再将其前序结点的link指向新增结点。 - 注意要考虑到新增结点在链首、中、尾这三种情况的处理,插入到链表末尾时,last指针指向该新增结点。
ListNode * insert(ELEM value, int i) { ListNode * preNode; // 查找到前序结点,因为特殊“首结点”的存在,使i==0的情况不用特殊处理。 preNode = find(i - 1); if (NULL == preNode) { return NULL; } ListNode * newNode; newNode = new ListNode; newNode->link = preNode->link; newNode->data = value; preNode->link = newNode; // 处理插入到链表尾部的情况 if (NULL == newNode->link) { last = newNode; } return newNode;}
删除结点
删除某一个结点的后续结点。
关键点:
- 在真正删除节点之前,安排好它的“后事”:将其前序结点的link指向待删除结点的link;
void removeAfter(ListNode * preNode) { // 暂存待删除结点 ListNode * delLink = preNode->link; if (delLink != NULL) { preNode->link = delLink->link; delete delLink; }}
链表长度
int length() { ListNode * curNode = first->link; int count = 0; while (curNode != NULL) { curNode = curNode->link; count++; } return count;}
单链表的缺点
- 可以方便地查询某个结点的后续结点,但不能直接查询其前驱结点。
双链表(double link list)
顾名思义,它是双向列表。主要在其每个结点中增加了一个指向前驱的指针rlink,指向后续结点的指针为llink;
结点结构
struct DblListNode { ELEM data; DblListNode * preLink; DblListNode * nextLink;};DblListNode *first, *last;
空链表表示和单链表一样,使用一个特殊“首结点”,其preLink为NULL。
相关算法
新增结点
在某个结点的后面新增一个结点。
关键点:
- 修改旧有结点的preLink或nextLink之前,一定要确定它的旧值是否已经利用完了?
DblListNode * insertAfter(ELEM value, dblListNode * node) { DblListNode * preNode = node->preLink; DblListNode * nextNode = node->nextLink; // 设置新增结点的各字段 DblListNode * newNode = new DblListNode; newNode->data = value; newNode->nextLink = node->nextLink; newNode->preLink = node; node->llink->rlink = newNode; node->llink = newNode; return newNode;}
删除结点
删除某个指定的结点。
关键点:
- 删除前处理好“后事”:如待删除结点的前驱和后续结点是否已安排妥当?
- 待删除结点的preLink和nextLink置为空。
void deleteNode(DblListNode * node) { node->preLink->nextLink = node->nextLink; node->nextLink->preLink = node->preLink; node->preLink == NULL; node->nextLink == NULL; delete node;}
链表 vs 数组
数组
数组的优点
- 没有使用指针,节省存储空间(因为指针需要2或4字节存储);
- 可直接访问指定下标的元素,简洁便利,程序更易懂;
数组的缺点
- 不可动态改变长度,必须事先确定数组长度。
- 往数组中插入、删除某个元素时,可能需要移动O(k)个元素。
- 需要连续的一大块存储空间,以存储所有的元素。
链表
链表的优点
- 无需事先确定长度,可事先动态增、删元素。
- 删除、插入元素时不需要移动O(k)个元素。
- 结点间的存储空间不需要连续,因而可以利用一些碎片空间。
应用场合
- 当线性表中需要经常删除、插入元素时,不使用数组,使用链表。
当线性表长度不确定时,不能使用数组,使用链表,否则可能为了预留足够的空间而定义一个很大的数组,造成资源浪费。
当读取操作频繁(特别是按位读取操作频繁),插入、删除操作不频繁的时候,可以使用数组。
- 当存储的数据占的存储空间与指针占有的空间相当时(1:1),要慎重考虑使用链表是否值得。
- 当元素内容经常更新,但删除、插入操作并不常见的时候,可以考虑使用数组作为一个“索引”,即数组中元素为一个指针,指针指向内容真正存储的地址,这样就很容易找到内容存储地址,并进行更新。
阅读全文
0 0
- Linked List(链表)
- 链表(linked list)
- Singly Linked List(链表)
- C001:数据结构7-链表(Linked list)
- C++链表(single-linked list)
- Leetcode141 - Linked List Cycle(链表)
- Leetcode234 - Palindrome Linked List(链表)
- Leetcode142 - Linked List Cycle II(链表)
- 141. Linked List Cycle (链表)
- 203. Remove Linked List Elements(链表)
- 206. Reverse Linked List (链表)
- 92. Reverse Linked List II(链表)
- 链表---Reverse Linked List
- 数据结构:链表(linked-list)
- Palindrome Linked List---链表
- Linked List 链表
- List(double linked)
- leetcode Palindrome Linked List 链表
- windows10安装Framework3.5失败问题
- Java程序员成神列表
- 学习随笔——如何打印一个类的全部信息
- bzoj4243: 交朋友
- 内网、外网、动态IP的通俗解释
- 链表(linked list)
- System类中环境属性
- tomcat服务设置开机自动启动模式!
- h3c 构建中小企业网络 实验手册 第3章 笔记
- linux安全-禁止密码登录及root登录
- 支付开发(六)----支付宝支付的几种方式
- mallloc/free和new/delete
- Android中关于Handle的用法和分析
- vb.net 教程 3-12 资源文件 1-1