链表总结复习
1.链表的基本概念
1.1 链表和数组的区别
链表和数组同为线性存储结构,在逻辑上并无本质上的不同,因为在逻辑上存储的数据是连续的,但是数组存储数据在逻辑和物理两个层面上都是连续的,链表在物理上并不连续。 链表由于在物理上不连续,所以链式存储结构只能从头指针开始顺序存取,数组由于物理上的连续性,所以可以实现数据的随机存取。 数组的缺点是在插入和删除元素的时候,需要大量的数据搬运工作,所以不适合用来作为频繁增删的数据结构。
1.2 链表和数组各自的优缺点
配图如下: 两种结构都有自己各自的优缺点,所以要选择合适的应用场景,发挥各自的优势。
1.3 链式存储的相关术语
结点:数据元素的存储映像(最小存储单元)。由数据域和指针域两部分组成。 链表的分类:
单链表:结点只有一个指针域的链表,成为单链表。 双链表:结点有两个指针域的链表 循环链表:首尾相接的链表。 头指针,头节点和首元结点
指向头节点的指针是头指针 头节点,链表的附加结点,用来存储一些链表相关的信息。 首元结点:第一个真正存储数据信息的结点。
1.4 链表存储结构的两种形式
1.5 讨论几个问题
如何表示空表
有头结点的时候,头指针的指针域为空表示为空表。 没有头结点的时候,头指针为空表示空表。
2.单链表的相关研究
2.1 单链表的数据结构
typedef struct _tag_LinkListNode
{
ElemType data;
LinkListNode * next;
} LinkListNode;
typdef struct _tag_LinkListHeader
{
int List_lenth;
LinkListNode * next;
} LinkListHeader;
2.2 单链表的基本操作
2.2.1 链表的初始化
1.算法步骤:
- 利用malloc函数申请头结点容量大小的内存空间。
- 初始化头结点的数据域为0,指针域为NULL
- 把首地址作为返回值,传给后期的头指针。
2.实现
LinkListNode* LinkList_Init(void)
{
LinkListNode* new = (LinkListNode*)malloc(sizeof(LinkListNode));
new->data = 0;
new->next = NULL;
return new;
}
2.2.2 判断链表是否为空
1.算法思路
- 带头结点,头结点的指针域是否为空。
- 不带头结点,链表指针是否为空。
2.实现
Status LinkList_IfEmpty(LinkList *list)
{
if(NULL != list)
{
if (NULL == list->next)
return Flase;
}
return True;
}
2.2.3 单链表的销毁
1. 算法思路
- 头指针不断向下一个结点移动。
- 保存还未时释放的结点地址。
- 释放掉前一个结点。
2. 代码
Status List_Destroy ( LinkList * list)
{
LinkList * tmp;
while ( NULL != list)
{
tmp = list;
list = list-> next;
free ( list) ;
}
reurn True;
}
2.2.4 清空链表
1. 思路(保留头指针和头结点,去掉其他结点)
- 不断切换后一个结点为首元结点。
- 释放掉剔出的首元结点,直到新的首元结点为空。
2.算法
Status LinkList_Empty(LinkList* list)
{
LinkListNode *current;
if (NULL != list)
{
current = list->next; //指向首元结点
while(NULL != current)
{
list->next = list->next->next;
free(current);
current = list->next;
}
}
return True;
}
2.2.5 求链表的表长
1.算法思路
- 从首元结点开始依次计数所有结点。
2.算法实现
- 从首元结点开始计数,一直找到NULL
3.代码
int LinkList_Lenth(LinkList *list)
{
int i = 0;
if (NULL != list)
{
LinkListNode *current = list->next;
while(NULL != current)
{
i++;
current = current->next;
}
}
return i;
}
2.2.6 取单链表的第i个值
1. 思路
- 单链表是一种顺序存储结构,所以只能从头结点开始遍历,之后取出数据。
2. 算法
- 获取首元结点,在首元结点的基础上,移动几次,临时指针就指向哪一个结点。
- 第0 个元素,不需要移动,第一个元素移动一次,. . . 依次类推规律就是第几个元素,移动几次。
- 利用for 或者 while 循环
3. 代码
void * LinkList_GetEle ( LinkList * list, int index)
{
void * tmp = NULL ;
if ( NULL != list)
{
if ( ( 0 < index) && ( index < LinkList_GetLenth ( list) ) )
{
LinkListNode * current = list-> next;
for ( int i= 0 ; i< index; i++ )
{
current = current-> next;
}
tmp = ( void * ) & current-> data;
}
}
return tmp;
}
4. 代码版本2 :( 更好的搜索同时不用提前进行边缘检查)
void * LinkList_GetEle ( LinkList * list, int index)
{
void * tmp = NULL ;
if ( NULL != list)
{
LinkNode * currnet = list-> next;
int i = 0 ;
while ( ( NULL != current) && ( i< index) )
{
current = current-> next;
i++ ;
}
if ( ( NULL != current) && ( i == index) )
{
tmp = ( void * ) & current-> data;
}
}
return tmp;
}
2.2.7 在合适的位置插入结点
1.思路
- 先搜索到要插入的结点的前一个结点
- 插入该节点
2.算法步骤
- 首先找到a[i-1]的存储位置p
- 生成一个新的数据域e的新节点s
- 插入新节点
- 新节点的指针域指向结点a[i]
- 结点a[i-1]指向新节点
3.代码
int LinkList_Insert(LinkList *list,int i,ElemType e)
{
int j = 0;
if (NULL != list)
{
LinkNode *new = (LinkNode*)malloc(sizeof(LinkNode));
LinkNode *current = list;
while(current && (j < i))
{
j++;
current = current->next;
}
if(current && (j == i))
{
new->next = current->next;
current->next = new;
}
}
return j;
}
2.2.8 在合适的位置删除结点
1. 算法步骤
- 找到a[ i- 1 ] 的位置p, 保存要删除的结点a[ i] 的地址。
- a[ i- 1 ] 指向a[ i+ 1 ]
- 删除a[ i]
2. 代码
int LinkList_Del ( LinkList * list, int i)
{
int tmp = 0 ;
if ( NULL != list)
{
LinkNode * new;
LinkNode * current = list;
int j = 0 ;
while ( current && ( j < i) )
{
j++ ;
current = current-> next;
}
if ( current && ( j == i) )
{
new = current-> next;
current-> next = current-> next-> next;
free ( new) ;
tmp = 1 ;
}
}
return tmp;
}
2.2.9 按值查找
1. 算法步骤
- 移动结点指针
- 比较数据域
2. 代码
version1: return the index of Element
int LinkList_Search ( LinkList * list, ElemType e)
{
if ( NULL != list)
{
LinkNode * current = list-> next;
int i = 1 ;
while ( current && ( e != current-> data) )
{
current = current-> next;
i++ ;
}
}
if ( ! current) i = 0 ;
return i;
}
version2: return the addr of node
LinkNode* LinkList_Search ( LinkList, ElemType e)
{
if ( NULL != list)
{
int i = 1 ;
LinkNode * current = list-> next;
while ( current && e != current-> data)
{
current = current-> next;
i = 2 ;
}
}
return current;
}