单链表的基本操作

链表:

用一组任意的存储单元来存放线性表的节点,这组存储单元可以是连续的,也可以是非连续的,甚至是零散分布在内存的任何位置上。

typedef int DataType;
typedef struct Node
{
    DataType _data;
    struct Node* _next;
}Node, *PNode;

节点包括两个域:
数据域:用来存储节点的值;
指针域:用来存储数据元素的直接后继的地址(或位置)信息。
单链表:只有一个next指针域!
由于线性表中的第一个节点无前驱,所以应设一个头指针H指向第一个节点;
由于线性表最后一个节点没有直接后继,则指定单链表的最后一个节点的指针域为“空”(NULL)。

无头单链表

#include "Node.h"

// 创建新节点函数
PNode BuyNewNode(DataType data)
{
    PNode _new = (PNode)malloc(sizeof(Node));
    if (NULL == _new)
    {
        printf("内存申请失败\n");
        return NULL;
    }

    _new->_data = data;
    _new->_next = NULL;
    return _new;
}

// 尾插操作函数
void SListPushBack(PNode* pHead, DataType data)
{
    PNode _new = NULL;
    // 参数检验
    assert(pHead);

    _new = BuyNewNode(data);
    if (NULL == _new)
    {
        return;
    }
    else
    {
        // 申请到了新节点
        if (NULL == (*pHead))
        {
            // 如果单链表是空的话,让头指针直接指向新节点
            *pHead = _new;
        }
        else
        {
            // 链表非空
            PNode pCur = NULL;
            pCur = (*pHead);

            // 找链表的尾
            while (!(pCur->_next == NULL))
            {
                pCur = pCur->_next;
            }

            // 链接起来
            pCur->_next = _new;
        }
    }
}

// 尾删操作函数
void SListPopBack(PNode* pHead)
{
    PNode pCur = NULL; // 
    PNode pPre = NULL; // 用来保存当前节点前一个节点

    // 参数检验
    assert(pHead);

    pCur = *pHead;
    pPre = pCur;

    // 找单链表的尾,并用pPre保存前一个节点
    while (!(pCur->_next == NULL))
    {
        pPre = pCur;
        pCur = pCur->_next;
    }

    pPre->_next = NULL; // 找到尾之后直接让pPre->_next赋空
    free(pCur); // 释放掉从单链表摘下的节点
    pCur = NULL; // 避免野指针
}

// 头插操作函数
void SListPushFront(PNode* pHead, DataType data)
{
    PNode _new = NULL;

    // 参数检验
    assert(pHead);

    _new = BuyNewNode(data);
    if (NULL == _new)
    {
        return;
    }
    else
    {
        // 成功申请到新节点
        if (*pHead == NULL)
        {
            // 空链表 直接让头指针指向新节点
            *pHead = BuyNewNode(data);
            return;
        }
        // 链表非空 进行头插
        _new = BuyNewNode(data);
        _new->_next = *pHead;
        *pHead = _new;// 修改头指针
    }
}

// 头删操作函数
void SListPopFront(PNode* pHead)
{
    PNode pCur = NULL;

    // 参数检验
    assert(pHead);

    pCur = *pHead;
    if(NULL == pCur)
    {
        printf("链表为空\n");
        return;
    }
    // 单链表非空 
    *pHead = pCur->_next; // 让头指针指向头的_next域
    free(pCur); // 释放pCur
    pCur = NULL; // 避免野指针
}

// 在链表中查找值为data的元素,找到后返回值为data的结点 
PNode SListFind(PNode pHead, DataType data)
{
    PNode pCur = NULL;

    // 参数检验
    assert(pHead);

    pCur = pHead;
    // 遍历单链表 查找值为data的元素
    while (pCur != NULL)
    {
        if (pCur->_data == data)
        {
            return pCur; // 找到了返回
        }
        pCur = pCur->_next; // pCur后移
    }

    return NULL; // 找不到返回NULL
}

// 在pos位置插入值为data的结点 
void SListInsert(PNode* pHead, PNode pos, DataType data)
{
    PNode pPre = NULL;
    PNode pCur = NULL;
    PNode _new = NULL;

    // 参数检验
    assert(pHead);

    pCur = *pHead;
    pPre = pCur;

    if (pCur->_next == NULL)
    {
        // 链表为空
        if (pCur == pos)
        {
            // 头插
            _new = BuyNewNode(data);
            if (_new == NULL)
            {
                return;
            }
            else
            {
                pHead = &_new;
            }
        }
        else
        {
            // 链表位置不合法
            printf("pos位置不合法\n");
            return;
        }
    }
    // 链表不为空
    while (pCur != NULL)
    {
        if (pos == pCur)
        {
            _new = BuyNewNode(data);
            if (_new == NULL)
            {
                return;
            }
            else
            {
                _new->_next = pPre->_next;
                pPre->_next = _new;
            }
            return;
        }
        else
        {
            pPre = pCur;
            pCur = pCur->_next;
        }       
    }
    // 已经遍历完了单链表 仍没找到pos位置
    printf("pos位置不合法\n");
    return;
}

