链表的基本原理和基本接口实现

本文详细介绍了链表的基本概念,包括链表与数组的区别、链表的优缺点以及链式存储的相关术语。重点讲解了单链表的数据结构、基本操作,如初始化、判断空表、销毁、清空、求表长、取值、插入、删除和按值查找。通过实例代码展示了这些操作的实现过程。
摘要由CSDN通过智能技术生成

链表总结复习

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;//作为头结点,数据域可以用来存放链表的长度,目前暂不使用该结点。
//由于每一个结点都由数据域和指向同一数据类型的指针域构成,所以使用结构体来定义一个结点。
//使用Elemtype 这种自定义的数据类型来定义数据域可以灵活的实现链表的拓展。

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++;
        }
        //NULL== current 说明最大值越界;i > index 说明最小值越界,比如index为负数
        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;//指向首元结点,index = 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;//指向首元结点,index = 1;
        while(current && e != current->data)
        {
            current = current->next;
            i = 2; //更新index
        }
    }
    return current;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值