代码随想录算法训练营第三天 | 链表 Part01


每日任务

一,滑动窗口理解
二,leetcode 203 移除链表元素
三,leetcode 703 设计链表
四,leetcode 206 反转链表

一、链表基础知识

1、什么是链表

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域,一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。
加粗样式

2、链表的类型

(1)单链表

(2)双链表
在这里插入图片描述
(3)循环链表
在这里插入图片描述

3、链表的存储方式

链表在内存中可不是连续分布的。链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
在这里插入图片描述

4、链表的操作

在这里插入图片描述
在这里插入图片描述

二、leetcode 203 (移除链表元素)

1、原题链接

leetcode 203 移除链表元素
在这里插入图片描述

2、解题思路及代码展示

本题首先第一个考虑的问题是,删除节点时,是将整个链表中所有满足条件的节点都删除掉,也就是使用while进行循环,直到链表中再也没有符合条件的节点。

(1)删除非头节点
在这里插入图片描述
(2)删除头节点
① 直接在原来的链表中进行删除
在这里插入图片描述 移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。
在这里插入图片描述
缺点:我对于“头节点”和“非头节点”的处理逻辑需要分开,要针对头节点的删除单独写一个逻辑,比较繁琐,所有就提出了下面的方法,虚拟头节点。
② 增加一个虚拟头节点
在这里插入图片描述
给链表添加一个虚拟头结点为新的头结点,此时要移除这个旧头结点元素1。这样是不是就可以使用和移除链表其他节点的方式统一
需要注意的是,return 头结点的时候,返回的是 dummyNode->next;, 这才是新的头结点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 设置一个虚拟头节点
        ListNode dummyHead = new ListNode();
        // 将虚拟头节点指向原始链表的头节点
        dummyHead.next = head;
        // 定义一个cur,记录当前节点
        ListNode cur = dummyHead;
        // 执行
        while(cur.next != null){
            if(cur.next.val == val){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        // 注意返回真实的头节点(dummyHead.next)
        return dummyHead.next;
    }
}

三、leetcode 703 (设计链表)

1、原题链接

leetcode 703 设计链表
在这里插入图片描述

2、解题思路及代码展示

直接沿用虚拟头节点的思路,本题要实现五个方法:
(1)获取链表第index个节点的数值
(2)在链表的最前面插入一个节点
(3)在链表的最后面插入一个节点
(4)在链表第index个节点前面插入一个节点
(5)删除链表的第index个节点
直接上代码:

class MyLinkedList {
    //size存储链表元素的个数
    int size;
    //虚拟头结点
    ListNode head;

    //初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }

    //获取第index个节点的数值
    public int get(int index) {
        //如果index非法,返回-1
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode currentNode = head;
        //包含一个虚拟头节点,所以查找第 index+1 个节点
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }

    //在链表最前面插入一个节点
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    //在链表的最后插入一个节点
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        if (index < 0) {
            index = 0;
        }
        size++;
        //找到要插入节点的前驱
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = pred.next;
        pred.next = toAdd;
    }

    //删除第index个节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        size--;
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }
}



四、leetcode 206 (反转链表)

1、原题链接

leetcode 206 反转链表
在这里插入图片描述

2、解题思路及代码展示

(1)双指针法
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {

        // 定义一个cur指向头节点
        ListNode cur = head;

        // 定义一个pre指针指向head前的,为null
        ListNode pre = null;

        // 定义一个temp临时节点,用来存cur的下一个元素,让循环继续下去
        ListNode temp = null;

        while(cur != null){
        	// 具体反转操作
            temp = cur.next;
            cur.next = pre;
			// pre 和 cur 统统向后移动,pre到原来cur的位置,cur走到pre的位置
            pre = cur;
            cur = temp;
        }

        return pre;

    }
}

(2)递归法
结题思路和双指针法是一样的

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {

        // // 定义一个cur指向头节点
        // ListNode cur = head;
        // // 定义一个pre指针指向head前的,为null
        // ListNode pre = null;
        // // 定义一个temp临时节点,用来存cur的下一个元素,让循环继续下去
        // ListNode temp = null;
        // while(cur != null){
        //     temp = cur.next;
        //     cur.next = pre;
        //     pre = cur;
        //     cur = temp;
        // }
        // return pre;

        return reverse(null,head);

    }

    private ListNode reverse(ListNode pre,ListNode cur){
        // 递归终止条件
        if(cur == null){
            return pre;
        }

        // 单层循环逻辑
        ListNode temp = null;
        temp = cur.next;
        cur.next = pre;
        return reverse(cur,temp);
    }

}

总结

提示:这里对文章进行总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值