203. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
思路
思路很简单,就是遍历整个链表,当发现目标值的时候将其移除出链表。 为了达到这个目的,我们需要
- 一个前驱节点,他的的作用是当发现目标结点的时候,这个节点指向的的是目标结点的前一个结点;
- 一个当前节点,我们遍历链表的节点
- 一个用来保存头节点的节点,如果我们直接用头节点遍历链表,那么最后我们会失去头节点的索引
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
ListNode dummy = new ListNode(-1, head);
ListNode cur = head;
ListNode pre = dummy;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
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 的节点
思路
本题就是个正常的链表设计,这五个接口都是常用的接口。
我用的思路是使用虚拟头节点;当初始化MyLinkedList list = new MyLinkedList()时,内部所存的节点实际上时一个前驱节点dummyHead,此节点的next节点才是链表的第一个节点。
特别注意:
命名一定要有一致性,不然写代码的时候容易弄混淆概念导致出bug!!!!! 例如:
dummyHead是虚拟头节点,dummyHead.next才是链表的第一个节点cur是当前节点,也就是我们当前所处的链表位置。pre是当前节点的前一个节点,删除节点时我们需要的这个节点
剩下的注意事项都写在代码注释里了
class Node {
int val;
Node next;
Node () {};
Node(int val) {
this.val = val;
this.next = null;
}
Node(int val, Node next) {
this.val = val;
this.next = next;
}
}
class MyLinkedList {
Node dummyHead;
int size;
public MyLinkedList() {
// 虚拟头节点
dummyHead = new Node(0);
size = 0;
}
public int get(int index) {
// 因为 index 是从0开始,所以链表最后一个节点的下标是 size-1
if (index < 0 || index >= size) {
return -1;
}
Node cur = dummyHead;
// 这里要特别注意,因为使用的是虚拟头节点,所以是 i <= index
// i = 0 的时候是虚拟头节点
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
if ( index < 0) {
index = 0;
}
size++;
Node pre = dummyHead;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next = new Node(val, pre.next);
}
public void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
size--;
Node pre = dummyHead;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next = pre.next.next;
}
}
206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
思路
通过改变next节点的指向来实现链表反转。
我们需要三个指针来协助我们完成翻转:
pre指针初始化为nullcur指针初始化为头节点temp指针为cur.next
当 cur != null 我们就进行如下操作:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
当cur = null时,我们就完成了整个链表的翻转,可以自己好好体会一下,或者在草稿纸上画图。
这时节点pre就是我们新链表的头节点。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)