《算法通关村第一关——链表青铜挑战笔记》

目录

  1. 如何构造链表
  2. 如何遍历链表
  3. 插入链表节点
  4. 删除链表节点

一、如何构造链表

1、基本概念

  • 链:各结点之间以指针(指出元素地址)相连的数据结构;
  • 两个域是广义的,数据域可以有多个数据项,就职能、功能而言,仅此2个。;
  • 存储单元可是连续的,也可是不连续的,由指针负责“联系”;
  • 线性链表 链表的每个结点中只包含一个指针域,又称单链表 ;
  • 头指针:第一个数据元素的存储地址作为线性表的基地址;
  • 链头结点:无data值,指向表首元;
  • 链尾结点:无next值,链为空

2、链表节点的结构

在C++中可以利用结构体表示数据域和指针域,具体代码如下所示:

struct ListNode {
      int data; //代表数据
      struct ListNode *next; //代表指针
 };

3、创建链表——尾插法

尾插法顾名思义,即在链表尾部进行节点的创建,为最常用的一种方法。具体而言,每次创建一个新的结构体,为其赋值后,由上一节点的指针指向新的节点即可完成创建,具体代码如下所示:

struct ListNode* initLink() {
    int i;
    //创建头指针
    struct ListNode* p = NULL; 
    //创建头节点
    struct ListNode* head = (struct ListNode* )malloc(sizeof(struct ListNode));
    //创建中间节点
    struct ListNode* temp = NULL;

    head -> val = 0;
    head -> next = NULL;
    p = head;
    temp = head;

    for (i = 1; i < 10; i++){
        //创建新节点
        struct ListNode* newNode = (struct ListNode* )malloc(sizeof(struct ListNode));
        newNode -> val = i;
        newNode -> next = NULL;
        temp -> next = newNode; //利用中间节点完成链表的逐步更新创建
        temp = temp -> next;
    }

    return p;
}

注意一些易错点:

  1. 不要直接用head进行操作,而应用其他的指针代替完成遍历链表的任务,防止链表丢失。
  2. 尾插法新节点的指针域注意为NULL。

4、创建链表——头插法

头插法与尾插法相似,即在链表头部(每次在头节点后插入)进行节点的创建,在特殊的题目条件下有奇效。具体而言,每次创建一个新的结构体,为其赋值后,由新节点的指针指向head的next所指向的节点,然后记得将新节点赋值给head的next即可,从而确保head始终指向头节点,即可完成创建,具体代码大部分与上述尾插法相同,核心代码如下所示:

for (i = 1; i < 10; i++){
        struct ListNode* newNode = (struct ListNode* )malloc(sizeof(struct ListNode));
        newNode -> val = i;
        newNode -> next = head -> next;
        head -> next = newNode;
    }

此时运行代码可以发现,头插法与尾插法生成的列表数字序列正好相反。


二、如何遍历链表

注意使用中间节点进行遍历,不要直接对head进行操作。

同时,注意以NULL为循环的终止条件,具体代码如下:

void printList(struct ListNode* p) {
    struct ListNode* temp = p;
    while (temp){
        printf("%d ", temp -> val);
        temp = temp -> next;
    }
    printf("\n");
}


int32_t getLength(struct ListNode* p) {
    struct ListNode* temp = p;
    int len = 0;
    while (temp){
        len++;
        temp = temp -> next;
    }
    return len;
}

需要注意的是getLength方法在之后的插入和删除中是必须的


三、插入链表节点

1、在第一个位置插入节点

此情况较为简单,将新节点的next指向head节点即可,记得最后要将head指向新节点,从而保证head节点始终指向的是链表的第一个节点。

2、在最后一个位置插入节点

同样,让原链表中最后一个节点的next指向新节点即可,记得新节点的next设置NULL,这是遍历链接循环中止的重要条件。

3、在链表中间插入节点

此情况与上述的头插法类似,需要找到插入位置的前一个链表节点,同时注意代码执行顺序不可轻易调换,如图所示:

此上图演示为正确的插入代码执行顺序

此上图演示为错误的插入代码执行顺序,将导致链表断裂;

整体的实现代码如下所示:

struct ListNode* insertNode(struct ListNode* head, struct ListNode* nodeInsert, int position) {
    if (head == NULL)
    {
        return nodeInsert;
    }
    if (position == 1)
    {
        nodeInsert -> next = head;
        head = nodeInsert;
    }
    int len = getLength(head);
    int count = 1;
    if (position > len + 1 || position < 1)
    {
        printf("位置参数越界");
        return head;
    }
    struct ListNode* temp;
    temp = head;
    while (count < position - 1)
    {
        temp = temp -> next;
        count++;
    }
    nodeInsert -> next = temp -> next;
    temp -> next = nodeInsert;
    return head;
}

需要注意的是位置参数越界的情况,插入是可以在当前链表长度加一的位置插入的,而删除则不行。还要注意对于空链表的插入情况,可以报错,也可以插入。


四、删除链表节点

1、删除第一个位置的节点

删除第一个位置的节点,只需要让head指向其下一个节点即可。

2、删除最后一个位置的节点

找到最后一个位置节点的前一个节点,让其next赋值为NULL即可。

3、删除中间位置的节点

找到需要删除位置的前一个节点,让这前一个节点的next指向删除节点的后一个节点即可,如图所示:

整体实现代码如下:

struct ListNode* deleteNode(struct ListNode*head, int position) {
    if (head == NULL)
    {
        return head;
    }

    int len = getLength(head);
    if (position > len || position < 1)
    {
        printf("位置参数错误");
        return head;
    }
    
    if (position == 1)
    {
        struct ListNode* temp = head;
        head = head -> next;
        free(temp);
        return head;
    }

    int count = 1;
    struct ListNode* cur = head;
    while (count < position - 1)
    {
        cur = cur -> next;
        count++;
    }
    struct ListNode* pNode = cur -> next;
    cur -> next = pNode -> next;
    free(pNode);
    return head;

}

需要注意的依然是位置参数错误的判断,此处的边界条件为链表长度,而非插入时的链表加一。还需要注意利用free()系统调用函数,释放内存,C++没有垃圾回收机制,需要手动释放,即要考虑到用新的指针代替完成free()操作。

至此,链表入门从创建到增删改查,结束。

学号:15      昵称:秋容何暮

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值