【Day2】代码随想录二刷之Chapter2——链表part1

本文详细介绍了链表的理论基础,包括单链表、双链表和循环链表,以及链表的存储方式和代码实现。随后探讨了LeetCode中的链表题目,如移除元素、设计链表、反转链表和两两交换节点,展示了链表操作的实践技巧。
摘要由CSDN通过智能技术生成

今天晚上刷的是链表的题目,加油!!!

今日任务:

  • 链表的理论基础
  • 移除链表元素
  • 设计链表(较为综合)
  • 翻转链表
  • 两两交换链表中的节点

一、链表的理论基础

1.1 什么是链表?

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,即数据域和指针域(存放指向一个节点的指针),最后一个节点的指针域指向null(空指针)。链表的入口节点称为链表的头节点也就是head。
在这里插入图片描述

1.2 链表的类型

1.2.1 单链表

上面说的就是单链表的问题。

1.2.2 双链表

双链表中有两个指针域(即prev 和 next),一个指向下一个节点,一个指向上一个节点。双链表既可以向前查询,也可以向后查询。
在这里插入图片描述

1.2.3 循环链表

循环链表,顾名思义,就是链表首尾相连。循环链表可以解决 约瑟夫环的问题。
在这里插入图片描述

1.3 链表的存储方式

链表在内存中不是连续分布的,它是通过指针域的指针链接在内存中的各个节点。它在内存中不是连续分布的,而是散乱分布在内存中的某个地址上,分配机制取决于操作系统的内存管理。

1.4 代码中如何实现链表?

1.4.1 链表的定义

单链表的定义方式如下:


struct ListNode{
	int val;       // 节点上存储的元素
	ListNode *next;   // 指向下一个函数的指针
	ListNode(int x): val(x), next(NULL){}   // 节点的构造函数
}

1.4.2 链表和数组的对比

  • 数组的查询很快,但是删除或者插入一个元素很慢;
  • 链表的查询较慢,但是删除或者插入一个元素容易。
    在这里插入图片描述
  • 数组在定义的时候,长度就是固定的,如果想要改动数组的长度,那么就需要重新定义一个新的数组。
  • 链表的长度可以是不固定的,可以动态地增删,适合数据量不固定地,频繁增删,较少查询地场景。

对于解决链表的题目,我们都是采用虚拟头节点的方式来实现,因为这样可以完成删除头节点和其他节点的代码的统一。

二、203.移除链表元素

Leetcode题目:【203.移除链表元素】

在这里插入图片描述

这个题目是链表中最简单的题(考察对链表的操作),但是需要注意下面几点:
(1)节点的结构体定义如何去写(模板一定要记得牢牢的);
(2)虚拟头节点的初始化操作如何实现;
(3)如何将节点通过delete删除;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyNode = new ListNode(0);
        dummyNode -> next = head;
        ListNode* cur = dummyNode;
        while(cur->next!=NULL){
            if(cur -> next -> val == val){
                ListNode* temp = cur -> next;
                cur -> next = cur -> next -> next;
                delete temp;
            }else{
                cur = cur -> next;
            }
        }
        head = dummyNode -> next;
        delete dummyNode;
        return head;
        
    }
};

三、707.设计链表(最综合的一道题目)

Leetcode题目:【707.设计链表】

在这里插入图片描述

通过这个题目,我们其实可以对链表的操作有比较深入的了解,比较难的点在于:
(1)如何去定义节点的结构体,最后的结构体定义是有“;”的
(2)指针随索引偏移的时候,到底偏移多少单位(跟题目中要求索引是从0开始还是1开始有关系),这个需要自己尝试;
(3)全局变量还定义个一个size的问题,用于统计偏移量;
(4)在调试的时候,每操作一步需要最好打印完整个链表中的数据,以方便调试。

