第三天| 第二章 链表part01 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表
链表理论基础
- 建议:了解一下链接基础,以及链表和数组的区别
- 文章链接:https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html
203.移除链表元素
-
题目链接:https://leetcode.cn/problems/remove-linked-list-elements/description/
-
文章讲解:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
-
题目介绍:给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。 -
解法:(因为涉及到是否删除头节点,所以有以下两种解法)
-
第一种:直接删除
-
先判断head是否为空,空的话直接返回head即可。
-
因为头节点的删除与其他中间节点不同,所以需要单独讨论。
- 如果头节点不为空,并且头节点中的值是目标值的话,就将头节点向他的next位置移动,即head = head.next;
-
其他中间节点,需要指定一个指针cur,cur指向头节点,通过cur指针的移动遍历整个链表,因为要判断中间节点的val是否与目标值相等,因此需要保证cur.next != null; 。如果cur.next.val是目标值,就将这个节点删除掉。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sHwzSyay-1691778284104)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20230811235113322.png)]
-
代码如下:
-
class Solution { public ListNode removeElements(ListNode head, int val) { ListNode cur = head; if (head == null) { return head; } while (head != null && head.val == val) { head = head.next; } while (cur.next != null) { if (cur.next.val == val) { cur.next = cur.next.next; } else { cur = cur.next; } } return head; } }
-
-
-
第二种:虚拟头节点:
-
思路:给现有的链表加一个虚拟的头节点,那么原来的链表头节点就成为一个中间节点,可以统一处理了。如果cur.next.val与目标值相等,那么和解法一一样,将cur.next赋值为cur.next.next,否则就让指针向后移一位。
-
代码:
-
class Solution { public ListNode removeElements(ListNode head, int val) { ListNode dummyHead = new ListNode(-1, head); ListNode cur = dummyHead; while (cur.next != null) { if (cur.next.val == val) { cur.next = cur.next.next; } else { cur = cur.next; } } return dummyHead.next; } }
-
注意:
- 最终返回的一定是**dummyHead.next**,不管之前的头节点是否存在,虚拟节点之后的第一个节点就是新链表的头节点
-
-
-
707.设计链表
-
题目链接:https://leetcode.cn/problems/design-linked-list/
-
文章讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
-
题目介绍:
- 你可以选择使用单链表或者双链表,设计并实现自己的链表。
- 单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
- 如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
- 实现 MyLinkedList 类:
- MyLinkedList() 初始化 MyLinkedList 对象。
- int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
- void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
- void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
- void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
- void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
-
代码:
-
//单链表 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 MyLinkedList { //size存储链表元素的个数 int size; //虚拟头节点 ListNode dummyHead; //头节点 ListNode head = null; //初始化链表 public MyLinkedList() { size = 0; dummyHead = new ListNode(-1, head); } // 获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点 public int get(int index) { // 获取一个遍历指针 ListNode cur = dummyHead; // 如果index非法,返回-1 if (index >= 0 && index <= size - 1) { //从虚拟节点开始寻找,要找到第index的位置,所以是<= for (int i = 0; i <= index; i++) { cur = cur.next; } return cur.val; } else { return -1; } } // 在链表最前面插入一个节点,等价于在第0个元素前添加 public void addAtHead(int val) { addAtIndex(0, val); } // 在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加 public void addAtTail(int val) { addAtIndex(size, val); } // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。 // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点 // 如果 index 大于链表的长度,则返回空 // 如果 index 小于0,把index赋值为0,在链表头部插入 public void addAtIndex(int index, int val) { if (index > size) { return; } if (index < 0) { index = 0; } // 从虚拟节点开始寻找,要找到第index的位置的前一个,所以是< // 从前一个开始插数据 ListNode cur = dummyHead; for (int i = 0; i < index; i++) { cur = cur.next; } ListNode toAdd = new ListNode(val); toAdd.next = cur.next; cur.next = toAdd; size++; } // 删除第index个节点 // 如果 index 小于0或者大于链表长度,直接终止 // 如果 index 等于0, public void deleteAtIndex(int index) { if (index < 0 || index >= size) { return; } // 从虚拟节点开始寻找,要找到第index的位置的前一个,所以是< // 从前一个开始删数据 // 与插入数据保持高度统一,完美!! ListNode cur = dummyHead; for (int i = 0; i < index ; i++) { cur = cur.next; } cur.next = cur.next.next; size--; } }
-
206.反转链表
-
题目链接:https://leetcode.cn/problems/reverse-linked-list/
-
文章讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html
-
题目介绍:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
-
解法:
-
解法一:双指针
-
思路:
- 先提供一个空指针pre和一个指向head的指针cur,如图所示。循环终止的条件是cur找到了原顺序的最后一个null值,在一次循环的过程中完成的操作是:
- 首先,把**原来顺序的链路径cur.next**保存在一个temp中(我为了方便理解,给它起名为链路径,实际没有这种叫法)(temp = cur.next;)
- 其次,让cur.next指向pre(cur.next = pre;)
- 然后,移动pre到cur现在的位置(pre = cur;)
- 最后,把cur移动原来顺序的下一个位置(cur = temp;)
- 返回值是新链表的头节点,即pre;
- 先提供一个空指针pre和一个指向head的指针cur,如图所示。循环终止的条件是cur找到了原顺序的最后一个null值,在一次循环的过程中完成的操作是:
-
代码:
-
class Solution { public ListNode reverseList(ListNode head) { ListNode cur = head; ListNode pre = null; ListNode temp = null; while (cur != null) { temp = cur.next; cur.next = pre; pre = cur; cur = temp; } return pre; } }
-
-
-
解法二:递归
-
思路:递归的思路很简单,就是把最后两步的赋值通过递归函数实现。
-
代码:
-
class Solution { public ListNode reverseList(ListNode head) { // 初次赋值: // ListNode cur = head; // ListNode pre = null; return reverse(head, null); } public ListNode reverse(ListNode cur, ListNode pre) { if (cur == null) { return pre; } ListNode temp = null; temp = cur.next; cur.next = pre; return reverse(temp, cur); } }
-
-
-