单链表的基本操作

链表:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称为存储单元为一个节点。
这里写图片描述

单链表的存储结构
typedef int DataType;

typedef struct ListNode
{
    DataType _data;                 //当前节点中所保存的元素
    struct ListNode* _pNext;    //指向链表中下一个结点

}SLinkList, SListNode;
链表的分类

这里写图片描述

单链表的基本操作
void PrintSLinkList(SLinkList* pHead);//打印
void InitSLinkList(SLinkList** ppHead);//初始化
void DestroySLinkList(SLinkList** ppHead);//销毁
void PushBack(SLinkList** ppHead, DataType data);//尾部插入
void PopBack(SLinkList** ppHead);//尾部删除
void PushFront(SLinkList** ppHead, DataType data);//头部插入
void PopFront(SLinkList** ppHead);//头部删除
SLinkList* Find(SLinkList* pHead, DataType data);//查找元素的位置
void Delete(SLinkList** ppHead, SLinkList* pos);//指定位置删除元素
void Revome(SLinkList** ppHead, DataType data);//删除第一个出现的指定元素
void RemoveAll(SLinkList** ppHead, DataType data);//删除出现的所有元素
int GetLinkListLength(SLinkList* pHead);//计算单链表的长度
void BubbleSort(SLinkList** ppHead);//冒泡排序

0.打印
//打印
void PrintSLinkList(SLinkList* pHead)
{
    SLinkList* cur = pHead;
    while (cur)
    {
        printf("%d -> ", cur->_data);
        cur = cur->_pNext;
    }
    printf("NULL\n");
}
1.初始化

 初始化就是让指向单链表的指针指向空,因为要改变指针的志向所以需要传指针的地址

//初始化
void InitSLinkList(SLinkList** ppHead)
{
    assert(ppHead);
    *ppHead = NULL;
}
2.销毁

 因为单链表都是用malloc开辟的内存空间,所以每次使用结束都要将其释放掉,所以就有一个销毁函数,将所有的结点释放掉,并让指向链表的指针指向空。

//销毁
void DestroySLinkList(SLinkList** ppHead)
{
    SLinkList* cur = *ppHead;
    SLinkList* del = NULL;

    assert(ppHead);

    while (cur)
    {
        del = cur;
        cur = cur->_pNext;

        free(del);
        del = NULL;
    }
    *ppHead = NULL;
}

节点的函数

 由于后面的插入会用malloc开辟空间,制作节点,所以在这里提前将我封装的函数写下来

SLinkList* BuyNode(DataType data)
{
    SLinkList* p = (SLinkList*)malloc(sizeof(SLinkList));
    //malloc开辟的空间一定要判断成功还是失败
    if (p == NULL)
    {
        perror("BuyNode::malloc");
        return NULL;
    }
    //空间开辟成功,将赋给节点中的数据域,将指针域赋为空指针
    p->_data = data;
    p->_pNext = NULL;
    //返回节点的地址
    return p;
}
3.尾部插入
1.空链表:直接连接,不需要遍历
2.非空链表:找到最后一个节点,连接

 尾部插入,顾名思义就要知道它的尾部在哪里,所以,我们要遍历取找它的尾部,单恋表的最后一个元素没有指向任何元素,所以它指向小孩一个节点的指针指向空,找到后插入。单链表有时是一个空表,需要改变指针的值,所以需要传指针的地址。
这里写图片描述

//尾部插入
void PushBack(SLinkList** ppHead, DataType data)
{
    SLinkList* NewNode = NULL;
    SLinkList* cur = *ppHead;

    assert(ppHead);

    NewNode = BuyNode(data);

    //1.空链表:直接连接
    if (*ppHead == NULL)
    {
        *ppHead = NewNode;
    }
    //2.非空链表
    else
    {
        //a.找最后一个结点
        while (cur->_pNext)
        {
            cur = cur->_pNext;
        }
        //b.连接
        cur->_pNext = NewNode;
    }
}
4.尾部删除
1.空链表:不能删除,直接结束
2.只有一个节点:删除,将头指针置空
3.两个及以上的节点:找到最后一个节点并将倒数第二个节点置空
//尾部删除
void PopBack(SLinkList** ppHead)
{
    SLinkList* cur = *ppHead;
    assert(ppHead);
    //1.空链表:不能删除,直接结束
    if (*ppHead == NULL)
    {
        printf("单链表为空,不能删除!!!\n");
        return;
    }
    //2.只有一个元素:删除,并将头结点置空
    else if ((*ppHead)->_pNext == NULL)
    {
        free((*ppHead)->_pNext);
        *ppHead = NULL;
    }
    //3.两个及以上的元素:找最后一个元素,删除
    else
    {
        while (cur->_pNext->_pNext != NULL)
        {
            cur = cur->_pNext;
        }
        free(cur->_pNext);
        cur->_pNext = NULL;
    }
}

5.头部插入
单链表的头部插入都需要改变指向链表指针的指向,所以不分情况可以直接操作
//头部插入
void PushFront(SLinkList** ppHead, DataType data)
{
    SLinkList* NewNode = NULL;
    assert(ppHead);

    NewNode = BuyNode(data);

    NewNode->_pNext = *ppHead;
    *ppHead = NewNode;
}
6.头部删除
单链表为空,不能随其进行删除操作,所以需要分别其为空链表还是非空链表
//头部删除
void PopFront(SLinkList** ppHead)
{
    SLinkList* cur = *ppHead;
    assert(ppHead);
    //1.空链表,不能删除
    if (*ppHead == NULL)
    {
        printf("单链表为空,不能删除!!!\n");
        return;
    }
    //2.非空链表
    *ppHead = cur->_pNext;
    free(cur);
    cur = NULL;
}
7.查找元素的位置

 从单链表的第一个节点开始,判断当前节点的数据域的值是否与给定的值相等。若相等,则返回该节点的地址,否则继续比较下一个节点,直到链表结束。若节点不存在,则返回空。