class MyLinkedList {
public:
    struct ListNode{
        int val;
        ListNode *next;
        ListNode(int x): val(x), next(NULL){}
    };
    // 构造函数初始化
    MyLinkedList(){
        dummyNode = new ListNode(0);
        cur = dummyNode;
        cout << "new object is already built" << endl;
    }

    // 获取下标节点的值
    int get(int index){
        if(size == 0 || index >= size){
            return -1;
        }else{
            cur = dummyNode;
            for(int i = 0; i<=index; i++){
                cur = cur -> next;
            }
            return cur -> val;
        }
        //printList();
    }

    // 添加头节点
    void addAtHead(int val){
        ListNode *head = new ListNode(val);
        ListNode *temp = dummyNode -> next;
        dummyNode -> next = head;
        head -> next = temp;
        size++;
        //printList();
    }

    // 添加尾节点
    void addAtTail(int val){
        ListNode *tail = new ListNode(val);
        cur = dummyNode;
        while(cur -> next != NULL){
            cur = cur -> next;
        }
        cur -> next = tail;
        size++;
        //printList();
    }

    // 插入中间索引的某一个值
    void addAtIndex(int index, int val){
        if(index>size) return;
        ListNode *midNode = new ListNode(val);
        cur = dummyNode;
        for(int i = 0; i<index; i++){
            cur = cur -> next;
        }
        ListNode *temp = cur -> next;
        cur -> next = midNode;
        cur -> next -> next = temp;
        size++;
        //printList();
    }

    // 删除某一个索引值
    void deleteAtIndex(int index){
        // 这句话没加
        if(index >= size ||index<0){return ;} 
        cur = dummyNode;
        for(int i = 0; i<index; i++){
            cur = cur -> next;
        }
        ListNode *tmp = cur -> next;
        cur->next = cur -> next -> next;
        delete tmp;
        tmp = NULL;
        size--;
        //printList();
    }

    // 遍历链表元素
    void printList(){
        cur = dummyNode;
        while(cur->next != NULL){
            cout << cur -> next -> val << " ";
            cur = cur -> next;
        }
        cout << endl;
    }


private:
    ListNode * dummyNode = NULL;
    ListNode* cur = NULL;
    int size = 0;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

四、206.反转链表

Leetcode题目:【206.反转链表】

在这里插入图片描述

很有意思的一道题,当时就是一位大佬去谷歌面试不会写反转链表,然后就被谷歌拒绝了。

这个方法很巧妙,采用双指针,一个pre,一个cur,此时是没有虚拟头节点的(指针先反转,然后再移动)。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       ListNode *pre = NULL, *cur;
       cur = head;

       while(cur != NULL){
           ListNode* tmp = cur -> next;
           cur -> next = pre;
           pre = cur;
           cur = tmp;
        
       }
       return pre;
    }
};

五、24.两两交换链表中的节点

Leetcode题目:【24. 两两交换链表中的节点】

在这里插入图片描述

这个题目也基本上是链表的操作题,一定要画图,不画图做不出来的,

在这里插入图片描述

需要注意的是:
(1)while的临界条件:因为需要保证cur后面有两个节点,才不会指向空指针,因此需要满足本节点的下一个节点和下下节点均不为空;
(2)cur最终要移到1的位置,后面指向要交换的节点,不要搞混了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
      ListNode* dummyNode = new ListNode(0);
      ListNode* cur = dummyNode;
      dummyNode -> next = head;
      // 因为要交换指针后面的两个节点,所以需要保证下一个节点和下下节点均不为空才可以
      while(cur -> next!= NULL && cur -> next -> next != NULL){
          ListNode *temp = cur -> next;
          ListNode *temp1 = cur -> next -> next -> next;
          cur -> next = cur -> next -> next;    // 步骤一
          cur -> next -> next = temp;           // 步骤二
          cur -> next -> next -> next = temp1;  // 步骤三
          cur = cur -> next -> next;
      }
      return dummyNode -> next;
    }
};
  • 13
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值