注:此博客不再更新,所有最新文章将发表在个人独立博客limengting.site。分享技术,记录生活,欢迎大家关注
删除链表中的节点
题目描述:
在O(1)时间内删除链表节点。给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。
测试用例:
1、功能测试:重复的节点位于链表的头部 / 中间 / 尾部;链表中没有重复的节点
2、特殊输入测试:链表头结点为null,链表中所有节点都是重复的
思路:
b图常规遍历复杂度O(n),c图覆盖方法复杂度O(1):我们要删除的节点i,先把i的下一个节点j的内容复制到i,然后把i的指针指向节点j的下一个节点。此时再删除节点j,其效果刚好是把节点i给删除了(图c)
要注意的地方:
(1)如果要删除的节点位于链表的尾部,那么它就没有下一个节点,只能从链表的头节点开始,顺序遍历得到该节点的前序节点,并完成删除操作。
(2)如果链表中只有一个节点,而我们又要删除链表的头节点(也是尾节点),此时我们在删除节点之后,链表的头节点指向NULL。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
// 有一个bug不知道怎么解决:当链表中只有一个节点且要删除这个节点时,head = null没有效果
public class Solution {
public static void deleteNode(ListNode head, ListNode toBeDeleted) {
if (head == null || toBeDeleted == null) return;
if (head.next == null && head == toBeDeleted) {
head = null; // 不生效???
return;
}
if (toBeDeleted.next == null) {
ListNode pNode = head;
while (pNode.next.next != null) {
pNode = pNode.next;
}
pNode.next = null;
return;
}
toBeDeleted.val = toBeDeleted.next.val;
toBeDeleted.next = toBeDeleted.next.next;
}
public static void printLinkedList(ListNode head) {
if (head == null) return;
ListNode pNode = head;
while (pNode.next != null) {
System.out.print(pNode.val + "->");
pNode = pNode.next;
}
System.out.println(pNode.val);
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
//ListNode node1 = new ListNode(2);
//ListNode node2 = new ListNode(3);
//head.next = node1;
//node1.next = node2;
System.out.println("删除节点之前:");
printLinkedList(head);
deleteNode(head, head);
System.out.println("删除节点之后:");
printLinkedList(head);
}
}
结合下面说的值传递和引用传递的问题,可以解决上面的bug,因为值传递head之后为head赋值了,使得head不再指向原来的链表,使得操作失效。
深刻理解到底层原理的重要性!
修改之后:
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
public class Solution {
public static ListNode deleteNode(ListNode head, ListNode toBeDeleted) {
if (head == null || toBeDeleted == null) return null;
if (head.next == null && head == toBeDeleted) {
head = null;
return head;
}
if (toBeDeleted.next == null) {
ListNode pNode = head;
while (pNode.next.next != null) {
pNode = pNode.next;
}
pNode.next = null;
return head;
}
toBeDeleted.val = toBeDeleted.next.val;
toBeDeleted.next = toBeDeleted.next.next;
return head;
}
public static void printLinkedList(ListNode head) {
if (head == null) return;
ListNode pNode = head;
while (pNode.next != null) {
System.out.print(pNode.val + "->");
pNode = pNode.next;
}
System.out.println(pNode.val);
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
//ListNode node1 = new ListNode(2);
//ListNode node2 = new ListNode(3);
//head.next = node1;
//node1.next = node2;
System.out.println("删除节点之前:");
printLinkedList(head);
//deleteNode(head, head);
System.out.println("删除节点之后:");
printLinkedList(deleteNode(head, head));
}
}
删除链表中重复的节点
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路:
设置一个标志位needDelete,如果需要删除我,则找到所有相等的值都删除,将pre指向第一个不重复的值pos;如果不需要删除我,遍历下一个。
时间复杂度O(n),空间复杂度O(1)
package sword_to_offer_linkedlist;
public class DeleteDuplicateNodeInSortedLinkedList {
public static class Node {
int value;
Node next = null;
Node(int value) {
this.value = value;
}
}
public static Node deleteDuplicateNode(Node head) {
if (head == null) return null;
Node pre = null; // 当前节点的前一个节点
Node cur = head; // 当前节点
while (cur != null) {
Node pos = cur.next;
boolean needDelete = false;
// 如果我和后一个节点的值相等,则将我标志为需要删除
if (pos != null && cur.value == pos.value) {
needDelete = true;
}
if (!needDelete) { // 如果不需要删除我
pre = cur;
cur = cur.next;
} else { // 如果需要删除我,则找到所有相等的值都删除,将pre指向第一个不重复的值pos
int dupValue = cur.value;
while (cur != null && cur.value == dupValue) {
cur = cur.next;
pos = cur;
} // 跳出循环的条件是找到了第一个cur后面不重复的值pos,或者直到找到最后一个值都没找到不重复的值
if (pre == null) { // 如果删除的节点是头结点
head = pos; // 则把pos作为头结点
} else {
pre.next = pos; // 否则将pos接上pre
}
cur = pos; // pos作为下一个cur
}
}
return head;
}
public static void printLinkedList(Node head) {
if (head == null) return;
//System.out.print("Linked List: ");
while (head.next != null) {
System.out.print(head.value + " -> ");
head = head.next;
}
System.out.println(head.value);
}
public static void main(String[] args) {
Node head = new Node(3);
Node node1 = new Node(3);
Node node2 = new Node(3);
Node node3 = new Node(3);
Node node4 = new Node(3);
Node node5 = new Node(3);
Node node6 = new Node(3);
//Node node7 = new Node(4);
head.next = node1;
head.next.next = node2;
head.next.next.next = node3;
head.next.next.next.next = node4;
head.next.next.next.next.next = node5;
head.next.next.next.next.next.next = node6;
//head.next.next.next.next.next.next.next = node7;
System.out.println("删除重复节点前:");
printLinkedList(head);
System.out.println("删除重复节点后:");
printLinkedList(deleteDuplicateNode(head));
}
}
在非递归实现的时候发现一个有趣的问题,当测试1,1,1时输出结果不正确,仔细想想发现是涉及了Java函数调用时是传值还是传引用的问题:Java中所有的都是传值,当传的值的数据类型是对象时,传递的是该对象的引用这个值,可以在调用的函数中用该引用来改变所指的对象中实际的值,但是不能为该对象引用本身赋值,因为赋值操作会改变引用所指向的对象不再为原来的对象,以后进行的改变在新对象上进行,无法达到修改原对象的目的。类似于Final修饰对象时,可以修改该对象的内容,但是不能修改该对象本身。
Java是传值还是传引用