1 203. 移除链表元素
题目:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
提示:
- 列表中的节点数目在范围
[0, 104]
内 1 <= Node.val <= 50
0 <= val <= 50
思路:删除链表中值为val的节点,边遍历链表边删除满足条件的节点。
注意:进行删除操作需要将指针停到满足删除条件的节点前一位,否则删除当前节点指针指向出现问题,因此采用虚拟头结点的方法遍历链表。
另外,该函数需要返回链表新的头结点,需要记录头结点信息,因此需要保留虚拟头结点指向真实的头结点。
Java实现
/**
* 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 p = new ListNode(0);
p.next = head;
ListNode temp = p;
// 预留一个判断节点,只判断当前节点无法删除
while(temp.next!=null){
if(temp.next.val == val){
temp.next = temp.next.next;
}
else{
temp = temp.next;
}
}
return p.next;
}
}
2 707. 设计链表
题目:你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性: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
的节点。
提示:
0 <= index, val <= 1000
- 请不要使用内置的 LinkedList 库。
- 调用
get
、addAtHead
、addAtTail
、addAtIndex
和deleteAtIndex
的次数不超过2000
。
思路:偷懒了,用单链表实现的。考虑到链表包含增删操作,设置虚拟头结点更加方便。
MyLinkedList()
初始化:定义虚拟头结点,方便后续使用。
int get(int index)
获取链表中下标为 index
的节点的值:首先需要考虑index的合法区间,因为初始化中包含一个虚拟头结点,所以index[0,size-1]。其次index的循环次数,for循环中判停条件应为index<=size。
void addAtIndex(int index, int val)
将一个值为 val
的节点插入到链表中下标为 index
的节点之前:首先判断index的范围,见代码注释。其次添加时要求在下标index前,因此for循环判停条件为i<index即可。addAtHead
、addAtTail
同样。
void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为 index
的节点:参考203。
注意:使用虚拟头结点后要注意index和size的大小关系。
Java实现
// 自定义单链表结点结构
// class ListNode{
// int val;
// ListNode* next;
// ListNode(){}
// ListNode(int val){
// this.val = val;
// }
// }
class MyLinkedList {
int size; // 定义链表长度,添加虚拟头结点后判断链表下标index需要+1
ListNode head; // 虚拟头结点
// 初始化链表
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
// 获取链表中下标为index的值,如无效返回-1
public int get(int index) {
// 判断下标范围是否正确,非法返回-1
// 虚拟头结点也占一个size,index合法区间为[0,size-1]
if (index<0 || index>=size){
return -1;
}
ListNode temp = head; // 从虚拟头结点开始
for(int i=0;i<=index;i++){
temp = temp.next;
}
return temp.val;
}
// 头部插入,插入的特殊用法
public void addAtHead(int val) {
addAtIndex(0,val);
}
// 尾部插入,插入的特殊用法
public void addAtTail(int val) {
addAtIndex(size,val);
}
// 将值为val的节点插入下标为index的节点之前
// index小于0,默认为头部插入;index为0,头部插入;index为size,尾部插入;index大于size,插入失败
public void addAtIndex(int index, int val) {
if(index > size){
return;
}
// index小于0,默认为头部插入
if(index < 0){
index = 0;
}
size++;
ListNode pre = head; // 从虚拟头结点开始
// 找到插入节点处
for(int i= 0;i<index;i++){
pre = pre.next;
}
ListNode add = new ListNode(val);
add.next = pre.next;
pre.next = add;
}
// 若下标有效,则删除下标为index的节点
public void deleteAtIndex(int index) {
// 判断下标是否合法
if(index<0 || index>=size){
return ;
}
size--;
ListNode pre = head;
// 停在要删除节点前一个
for(int i=0;i<index;i++){
pre = pre.next;
}
pre.next = pre.next.next;
}
}
/**
* 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);
*/
3 206. 反转链表
题目:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
提示:
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 5000
思路:建议学习一下指导文档。我这里用的双指针法,相当于原链表和翻转链表每操作依次都有指针记录轨迹,不容易出错。https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE Java实现
/**
* 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) { // 虚拟头结点判断,什么时候有什么时候没有?
ListNode pre = null;
ListNode temp = null;
ListNode cur = head;
while(cur!=null){
temp = cur.next; // 保存下一个节点;
cur.next = pre; // 生成翻转链表
pre = cur;
cur = temp;
}
return pre;
}
}