// 删除pos位置的结点 
void SListErase(PNode* pHead, PNode pos)
{
    PNode pCur = NULL;
    PNode pPre = NULL;

    // 参数检验
    assert(pHead);

    pCur = *pHead;
    pPre = pCur;
    if (pCur == NULL)
    {
        printf("链表为空!!!\n");
        return;
    }

    while (pCur != NULL)
    {
        if (pos == pCur)
        {
            pPre->_next = pCur->_next;
            free(pCur);
            pCur = NULL;
            return;
        }
        else
        {
            pPre = pCur;
            pCur = pCur->_next;
        }
    }
    // 已遍历单链表
    printf("pos位置不合法\n");
    return;
}

// 判断链表是否为空  为空返回1 不为空返回0
int SListEmpty(PNode pHead)
{
    if (pHead == NULL)
    {
        return 1;
    }
    return 0;
}

// 销毁链表 
void SListDestroy(PNode* pHead)
{
    PNode p = NULL;
    PNode q = NULL;
    // 参数检验
    assert(pHead);

    p = *pHead;
    while (p->_next != 0)
    {
        // 不是链尾时
        q = p->_next; // 让q指向头节点的后继节点
        free(p);
        p = q; // 让p和q都指向后继节点
    }

    *pHead = NULL; // 将头指针赋空
}
// 链表遍历
void PrintNode(PNode pHead)
{
    PNode pCur = NULL;
    // 参数检验
    assert(pHead);

    pCur = pHead;
    printf("单链表中所有元素:");

    while (pCur != NULL)
    {
        printf("%d->", pCur->_data);
        pCur = pCur->_next;
    }

    printf("NULL\n");

}
// Node.h
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>

typedef int DataType;
typedef struct Node
{
    DataType _data;
    struct Node* _next;
}Node, *PNode;

// 尾插 
void SListPushBack(PNode* pHead, DataType data);

// 尾删 
void SListPopBack(PNode* pHead);

// 头插 
void SListPushFront(PNode* pHead, DataType data);

// 头删 
void SListPopFront(PNode* pHead);

// 在链表中查找值为data的元素,找到后返回值为data的结点 
PNode SListFind(PNode pHead, DataType data);

// 在pos位置插入值为data的结点 
void SListInsert(PNode* pHead, PNode pos, DataType data);

// 删除pos位置的结点 
void SListErase(PNode* pHead, PNode pos);

// 判断链表是否为空 
int SListEmpty(PNode pHead);

// 销毁链表 
void SListDestroy(PNode* pHead);

// 链表遍历
void PrintNode(PNode pHead);

1.测试单链表尾插、尾删操作,遍历单链表

#include "Node.h"

void TestNodePushBack_PopBack()
{
    PNode pHead = NULL; // 头指针,并初始化
    SListPushBack(&pHead, 1); // 尾插元素
    SListPushBack(&pHead, 2);
    SListPushBack(&pHead, 3);
    SListPushBack(&pHead, 4);
    PrintNode(pHead); // 遍历打印单链表

    SListPopBack(&pHead); // 尾删一个元素
    PrintNode(pHead); // 遍历打印单链表

    SListDestroy(&pHead); // 销毁单链表
}

// 主函数
int main()
{
    TestNodePushBack_PopBack(); // 调用测试函数
    system("pause");
    return 0;
}

测试结果:
这里写图片描述

2.测试单链表头插、头删操作,遍历单链表

#include "Node.h"

void TestNodePushFront_PopFront()
{
    PNode pHead = NULL; // 头指针,并初始化
    SListPushFront(&pHead, 4); // 头插元素
    SListPushFront(&pHead, 3);
    SListPushFront(&pHead, 2);
    SListPushFront(&pHead, 1);
    PrintNode(pHead); // 遍历打印单链表

    SListPopFront(&pHead); // 头删一个元素
    PrintNode(pHead); // 遍历打印单链表

    SListDestroy(&pHead); // 销毁单链表

}

// 主函数
int main()
{
    TestNodePushFront_PopFront();
    system("pause");
    return 0;
}

测试结果:
这里写图片描述
3.测试单链表查找、指定位置插入、指定位置删除操作

#include "Node.h"

void TestNodeFind_Insert_Erase()
{
    PNode pHead = NULL;
    PNode ret_Find = NULL;
    SListPushBack(&pHead, 1);
    SListPushBack(&pHead, 2);
    SListPushBack(&pHead, 4);
    PrintNode(pHead);

    ret_Find = SListFind(pHead, 4);
    SListInsert(&pHead, ret_Find, 3);
    PrintNode(pHead);

    SListErase(&pHead, ret_Find);
    PrintNode(pHead);

    SListDestroy(&pHead); // 销毁单链表

}

// 主函数
int main()
{
    TestNodeFind_Insert_Erase(); // 调用测试函数
    system("pause");
    return 0;
}

测试结果:
这里写图片描述
4. 最后说一下单链表的销毁,为了避免内存泄漏,在使用完单链表之后一点要销毁。

请各位大佬点评,有错的地方一定要指正,谢谢

猜你喜欢

转载自blog.csdn.net/Eric_qiushui/article/details/80224022
今日推荐