C链表总结——单链表

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近学了一下关于单链表的知识以及做了一些链表有关的习题,接下来做一下单链表的总结,所用语言为C


一、什么是单链表

如上图,这就是一个简单的单链表示意图。(ppt制作有点烂哈)

首先,在回答什么是单链表前,先简单谈谈什么是链表。链表就是一种数据结构,而之前学到的数组也是一种数据,链表和数组算是两种最简单数据结构了。

而单链表,顾名思义,就是单向的链表,如上面的示意图,每个方块就是链表的一个节点,每个节点一般至少包含两个变量,val和next,val指的是数值(上图中的1、2、3),next是结构体指针变量,它指向的是后一个节点。单链表中最特殊的就是头节点和尾节点。头节点指的是最前面的节点,为什么说头节点特殊,因为单链表的指向性是单向的,也就说如果想要遍历全部节点,我们需要从头节点开始遍历。而尾节点则是指向NULL空节点的节点,上图的尾节点是 3 节点,注意,并不是NULL节点!

二、单链表的代码实现

1.链表的定义

此处所用的IDE为codebolcks

代码如下(示例)

//头文件
#include<stdio.h>   //标准输入输出头文件 
#include<stdlib.h>  // 包含了C常用的系统函数

// 宏定义相关变量
#define TYPE struct ListNode // 宏定义结构体变量名

//定义链表,创建结构体
struct ListNode
{
    int val;
    // 声明结构体指针变量,指向结构体——链表
    struct ListNode* next; 
};

2.链表的实现

        1) 静态实现,(main函数)代码录入

int main(void)
{
    TYPE *head, *t; //声明头节点
    struct ListNode a, b, c; //声明三个链表节点
    /*  1. A.B 则A为对象或结构体
        2. A->B 则A为指针,->表示成员提取,即结构体内的元素变量,比如这里用的val,next
        */
    a.val = 1;  //对应成员赋值
    a.next = &b;    //标注节点指向   
    b.val = 2;
    b.next = &c;
    c.val = 3;
    c.next = NULL;
    head = &a;   //头节点位置
    t = head;
    while(t != NULL)
    {
        printf("%d\n",t->val);
        t = t->next;
    }

    return 0;
}

        2)动态实现

                需要引入动态分配内存头文件

#include<malloc.h>  //动态分配内存函数头文件

                链表声明定义同上

                创建链表函数


TYPE* creat(void)
{
    TYPE* head;
    //声明两个节点
    TYPE* a, *b;
    //声明节点数变量并初始化为0
    int number = 0;
    //动态分配节点内存空间,开辟节点
    a = b = (TYPE*)malloc(sizeof(struct ListNode));
    printf("input: val = \n");
    scanf("%d", &a->val);
    head = NULL;
    //当输入77时,循环终止
    while(a->val != 77)
    {
        ++ number;
        //当节点数为1时,头指针指向第一个节点信息
        if(number == 1)
        {
            head = a;
        }
        else
        {
            //节点数大于1时,用next保存当前节点信息
            b->next = a;
        }
        //保存当前节点信息
        b = a;
        a = (TYPE*)malloc(sizeof(struct ListNode));
        scanf("%d",&a->val);
    }
    //循环终止后,将尾节点赋值为NULL
    b->next = NULL;
    //返回head头节点
    return (head);
}

                循环输出主函数

int main(void)
{
    TYPE *kk;
    //将kk赋值为head
    kk = creat();
    //默认链表不是空链表
    if(kk != NULL)
    {
        //循环打印链表的val值,直到空节点
        do{
            printf("\n val=%d\n", kk->val);
            kk = kk->next;
        }while(kk != NULL);
    }

    return 0;
}

                        输出结果

creat()函数的定义参考了https://blog.csdn.net/xiaoxiaodawei/article/details/104807198

这位dalao对于单链表的解释远胜于我,有需要的不妨去看看。

三、相关习题和题解

我认为编程的学习离不开刷题,以下题目都是来自于leetcode网站

       1. 删除链表中的节点

编写一个函数,使其可以删除某个链表中的给定的(非末尾)节点,传入函数的唯一参数为 要被删除的节点。

比如一个链表=[4,5,1,9],如下图

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9


链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list/

题目的模板如下

**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
void deleteNode(struct ListNode* node) {
    
}

这道题并没有给出链表的head节点,而是给出了具体要删除的节点,这时候怎么做呢?

很简单,只需这个节点的后一个节点等于它,在把它的指向变成下下个节点即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
void deleteNode(struct ListNode* node) {
    node->val = node->next->val;
    node->next = node->next->next;
}

        2. 删除倒数第n个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

题目链接 https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

题目模板

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeNthFromEnd(struct ListNode* head, int n){

}

                1)法一,遍历得到长度,然后再遍历一次删除节点。

​
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int getlength(struct ListNode* head){
    int len = 0;
    while(head){
        len++;
        head = head->next;
    }return len;
}

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode* newLN = malloc(sizeof(struct ListNode));
    newLN->val = 0; newLN->next = head;
    int len = getlength(head);
    struct ListNode* cur = newLN;
    for(int i = 1; i < len - n + 1; ++i){
        cur = cur->next;
    }
    cur->next = cur->next->next;
    struct ListNode* ans = newLN->next;
    free(newLN);
    return ans;
}

​

               2)法二,栈实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct Stack{
    struct ListNode* val;
    struct Stack* next;
};

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode* dummy = malloc(sizeof(struct ListNode));
    dummy->val = 0, dummy->next = head;
    struct Stack* stk = NULL; 
    struct ListNode* cur = dummy;
    while(cur){
        struct Stack* tmp = malloc(sizeof(struct Stack));
        tmp->val = cur, tmp->next = stk;
        stk = tmp;
        cur = cur->next;
    }
    for(int i=0; i<n; ++i){
        struct Stack* tmp = stk->next;
        free(stk);
        stk = tmp;
    }
    struct ListNode* prev = stk->val;
    prev->next = prev->next->next;
    struct ListNode* ans = dummy->next;
    free(dummy);
    return ans;
}

        3. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

题目链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){

}

                1)迭代实现

​
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    // 声明前节点, 并初始为 NULL 空值
    struct ListNode* prev = NULL;
    // 声明节点,初始为所给头节点
    struct ListNode* curr = head;
    // 循环,当节点不为空值循环
    while(curr)
    {
        // 声明节点,初始化为 curr 的下一节点
        struct ListNode* next = curr -> next;
        // 将前节点 赋值给 curr(当前节点)的下一节点
        curr -> next = prev;
        // 前节点赋值为当前节点
        prev = curr;
        // curr(当前节点)赋值为原来的下一节点
        curr = next;
    }
    // 返回前节点,此时为原来的链表的最后一个非空节点
    return prev;
}

​

                        2)递归实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    if(head == NULL || head -> next == NULL)
    {
        return head;
    }
    // 声明新的头节点,并递归初始化,最后新的头节点变成原来的最后一个节点
    struct ListNode* newHead = reverseList(head -> next);
    // 把节点k+1+1 赋值为 k
    head -> next -> next = head;
    // 把k+1 赋值为空值
    head -> next = NULL;
    
    //返回新头节点
    return newHead;
}


总结

单链表,其实可以理解为一个数据域,每个节点都有相应的数据信息,且只能指向后一个节点,一个有限的单链表的尾节点会指向NULL

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值