//单链表的定义
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; }
}
https://leetcode-cn.com/problems/remove-linked-list-elements/
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [7,7,7,7], val = 7
输出:[]
方法一:直接法—迭代
删除头结点时另做考虑
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null && head.val==val){
head=head.next; //若满足条件,则移动头节点的位置
}
if(head==null) return head;
ListNode first = head; //保存头节点的位置
while(head.next!=null){
if(head.next.val==val){
head.next=head.next.next;
}else{
head=head.next;
}
}
return first;
}
}
//时间复杂度: O(n)
//空间复杂度: O(1)
方法二:虚拟头节点(常用)
设置一个虚拟头结点,不需要另外考虑头节点,这样原链表的所有节点就都可以按照统一的方式进行移除了
class Solution {
public ListNode removeElements(ListNode head, int val) {
//虚拟结点,指向下一个结点是head头节点
ListNode dummy = new ListNode(-1,head);
ListNode prev=dummy; //借用其他结点进行删除,保证dummy.next指向头节点
while(prev.next!=null){
if(prev.next.val==val){
prev.next=prev.next.next;
}else{
prev=prev.next;
}
}
return dummy.next;
}
}
//时间复杂度: O(n)
//空间复杂度: O(1)
方法三:递归
递归的终止条件是head
为空,此时直接返回head
。
当head
不为空时,递归地进行删除操作,然后判断head
的节点值是否等于val
并决定是否要删除head
。
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head; //退出递归
}
head.next = removeElements(head.next, val); //下一个结点交给递归处理
return head.val == val ? head.next : head; //保证当前节点满足条件
}
}
//时间复杂度: O(n)
//空间复杂度: O(n) 其中n是链表的长度。空间复杂度主要取决于递归调用栈,最多不会超过n层
https://leetcode-cn.com/problems/design-linked-list/
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val
和 next
。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev
以指示链表中的上一个节点。假设链表中的所有节点都是 0-index
的。
在链表类中实现这些功能:
方法一:单链表法
先设计 addAtIndex,因为伪头的关系 addAtHead 和 addAtTail 可以使用 addAtIndex 来完成。
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
class MyLinkedList {
int size; //链表大小
ListNode head; //虚拟头节点
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
/*+++++++++++++++++方法实现++++++++++++++++++*/
//对应下标插入新节点
public void addAtIndex(int index, int val) {
if (index > size) return;
if (index < 0) index = 0;
ListNode node = new ListNode(val);
ListNode prev = head;
while(index-- > 0) prev = prev.next;
node.next = prev.next;
prev.next = node;
size++;
}
//头部插入新节点
public void addAtHead(int val) {
addAtIndex(0,val);
}
//尾部插入新节点
public void addAtTail(int val) {
addAtIndex(size,val);
}
//删除对应下标的节点
public void deleteAtIndex(int index) {
if(index>=size||index<0) return;
ListNode prev=head;
while(index-- > 0) prev = prev.next;
prev.next = prev.next.next; //删除节点prev.next
size--;
}
//获取对应下标的节点值
public int get(int index) {
if(index>=size||index<0) return-1;
ListNode prev=head;
while(index-- >= 0) prev = prev.next;
return prev.val;
}
}
https://leetcode-cn.com/problems/reverse-linked-list/
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
题解:
假设存在链表 1→2→3→∅,我们想要把它改成 ∅←1←2←3。
方法一:迭代
在遍历列表时,将当前节点的 next
指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针tmp
来存储下一个节点。不要忘记在最后返回新的头引用!
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode curr=head;
while(curr!=null){
ListNode tmp=curr.next; // 保存下一个节点
curr.next=pre; //指向反向
pre=curr;
curr=tmp;
}
return pre; //返回新的头节点
}
}
//时间复杂度: O(n)
//空间复杂度: O(1)
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode p = reverseList(head.next);
head.next.next = head; //指向反转 head.next-->head
head.next = null; // 当前head-->null
return p;
}
}
//时间复杂度: O(n)
//空间复杂度: O(n)
https://leetcode-cn.com/problems/swap-nodes-in-pairs/
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入:head = [1,2,3,4]
输出:[2,1,4,3]
题解:
方法一:迭代法
具体而言,交换之前的节点关系是temp -> node1 -> node2
,交换之后的节点关系要变成 temp -> node2 -> node1
,因此需要进行如下操作
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0); //虚拟头节点
dummy.next = head;
ListNode prev = dummy;
while(prev.next!=null && prev.next.next!=null){
ListNode temp = head.next.next; // 缓存 next
prev.next = head.next; // 将 prev 的 next 改为 head 的 next
head.next.next = head; // 将 head.next(prev.next) 的next,指向 head
head.next = temp; // 将head 的 next 接上缓存的temp
prev = head; // 步进1位
head = head.next; // 步进1位
}
return dummy.next;
}
}
//时间复杂度: O(n)
//空间复杂度: O(1)
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。(1 <= n <= sz)
你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
思路:双指针
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
这里我们待fast指向null的时候,slow指向的是要删除的前驱结点,直接slow.next=slow.next.next
删除结点。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0,head); //虚拟头节点
ListNode fast = head; //快指针
ListNode slow = dummy; //慢指针
while(n-->0){
fast=fast.next; //快指针先移动n步
}
while(fast!=null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next; //删除结点
return dummy.next;
}
}