单链表及其基本操作

1 链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

 

 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构: 1. 单向、双向 2. 带头、不带头 3. 循环、非循环。

常用的有无头单向非循环链表、带头双向循环链表。

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。

下面是无头单向非循环链表的一些基本操作代码:

//NodeList.c
#include"NodeList2.h"
//遍历
void print(Node* head)
{
    Node* cur = head;   //声明一个新指针用来进行遍历
    while (cur)
    {                  //cur为NULL时链表遍历完
        printf("%d-->", cur->val);
        cur = cur->next;
    }
    printf("\n");
}
//销毁链表
Node* NodeListDestory(Node* head)
{
    Node* cur = head;
    while (cur)            //每一个节点都要销毁
    {
        Node* node = cur->next;
        free(cur);
        cur = node;
    }
    head = NULL;
    return head;
}
//头插
Node* NodeListPushFront(Node* head, DataType x)
{
    Node* node = (Node*)malloc(sizeof(Node)); //申请节点
    node->val = x;          // 将数据放入节点中
    node->next = head;       //新节点的下一个节点置为原链表的头结点
    return node;             //返回新的头结点

    //head=node;return head;
}


//尾插
Node* NodeListPushBack(Node* head,DataType x)
{
    if (NULL == head)
    {
        head=NodeListPushFront(head, x);   //如果原链表为空,插入第一个元素用头插
    }
    else
    {              //原链表不为空,申请新节点
        Node* node = (Node*)malloc(sizeof(Node));
        node->val = x;               //将要插入的数据放入节点
        node->next = NULL;           //因为是尾插,所以插入的节点的下一个一定为空节点
        Node* cur = head;           //声明一个变量来进行遍历
        while (cur->next != NULL)  //cur的next为NULL时说明cur是最后一个节点
        {
            cur = cur->next;
        }
        cur->next = node;   //循环结束,cur遍历到链表最后一项的位置,使next指向新节点,完成尾插
    }
    return head;
}
//头删
Node* NodeListPopFront(Node* head)
{
    assert(head);
    if (NULL == head->next)  //链表的第一个节点的next为空时说明链表只有一个节点
    {
        free(head);
        head = NULL;
        return head;
    }
    else            //链表不止一个节点时
    {
        Node* node = head->next;  //定义一个新指针指向head的next
        free(head);             //释放head节点
        return node;            //返回node节点,即为将原链表的第二个节点返回,外部接收使其成为新的头结点
    }
}
//尾删
Node* NodeListPopBack(Node* head)
{
    assert(head);
    if (NULL == head->next)  //链表只有一个节点
    {
        free(head);
        head = NULL;
    }
    else  //链表不止一个节点时
    {
        Node* cur = head;
        while (NULL != cur->next->next) //节点的next的next为空,说明当前节点在倒数第二个节点
        {
            cur = cur->next;
        }
        //循环结束则说明cur已经是倒数第二个节点,它的下一个是最后一个节点,也就是要删除的节点
        free(cur->next);     //释放cur的下一个节点,即删除最后一个节点
        cur->next = NULL;    //cur的next置为空,防止野指针
    }
    return head;
}

//查找x并第一次在链表中出现的位置,没有该元素则输出提示信息
Node* NodeListFind(Node* head, DataType x)
{
    assert(head);
    int count = 0;
    for (Node* cur = head; cur != NULL; cur = cur->next)
    {
        ++count;
        if (x == cur->val)
        {
            printf("%d\n", count);  //记录x是链表中的第几个元素
            return cur;         //如果找到x第一次出现的位置,返回其所在的节点地址
        }

    }
    count = 0;
    printf("链表中没有%d\n", x);
    return NULL;  //如果会跳出循环,说明链表中没有x,返回NULL表示该元素不存在链表中
}

//删除x第一次出现的节点
Node* NodeListRemove(Node* head, DataType x)
{
    assert(head);
    if (x == head->val)
    {
        Node *node = head->next;
        free(head);
        return node;
    }
    else
    {
        Node*  cur = head;
        while (cur)
        {
            if (x == cur->next->val)  //第一个元素已经在前面判断过,所以从第二个元素的值开始判断
            {                         //当前节点的next节点的值为x时进行删除操作
                Node* node = cur->next;  //记录下要删除的节点,以免断开链接后失去地址,无法释放
                cur->next = cur->next->next;   //当前节点的next指向next的next,完成删除
                free(node); //释放删除的节点的空间
                return head;

            }
            cur = cur->next;
        }
    }
}
//NodeList.h
typedef int DataType;
typedef struct Node
{
    DataType val;
    struct Node* next;
}Node;

//销毁链表
Node* NodeListDestory(Node* head);
//头插
Node* NodeListPushFront(Node* head, DataType x);
//遍历
void print(Node* head);
//尾插
Node* NodeListPushBack(Node* head, DataType x);
//头删
Node* NodeListPopFront(Node* head);
//尾删
Node* NodeListPopBack(Node* head);
////查找x并第一次在链表中出现的位置
Node* NodeListFind(Node* head, DataType x);
//删除x第一次出现的节点
Node* NodeListRemove(Node* head, DataType x);

//删除值为x的所有节点
Node* NodeListRemoveAll(Node* head, DataType x);
//test.c
#include"NodeList2.h"
void test()
{
    Node* head;
    head = NULL;  //链表为空即为初始化
    head = NodeListPushFront(head, 5);

    head = NodeListPushFront(head, 4);
    head = NodeListPushFront(head, 3);
    head = NodeListPushFront(head, 2);
    head = NodeListPushFront(head, 1);
    head = NodeListPushFront(head, 5);

    print(head);
    head = NodeListPushBack(head, 6);
    head = NodeListPushBack(head, 7);
    head = NodeListPushBack(head, 8);
    head = NodeListPushFront(head, 5);
    //head=NodeListDestory(head);  

    print(head);
    //head = NodeListPopFront(head);
    //print(head);
    //head = NodeListPopFront(head);
    //print(head);

    //head = NodeListPopBack(head);
    //print(head);
    //head = NodeListPopBack(head);
    //print(head);

    //Node* pos = NodeListFind(head, 11);

    //head=NodeListRemove(head, 5);
    //print(head);

    system("pause");
}


int main()
{
    test();

    return 0;
}

 

 

posted on 2019-11-30 01:58  青椒炒肉没有肉  阅读(161)  评论(0编辑  收藏  举报

导航