首页 > 编程语言 >代码随想录算法训练营第三天 | 203.移除链表元素 707.设计链表 206.反转链表

代码随想录算法训练营第三天 | 203.移除链表元素 707.设计链表 206.反转链表

时间:2022-10-14 21:24:56浏览次数:77  
标签:ListNode cur val 随想录 next 链表 移除 节点

链表的数据结构基础

  • 链表结构
    image

链表是一种通过指针串联在一起的线性结构。每一个节点由两钟部分构成,一部分是数据域,一部分是指针域,指针域存放的指针指向另一个节点。链表最后一个节点指向下一个节点的指针域指向null。链表的头节点称为头节点,也可称为head。

  • 链表类型
    • 单链表
      单链表即只有单向链接的列表,表内的节点只存储指向下一个节点的指针域。
    • 双链表
      与单链表相比,双链表内的节点多了一个指针域,用于存储指向上一个节点的指针信息,因此允许双向遍历。
    • 循环链表
      循环链表的最后一个节点,其指向下一个节点的指针域并不为null,而是head,使得链表形成首尾相连的结构。
  • 链表存储方式
    链表在内存中并非连续分布,节点之间通过指针链接。因此,链表内数据的内存地址并不相连,其分布取决于操作系统的内存管理。
  • 链表定义
    以最经典的单链表为例,给出Java中的一种定义方式:
class ListNode {
	int val;
	ListNode next;
	ListNode(){};	// 无参构造方法
	ListNode(int val){	// 设定初始值的有参构造方法
		this.val = val;
	}
	ListNode(int val, ListNode next) {	// 设定初始值和下一个节点的有参构造方法
		this.val = val;
		this.next = next;
	}
}
  • 链表基本操作
    • 删除节点
      结合链表的结构,不难发现对链表内的节点进行操作,实际上就是对节点之间的指针进行操作。因此,要删除一个节点,只需要让该节点的上一个节点的指针指向下一个节点即可。在Java中,因为垃圾回收机制的存在,不需要对这个不存在于链表中但仍然存在于内存中的节点进行额外的删除操作。
    • 增加节点
      仍然是对节点的指针进行操作。设存在节点A,C,需要将节点B插入节点A和C之间,则需要让B的指针指向C,再将A的指针指向B。务必注意两步操作不可颠倒,否则节点C及其之后的节点都将与链表断开链接。
  • 链表性能分析及与数组的性能对比
    • 增加&删除
      链表的增加与删除操作只需要修改指针指向即可,因此链表的增删操作时间复杂度为O(1)。数组的增加与删除操作需要对增加与删除的位置之后的元素进行整体的移动操作,因此数组的增删操作时间复杂度为O(n)
    • 查询
      查询链表元素需要对链表进行遍历操作,因此链表的查询操作时间复杂度为O(n)。而数组可以通过下标索引的方式快捷访问到数组内元素而无需遍历数组,因此数组的查询操作时间复杂度为O(1)

链表长度可变,增删简单,但查询性能较弱,适用于数据长度不定,需要频繁增删而少查询的环境。
数组长度不变,查询简单,但增删性能较弱,适用于数据长度固定,需要频繁查询而少增删的环境。

203.移除链表元素

本题以链表的删除操作为基础。首先,需要遍历整个链表,寻找是否存在值与给定的目标值相等的节点。当发现这样的节点时,进行删除操作。
对非头节点的节点进行删除操作的逻辑是一致的,但是如果是对头节点进行删除操作呢?此时并不存在一个“头节点之前的节点”需要指向头节点之后的节点。因此,只需要将头节点设置为头节点的下一个节点即可。
可以发现,删除头节点和删除非头节点的逻辑不一致。为了处理这种不一致,存在两种解决方案。一种是单独写一段针对删除头节点的代码,一种是设置虚拟头节点,即“头节点的头节点”。其中,设置虚拟头节点的方式可以应用在很多种其他的链表题目内,因此今天先针对使用虚拟头节点的方法进行了学习。

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode();
        dummy.next = head;

        ListNode pre = dummy;
        ListNode cur = dummy.next;

        while (cur != null) {
            if (cur.val == val) {
                pre.next = cur.next;	// 此时pre需要保持在原位不动,确保pre与cur仍然相差一位。
            } else {
                pre = pre.next;
            }
            cur = cur.next;
        }
        return dummy.next;
    }
}

707.设计链表

