【链表理论基础 】
1、链表的类型
单链表
双链表
循环链表
2、Java的节点定义方式
- 通常head指向第一个节点,即head.val存的就是第一个节点的值
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; }
}
【203.移除链表元素 】
方法一 添加虚拟节点
1、添加一个虚拟节点res,便于判断和记录返回值(res.next)
2、设定前节点pre和当前节点cur
3、循环遍历链表,根据当前节点的数据域cur.val判断是否需要删除当前节点cur
- 如果cur.val = val,删除cur节点
- 如果cur.val != val,保留cur节点
/**
* 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) {
if (head == null) {return null;}
ListNode res = new ListNode(0, head); // 记录虚拟节点
ListNode pre = res; // 上一个节点
ListNode cur = head; // 当前节点
// 如果当前节点非空,则判断是否需要删除当前节点
while (cur != null){
if (cur.val == val){ // 需要删除当前节点
pre.next = cur.next;
cur = cur.next;
}
else{ // 不需要删除当前节点
pre = cur;
cur = cur.next;
}
}
return res.next; // 不能直接返回head,如果head.val=val,则head节点会被删除
}
}
时间复杂度: O(n)
空间复杂度: O(1)
方法二 不添加虚拟节点
1、确认链表真正的head节点
2、处理真正的空链表
3、循环遍历链表,删除值为val的节点
(注意:处理的顺序不同,可能会导致bug)
/**
* 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) {
// 确定真正的head(head != null不能省略,防止head=head.next赋值为null,再访问null的val属性就报错)
while(head != null && head.val == val){
head = head.next;
}
// 处理空链表(要在确定了真正的head之后再判断,防止只有一个节点head且节点值为val的链表)
if (head == null) {return null;}
// 循环链表,删除值为val的节点
ListNode pre = head;
ListNode cur = head.next;
while (cur != null){
if (cur.val == val){
pre.next = cur.next;
cur = cur.next;
}
else{
pre = cur;
cur = cur.next;
}
}
return head;
}
}
时间复杂度: O(n)
空间复杂度: O(1)
【707.设计链表 】
单链表
关键:确定从虚拟节点prehead开始,寻找索引为index / index-1的节点所需要的次数
class ListNode{
int val;
ListNode next;
public ListNode(){}
public ListNode(int val){this.val = val;}
public ListNode(int val, ListNode next){this.val = val; this.next = next;}
}
class MyLinkedList {
int size;
ListNode prehead;
public MyLinkedList() {
this.size = 0;
this.prehead = new ListNode(0);
}
public int get(int index) {
if (index >= size){
return -1;
}
ListNode cur = prehead;
for(int i = 0; i <= index; i++){
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
// 相当于调用addAtIndex在index=0的位置插入节点
addAtIndex(0, val);
}
public void addAtTail(int val) {
// 相当于调用addAtIndex在index=size的位置插入节点
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) return;
// 找到索引为index-1的节点pretarget
ListNode pretarget = prehead;
for (int i = 0; i < index; i++){
pretarget = pretarget.next;
}
// 在索引为index-1的节点后面,索引为index处,插入节点target
ListNode target = new ListNode(val);
target.next = pretarget.next;
pretarget.next = target;
// 更新链表长度
size++;
return;
}
public void deleteAtIndex(int index) {
if (index >= size) return;
// 找到索引为index-1的节点pretarget
ListNode pretarget = prehead;
for (int i = 0; i < index; i++){
pretarget = pretarget.next;
}
// 删除索引为index的节点
pretarget.next = pretarget.next.next;
// 更新链表长度
size--;
return;
}
}
/**
* 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);
*/
时间复杂度: O(index)
空间复杂度: O(1)
【206.反转链表 】
1、构建虚拟节点,便于描述
2、箭头反转
3、更新上一个节点pre和当前节点cur,再继续遍历
/**
* 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 cur = head;
ListNode temp;
while (cur != null){
// 箭头反转
temp = cur.next; // temp记录当前节点的下一个节点
cur.next = pre; // 令当前节点的下一个节点变为上一个节点
// 继续遍历
pre = cur; // 当前节点 -> 前节点pre
cur = temp; // 当前节点的下一个节点 -> 当前节点cur
}
return pre; // 当链表为空,不进入循环,还是null;当链表只有一个节点,进入1次循环后的pre就是那个节点
}
}
时间复杂度: O(n)
空间复杂度: O(1)