//查找元素的位置
SLinkList* Find(SLinkList* pHead, DataType data)
{
    SLinkList* cur = pHead;
    while (cur != NULL)
    {
        if (cur->_data == data)
            break;
        cur = cur->_pNext;
    }
    return cur; //有可能返回空值或所查元素的地址
}
8.指定位置删除元素

 遍历单链表,由于要删除指定位置的元素,需要将前一个节点的next指针域指向被删除节点的下一个节点。
这里写图片描述

//指定位置删除元素
void Delete(SLinkList** ppHead, SLinkList* pos)
{
    SLinkList* cur = *ppHead;
    SLinkList* del = NULL;
    assert(ppHead);
    //空链表
    if (*ppHead == NULL)
        return;
    //要删除位置元素为第一个节点
    if (*ppHead == pos)
        PopFront(ppHead);
    while (cur != NULL && cur->_pNext != pos)
    {
        cur = cur->_pNext;
    }
    if (cur != NULL)
    {
        cur->_pNext = cur->_pNext->_pNext;
        free(pos);
        pos = NULL;
    }
}
9.删除第一个出现的元素

 找到第一个指定元素出现的元素的前一个节点,指向被删除元素的下一个节点。因为是使用malloc开辟的空间,所以要用free()释放掉。
这里写图片描述

//删除第一个出现的指定元素
void Revome(SLinkList** ppHead, DataType data)
{
    SLinkList* cur = *ppHead;
    SLinkList* pre = NULL;
    assert(ppHead);
    //链表为空
    if (*ppHead == NULL)
        PopFront(ppHead);
    //链表非空
    while (cur != NULL)
    {
        if (cur->_data == data)
        {
            //首节点为所删元素
            if (pre == NULL)
                PopFront(ppHead);   //头删法
            else
            {
                pre->_pNext = cur->_pNext;
                free(cur);
                cur = NULL;
            }
            return;
        }
        pre = cur;
        cur = cur->_pNext;
    }
}
10.删除出现的所有元素

这里写图片描述

//删除出现的所有元素
void RemoveAll(SLinkList** ppHead, DataType data)
{
    SLinkList* cur = *ppHead;
    SLinkList* pre = NULL;
    SLinkList* del = NULL;
    assert(ppHead);
    //空链表
    if (*ppHead == NULL)
        return;
    //非空链表
    while (cur != NULL)
    {
        if (cur->_data == data)
        {
            //要删除的元素是第一个节点
            if (pre == NULL)
            {
                del = cur;
                cur = cur->_pNext;
                *ppHead = cur;
            }
            else
            {
                //用del指定需要被删除的元素,将被删除元素从链表上取下来
                del = cur;
                cur = cur->_pNext;
                pre->_pNext = cur;
            }
            //删除需要删除的结点
            free(del);
            del = NULL;
        }
        else
        {
            pre = cur;
            cur = cur->_pNext;
        }
    }
}
11.计算单链表的长度

 利用count,对链表的结点进行计数。

//计算单链表的长度
int GetLinkListLength(SLinkList* pHead)
{
    SLinkList* cur = pHead;
    int len = 0;
    //空链表
    if (pHead == NULL)
        return 0;
    //非空链表
    while (cur != NULL)
    {
        len++;
        cur = cur->_pNext;
    }
    return len;
}
11.冒泡排序

   外循环为链表的长度-1次,设置一个尾结点,外循环每执行一次,尾结点向前移动一步。

//冒泡排序
void BubbleSort(SLinkList** ppHead)
{
    SLinkList* pTail = NULL;    //标记:未排序元素的最后一个元素
    SLinkList* pPre = NULL;
    SLinkList* pCur = NULL;

    assert(ppHead);
    //趟数
    while (pTail != *ppHead)
    {
        pPre = *ppHead;
        pCur = *ppHead;
        //单趟
        while (pCur != pTail)
        {
            //升序
            if (pPre->_data > pCur->_data)
            {
                DataType tmp = pPre->_data;
                pPre->_data = pCur->_data;
                pCur->_data = tmp;
            }
            pPre = pCur;
            pCur = pCur->_pNext;
        }
        pTail = pPre;
    }
}
12.选择排序
void SelectsortLinkList(SLinkList *pHead)
{
    SLinkList *max = pHead;  //最大值指针
    SLinkList *tail = pHead;
    SLinkList *cur = NULL;
    DataType node = 0;
    int length = GetLinkListLength(pHead) - 1;
    //尾结点,tail指向空
    while (tail != NULL)
    {
        tail = tail->_pNext;
    }
    while (length--)
    {
        max = pHead;
        cur = pHead->_pNext;
        while (cur->_pNext != tail)
        {
            //找到链表的最大值
            if (cur->_data > max->_data)
            {
                max = cur;
            }
            cur = cur->_pNext;
        }
        //比较链表尾结点是不是最大值
        if (cur->_data > max->_data)
        {
            max = cur;
        }
        if (max != cur)
        {
            //交换
            node = max->_data;
            max->_data = cur->_data;
            cur->_data = node;
        }
        tail = cur;
    }
}
  • 37
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值