一开始甚至没有注意到题目并没有像之前一样给出链表的定义……因此这道题目需要自行定义一个ListNode类来实现基本的单链表。同时,为了实现题目要求的方法,MyLinkedList类也需要添加头节点head(构造方法)和链表长度sizegetaddAtIndexdeleteAtIndex)两个属性。实现题目方法的过程就是对链表增删改查方法实现的复习。

class MyLinkedList {
    int size;
    ListNode dummy;


    public MyLinkedList() {
        size = 0;
        dummy = new ListNode();
    }

    public int get(int index) {
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode cur = dummy.next;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.val;
    }

    public void addAtHead(int val) {
        ListNode insert = new ListNode(val);
        insert.next = dummy.next;
        dummy.next = insert;
        System.out.print(dummy.next.val + " ");

        size++;
        System.out.println(size);
    }

    public void addAtTail(int val) {
        ListNode insert = new ListNode(val);
        ListNode cur = dummy;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = insert;
        size++;
    }

    public void addAtIndex(int index, int val) {
        if (index == size) {
            addAtTail(val);
        } else if (index <= 0) {
            addAtHead(val);
        } else if (index < size){
            ListNode insert = new ListNode(val);
            ListNode cur = dummy;
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            insert.next = cur.next;
            cur.next = insert;
            size++;
        }

    }

    public void deleteAtIndex(int index) {
        if (index >= 0 && index < size) {
            ListNode cur = dummy;
            System.out.print(1);
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            if (cur.next != null) {
                cur.next = cur.next.next;
            } else {
                cur.next = null;
            }
            size--;
        }
    }
}

class ListNode {
    int val;
    ListNode next;
    ListNode(){};
    ListNode (int val) {
        this.val = val;
    }
}

206.反转链表

本题最重要的地方在于理解链表反转的步骤。总体而言,反转单个节点的步骤如下:

  • Step 1
    image
    很容易想到至少需要两个指针,cur指向需要反转的节点,pre指向需要反转的节点即将指向的节点。但是,如果直接反转,那么cur指向的节点及其之后的节点都会与链表断开链接。
  • Step 2
    image
    因此,需要设置一个tmp指针,用于保存cur指向的节点之后的节点。
  • Step 3
    image
    保证了链表节点不会因反转操作而丢失之后,进行反转即可。
  • Step 4
    image
    完成反转操作之后,所有指针都向后移动一位,为下一次反转操作做准备。
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode tmp;

        while (cur != null) {
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }

        return pre;
    }
}

标签:ListNode,cur,val,随想录,next,链表,移除,节点
From: https://www.cnblogs.com/renewable/p/16793049.html

相关文章

  • 206. 反转链表
    206.反转链表给你单链表的头节点head,请你反转链表,并返回反转后的链表。示例1:输入:head=[1,2,3,4,5]输出:[5,4,3,2,1]示例2:输入:head=[1,2]输出:[2,1]示例......
  • Python 如何移除旧的版本特性,如何迎接新的特性?
    2020年4月20日,Python2的最后一个版本2.7.18发布了,这意味着Python2是真正的EOL(endoflife)了,一个时代终于落幕了。Python2.0版本是在2000年发布的,至今正好......
  • 83. 删除排序链表中的重复元素
    题目描述给定一个已排序的链表的头head,删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。示例代码varremoveDuplicates=function(nums){if(n......
  • 代码随想录训练营第三天 |203.移除链表元素, 707.设计链表, 206.反转链表
    第三天是链表,要注意的是可以创建虚拟头节点来进行链表操作。203.移除链表元素classSolution{publicListNoderemoveElements(ListNodehead,intval){......
  • 代码随想录训练营|Day 24|回溯算法,77
    回溯算法理论基础回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如......
  • 代码随想录Day1
     题目(LeetCode704) 给定一个 n 个元素有序的(升序)整型数组 nums和一个目标值 target ,写一个函数搜索 nums 中的target,如果目标值存在返回下标,否则返回-1......
  • 160. 相交链表
    题目描述给你两个单链表的头节点headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回null。题目解释参考代码vargetInters......
  • 141. 环形链表
    题目描述给你一个链表的头节点head,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内......
  • 876. 链表的中间结点
    题目描述给定一个头结点为head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。样例输入示例1:输入:[1,2,3,4,5]输出:此列表中的结点3......
  • 代码随想录 | 进阶二叉树
    二叉树的构造默认如下:/***Definitionforabinarytreenode.*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*......