C语言数据结构:链表的操作实现

本文包括链表的基本操作:初始化、头插法、尾插法、遍历打印、获取尾结点地址、指定位置添加和删除结点、获取链表长度、得到尾指针、释放链表、获得倒数第K个结点的值(快慢指针法)、翻转链表。

在链表的学习中(个人觉得)我们需要注意的几点:

1、结点类型声明的格式,指针域不可以使用别名取声明,

2、指针域的熟悉,要懂得L->next的含义,看到后知道其内容是什么意思。

3、循环的临界条件判断,需要多次写代码去熟悉。

4、多学习技巧,去降低时间复杂度,如快慢指针。

快慢指针用法(后续还会补充):

1.找到倒数第K个值:快指针先走K步,循环向链表尾部走,直到指向NULL,这时慢指针就会指向想要的结点。

2、寻找链表的中点(也可以找到其他比例的点去分割链表):快指针一次向尾部走两步,慢指针走一步,直到指向NULL,这时慢指针就会指向中间结点(偶数链表会指向中间的上一个,奇数会指向中间值)。

3.链表是否循环:通过快2步慢1步的方法、判断地址是否有重复的地方。

#include <stdio.h>
#include <stdlib.h>
typedef int Elemtype;
// 正确认识 typedef的重要性, 如果需要把成千上万的int 改为double 会有很大工作量,
// 可以事先在表头定义typedef int i,修改时只需要把int 改为double
typedef struct node
{
    Elemtype data;     // 数据域
    struct node *next; // 指针域
} Node;
Node *Node_init();                                 // 链表初始化 分配空间
void insertHead(Node *L, Elemtype e);              // 头插法
void listNode(Node *L);                            // 遍历链表(应该使用一个临时指针来遍历链表,而不是修改传入的头节点指针 L)
Node *get_tail(Node *L);                           // 获取尾结点的地址
Node *insertTail(Node *tail, Elemtype e);          // 在尾结点后插入一个数据
int insertNode(Node *L, int location, Elemtype e); // 在特定位置插入数据
int deletNode(Node *L, int location);              // 删除特定位置结点
int getlength(Node *L);                            // 获取链表长度
void freeList(Node *L);                            // 释放链表
int findNode(Node *L, Elemtype k);                 // 找到倒数第K个值
Node *reverslist(Node *L);                         // 翻转链表
Node *head;                                        // 定义头指针 在init初始化函数中对头指针分配空间
int main(int argc, char const *argv[])
{
    head = Node_init();  // 带头节点  链表初始化
    insertHead(head, 3); // 头插法
    insertHead(head, 2);
    insertHead(head, 1);
    Node *tail = get_tail(head); // 得到尾指针
    tail = insertTail(tail, 4);  // 尾插法 返回新的尾指针
    tail = insertTail(tail, 5);
    insertTail(tail, 6);   // 尾指针不变动
    listNode(head);        // 遍历链表
    Node *hd;              // 创建新的指针
    hd = reverslist(head); // 翻转链表
    listNode(hd);          // 遍历链表

    return 0;
}
Node *Node_init()
{
    Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配空间
    head->data = 0;                            // 初始化值为0
    head->next = NULL;                         // 初始化的指针为空
    return head;                               // 返回头指针
}
void insertHead(Node *L, Elemtype e)
{
    Node *p = (Node *)malloc(sizeof(Node)); // 要插入的结点分配空间
    p->next = L->next;                      // 新结点指向原头结点
    p->data = e;                            // 新结点赋值
    L->next = p;                            // 头指针指向新头结点
}
void listNode(Node *L)
{
    Node *p = L->next; // 因为头指针只能指向头结点,所以需要新的指针来遍历数组
    while (p != NULL)  // 尾结点的指针与为空,所以可以作为循环的条件
    {
        printf("%d ", p->data); // 打印新的指针指向的数据与
        p = p->next;            // 指针指向下一个结点
    }
    printf("\n");
}
Node *get_tail(Node *L)
{
    Node *p = L;            // 因为头指针只能指向头结点,所以需要新的指针来遍历数组
    while (p->next != NULL) // 尾结点的指针与为空,所以可以作为循环的条件 直到指向尾结点
    {
        p = p->next;
    }
    return p;
}
Node *insertTail(Node *tail, Elemtype e)
{
    Node *p = (Node *)malloc(sizeof(Node)); // 为新的尾结点分配空间
    tail->next = p;                         // 原尾结点指向新的尾结点,衔接
    p->data = e;                            // 新结点数据与赋值
    p->next = NULL;                         // 新尾结点指向空
    return p;
}
int insertNode(Node *L, int location, Elemtype e)
{
    Node *p = (Node *)malloc(sizeof(Node)); // 为新的结点分配空间
    for (int i = 0; i < location - 1; i++)  // 循环location-1次到达目标前一个位置,然后进行插入
    {
        L = L->next;
        if (L == NULL)
        {
            return 0;
        }
    }
    p->next = L->next;
    p->data = e;
    L->next = p;
}
int deletNode(Node *L, int location)
{
    Node *p = L;                           // 创建q指针,最后指向要被删除的结点
    for (int i = 0; i < location - 1; i++) // 循环location-1次到达目标前一个位置,然后进行删除
    {
        p = p->next;
        if (p->next == NULL) // 判断下一个结点(要被删除的结点)是否为空
        {
            printf("要删除的位置错误");
            return 1;
        }
    }
    Node *q = p->next; // q指向被删除的结点
    p->next = q->next; // p指向下一个结点,将q从链表中断开
    free(q);           // 释放删除结点的空间
    return 0;
}
int getlength(Node *L)
{
    Node *p = L;
    int count = 0;
    while (p != NULL) // 让计数器跟随指针自增,直到尾结点
    {
        p = p->next;
        count++;
    }
    return count; // 返回长度
}
void freeList(Node *L)
{
    Node *p = L->next;      // 定义p指针 指向头结点
    Node *q;                // 定义q指针
    while (p->next != NULL) // 检查是否到尾结点
    {
        q = p->next; // q指针指向p的下一个结点
        free(p);     // 释放p指向结点的内存
        p = q;       // p追随q
    }
    L->next = NULL; // 清空链表后,使头指针指向空
}
int findNode(Node *L, Elemtype k)
{
    Node *f = L->next; // 快指针
    Node *s = L->next; // 慢指针
    int len = 0;
    if (k > (len = getlength(head)))
    {
        printf("输入值不合法");
    }
    else
    {
        for (int i = 0; i < k; i++) // 让快指针先走k步
        {
            f = f->next;
        }
        while (f->next != NULL) // 当快指针指向末尾,慢指针所指就是倒数第K个结点
        {
            f = f->next;
            s = s->next;
        }
        printf("%d \n", s->data); // 打印倒数K结点的值
    }
    return 0;
}
Node *reverslist(Node *L)
{                           // 定义三个指针,使链表的指针方向翻转,达到翻转链表的目的
    Node *first = NULL;     // 1指针先作为新链表的尾指针指向空
    Node *second = L->next; // 2指针指向头结点
    Node *third;
    while (second != NULL) // 最后1指针指向原尾结点
    {
        third = second->next; // 3指针指向2的下一个结点
        second->next = first; // 2所指向的结点的指针域指1,翻转指针的指向
        first = second;       // 1 2两个指针下移
        second = third;
    }
    Node *hd = Node_init(); // 新建链表头指针,并初始化
    hd->next = first;       // 新指针指向新的头结点

    return hd; // 返回新链表的头指针
}

推荐某站孙老师的课程:(有详细的C语言代码,不是伪代码!!!)《数据结构(C 语言描述)》也许是全站最良心最通俗易懂最好看的数据结构课(最迟每周五更新~~)_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值