JZ6 从尾到头打印链表

image.png

我的解法

image.png

  1. import java.util.*;
  2. /**
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next = null;
  6. *
  7. * ListNode(int val) {
  8. * this.val = val;
  9. * }
  10. * }
  11. *
  12. */
  13. import java.util.ArrayList;
  14. public class Solution {
  15. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  16. ArrayList<Integer> list = new ArrayList();
  17. while( listNode!=null){
  18. list.add(listNode.val);
  19. listNode = listNode.next;
  20. }
  21. // 感觉此时的 ListNode.next != null 也不是很有必要;
  22. while(listNode !=null && listNode.next!=null){
  23. list.add(listNode.val);
  24. listNode = listNode.next;
  25. }
  26. ArrayList<Integer> listReverse = new ArrayList();
  27. while(list.listIterator().hasPrevious()){
  28. listReverse.add(list.listIterator().previous());
  29. }
  30. // listReverse.toArray();
  31. return listReverse;
  32. }
  33. }

难点: 自己 不知道 util 下有哪些工具类, 工具类的方法名字也不记得 总体思路清晰:
1.根据 head 去遍历所有,放入一个容器里,

  1. 遍历使用.next更新 head, 往容器里存入的是什么,是 节点类实例?还是节点类的值域;
  2. 倒序遍历使用了 ListIterator及 previous ,根据 2 确定
  3. 是否需要转换为java数组;

困惑点:

  1. 题目写的输入 是 {1,2,3}, 这肯定不是一个 head, 而是一个容器对象实例;
  2. 题目写的输出是[1,2,3] , 那么提示的 printListFromTailToHead 的返回值为什么不是 数组呀? 而是一个容器!
  3. 还是说, 输入输出是测试用例?我不太懂

网友答案

  1. // 更加容易理解一些;
  2. import java.util.*;
  3. /**
  4. * public class ListNode {
  5. * int val;
  6. * ListNode next = null;
  7. *
  8. * ListNode(int val) {
  9. * this.val = val;
  10. * }
  11. * }
  12. *
  13. */
  14. import java.util.ArrayList;
  15. import java.util.Stack;
  16. public class Solution {
  17. ArrayList<Integer> list = new ArrayList();
  18. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  19. if(listNode != null) {
  20. printListFromTailToHead(listNode.next);
  21. list.add(listNode.val); // add最后一个, 倒数第二个;...正数第一个;
  22. }
  23. return list;
  24. }
  25. }
  1. import java.util.Collections;
  2. import java.util.ArrayList;
  3. public class Solution {
  4. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  5. ArrayList<Integer> ans = new ArrayList<>();
  6. printListFromTailToHeadCore(ans,listNode);
  7. return ans;
  8. }
  9. private void printListFromTailToHeadCore(ArrayList<Integer> ans, ListNode listNode) {
  10. if (listNode == null){
  11. return;
  12. }
  13. printListFromTailToHeadCore(ans,listNode.next);
  14. ans.add(listNode.val);
  15. }
  16. }
  1. /**
  2. *1.递归
  3. *2.借用栈
  4. *3.头插法,每个元素都往前插入
  5. add(index,value)用法 小心些;
  6. https://blog.csdn.net/qq_27093465/article/details/55211722
  7. **/
  8. public class Solution {
  9. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  10. ArrayList<Integer> array = new ArrayList<Integer>();
  11. while(listNode != null){
  12. array.add(0,listNode.val);
  13. listNode = listNode.next;
  14. }
  15. return array;
  16. }
  17. }
  18. // ?
  19. // ArrayList<Integer> list = new ArrayList<>();
  20. // while(listNode != null){
  21. // list.add(listNode.val);
  22. // listNode = listNode.next;
  23. // }
  24. // int i = 0;
  25. // int j = list.size() - 1;
  26. // while(i < j){
  27. // int tmp = list.get(i);
  28. // list.set(i,list.get(j));
  29. // list.set(j,tmp);
  30. // i++;
  31. // j--;
  32. // }
  33. // return list;
  34. // 借用栈
  35. // ArrayList<Integer> list = new ArrayList<>();
  36. // Stack <Integer> stack = new Stack<>();
  37. // if(listNode == null){
  38. // return list;
  39. // }
  40. // while(listNode != null){
  41. // stack.push(listNode.val);
  42. // listNode = listNode.next;
  43. // }
  44. // while(!stack.empty()){
  45. // list.add(stack.pop());
  46. // }
  47. // return list;
  1. /**
  2. * public class ListNode {
  3. * int val;
  4. * ListNode next = null;
  5. *
  6. * ListNode(int val) {
  7. * this.val = val;
  8. * }
  9. * }
  10. *
  11. */
  12. import java.util.*;
  13. public class Solution {
  14. /*
  15. // 方法一:先进后出,我们可以想到栈
  16. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  17. //用来存储链表中节点的值。
  18. Stack<Integer> reverse = new Stack<>();
  19. while(listNode != null){
  20. reverse.push(listNode.val);
  21. listNode = listNode.next;
  22. }
  23. //创建的题目要求的数据类型来存储反向的节点值。
  24. ArrayList<Integer> result = new ArrayList<>();
  25. while(!reverse.isEmpty()){
  26. //将值从栈中弹出,并添加到ArrayList中
  27. result.add(reverse.pop());
  28. }
  29. return result;
  30. }
  31. */
  32. /*
  33. //方法一:递归
  34. ArrayList<Integer> arr = new ArrayList();
  35. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  36. if(listNode != null){
  37. printListFromTailToHead(listNode.next);
  38. arr.add(listNode.val);
  39. }
  40. return arr;
  41. }
  42. */
  43. /*
  44. // 方法二:使用ArrayList 中有个方法是 add(index,value)
  45. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  46. ArrayList<Integer> list = new ArrayList<>();
  47. ListNode tmp = listNode;
  48. while(tmp!=null){
  49. list.add(0,tmp.val);
  50. tmp = tmp.next;
  51. }
  52. return list;
  53. }
  54. */
  55. // 链表反转法
  56. // 时间复杂度: O(n)
  57. // 空间复杂度: O(n)
  58. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  59. ArrayList<Integer> vals = new ArrayList<>();
  60. ListNode pre = null, cur = listNode, next;
  61. // 反转链表
  62. while (cur != null) {
  63. next = cur.next;
  64. cur.next = pre;
  65. pre = cur;
  66. cur = next;
  67. }
  68. // 添加到vals中
  69. cur = pre;
  70. while (cur != null) {
  71. vals.add(cur.val);
  72. System.out.println(cur.val);
  73. cur = cur.next;
  74. }
  75. return vals;
  76. }
  77. }
  1. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  2. ListNode l=listNode;
  3. ArrayList<Integer> arrayList=new ArrayList<>();
  4. if(listNode==null) return arrayList;
  5. Stack<Integer> stack=new Stack<>();
  6. // 先进入 在后面;
  7. while(l.next!=null){
  8. stack.push(l.val);
  9. l=l.next;
  10. }
  11. stack.push(l.val);
  12. while (!stack.empty()){
  13. arrayList.add(stack.pop());
  14. }
  15. return arrayList;
  16. }
  17. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  18. // 建立一个list
  19. ArrayList<Integer> list = new ArrayList();
  20. Stack<Integer> stack = new Stack();
  21. // 先入栈, 是元素的值
  22. while(listNode != null){
  23. stack.push(listNode.val);
  24. listNode = listNode.next;
  25. }
  26. // 出栈并加入list
  27. while(!stack.isEmpty()){
  28. list.add(stack.pop());
  29. }
  30. return list;
  31. }

JZ24 反转链表
image.png

我的答案

尽管描述的测试用例让人误会,还是根据代码区提示即可
image.png
image.png

  1. import java.util.*;
  2. /*
  3. public class ListNode {
  4. int val;
  5. ListNode next = null;
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. }*/
  10. public class Solution {
  11. public ListNode ReverseList(ListNode head) {
  12. if(head == null) return null;
  13. if (head.next == null) return head;
  14. ListNode cur = head;
  15. ListNode pre = null;
  16. //
  17. while(cur!=null){
  18. ListNode temp = cur.next;
  19. cur.next = pre;// 在更改引用之前, 存储后一个节点
  20. pre = cur ; // 事先存储当前要调转节点的前一个节点;
  21. cur = temp;
  22. }
  23. return pre;
  24. }
  25. }

其他

image.png
image.png

  1. import java.util.*;
  2. /*
  3. public class ListNode {
  4. int val;
  5. ListNode next = null;
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. }*/
  10. public class Solution {
  11. public ListNode ReverseList(ListNode head) {
  12. Stack<ListNode> stack = new Stack<>();
  13. // 无需使用if进行 head == null判断;
  14. while(head !=null){
  15. stack.push(head);
  16. head = head.next;
  17. }
  18. // 先推,后查; 做边界条件控制
  19. if(stack.isEmpty()){
  20. return null ;
  21. }
  22. ListNode node = stack.pop();
  23. ListNode reverse = node;// 栈顶就是新链表的head;
  24. // 弹出一个后,有可能为空
  25. while(!stack.isEmpty()){
  26. ListNode tempNode = stack.pop();
  27. node.next = tempNode;
  28. // 写node.next更加符合 都是链表的序列元素;
  29. node = node.next // 我这里写的是tempNode;
  30. }
  31. node.next = null;
  32. return reverse;
  33. }
  34. }
  1. public ListNode ReverseList(ListNode head) {
  2. if(head != null) return head;
  3. // 新链表头节点
  4. ListNode newHead = null ;
  5. while(head != null){
  6. // 原链表 当前的下一个节点
  7. ListNode tempNode = head.next;
  8. // 这也是双向链表的实现方式;
  9. // 就是把 新的链表(头为newHead) 挂载到当前(head)的next(后面)即可;
  10. head.next = newHead;
  11. newHead = head;
  12. head = tempNode;
  13. }
  14. return newHead;
  15. }

:::info public ListNode reverseList(参数0) {
if (终止条件)
return;

  1. 逻辑处理(可能有,也可能没有,具体问题具体分析)
  2. //递归调用<br /> ListNode reverse = reverseList(参数1);
  3. 逻辑处理(可能有,也可能没有,具体问题具体分析)<br />}

:::

  1. public ListNode ReverseList(ListNode head) {
  2. // 退出条件
  3. if(head == null || head.next == null){
  4. return head;
  5. }
  6. // 逻辑处理
  7. ListNode next = head.next;
  8. //从当前节点的下一个节点开始递归调用
  9. ListNode reverse = ReverseList(next);
  10. // reverse是反转之后的链表的头节点,
  11. // 反转完之后, next是 链表的尾结点;
  12. // 将当前节点head, 挂载到 next之后即可.
  13. next.next = head; // next 是当前节点的环.
  14. head.next = null ; // 不这么做, 就会构成环.
  15. return reverse;
  16. }
  1. public ListNode ReverseList(ListNode head) {
  2. if (head == null || head.next == null)
  3. return head;
  4. ListNode reverse = ReverseList(head.next);
  5. head.next.next = head;
  6. head.next = null;
  7. return reverse;
  8. }
  9. 使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 ans
  10. 此后,每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。
  11. 同时让当前结点的 next 指针指向NULL ,从而实现从链表尾部开始的局部反转
  12. 当递归函数全部出栈后,链表反转完成。
  13. 时间复杂度:O(N),其中 N 是链表的长度。需要对链表的每个节点进行反转操作。
  14. 空间复杂度:O(N),其中 N 是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 N

image.png

合并排序链表

:::info 当两个链表为空时,返回空链表
当两个链表均不为空,谁的当前元素小,将谁放入新的链表中,然后指针右移
当最后只有一个非空链表时,直接放在新链表的右侧 :::

  1. import java.util.*;
  2. /*
  3. public class ListNode {
  4. int val;
  5. ListNode next = null;
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. }*/
  10. public class Solution {
  11. public ListNode Merge(ListNode list1,ListNode list2) {
  12. if(list1 == null && list2 == null){
  13. return null;
  14. }
  15. //其他情况,则
  16. // 定义一个新的ListNode;
  17. ListNode myList = new ListNode(0); // 即将被返回的 头结点
  18. ListNode now = myList;
  19. while(list1 !=null && list2 !=null){
  20. int key; // 临时存储区
  21. if( list1.val <= list2.val){
  22. key = list1.val;
  23. list1 = list1.next;
  24. }else{
  25. key = list2.val;
  26. list2 = list2.next;
  27. }
  28. // 把较小的值封装起来, 准备给到新的链表;
  29. ListNode node = new ListNode(key);
  30. now.next = node;
  31. now = now.next;
  32. }
  33. // 只要List1 和List2 有一个为null;
  34. if(list1 != null ) now.next = list1;
  35. if(list2 != null) now.next = list2;
  36. return myList.next; // myList是一个无用的领头;
  37. }

链表公共节点

  1. 快慢指针

day0702链表 - 图10

  1. import java.util.*;
  2. /*
  3. public class ListNode {
  4. int val;
  5. ListNode next = null;
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. }*/
  10. public class Solution {
  11. public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
  12. ListNode l1 = pHead1;
  13. ListNode l2 = pHead2;
  14. while(l1 != l2){
  15. // 同样长度,同样速度;那么 只要有相交,就一定会遇到.
  16. l1 = (l1 ==null)?pHead2:l1.next;
  17. l2 = (l2==null)?pHead1:l2.next; // 当l2 ==null时, 转向pHead1;
  18. }
  19. // 若l1 == l2 == null; 也算是一种相遇把.
  20. return l1;// return l2;
  21. }
  22. }

时间复杂度:O(m+n)。链表1和链表2的长度之和。
空间复杂度:O(1)。常数的空间。

  1. 集合set

先把第一个链表的节点全部存放到集合set中,然后遍历第二个链表的每一个节点,判断在集合set中是否存在,如果存在就直接返回这个存在的结点。如果遍历完了,在集合set中还没找到,说明他们没有相交,直接返回null即可

  1. public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
  2. HashSet<ListNode> set = new HashSet<ListNode>();
  3. // 将List1 全部加入set里;
  4. while(pHead1!=null){
  5. set.add(pHead1);
  6. pHead1 = pHead1.next;
  7. }
  8. while(pHead2 != null){
  9. // 若set里面含有pHead2
  10. // 必定是第一个公共节点
  11. if(set.contains(pHead2)){
  12. return pHead2;
  13. }
  14. pHead2 = pHead2.next;
  15. }
  16. // 若集合set不包含链表2的任意一个节点, 说明节点, 直接返回null;
  17. return null;
  18. }

时间复杂度O(M+N):M, N分别表示 pHead1, pHead2的链表长度,最差情况下需要遍历完两个链表
空间复杂度O(M):需要额外集合空间存储 pHead1 结点

  1. 统计两个链表的长度

day0702链表 - 图11
在仅仅遍历一次的情况下, 多余的长度的那部分, 是不可能成为公共区域的节点的.

  1. import java.util.*;
  2. /*
  3. public class ListNode {
  4. int val;
  5. ListNode next = null;
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. }*/
  10. public class Solution {
  11. public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
  12. int lenA =length(pHead1);
  13. int lenB = length(pHead2);
  14. while(lenA != lenB){
  15. if(lenA>lenB){
  16. pHead1 = pHead1.next;
  17. lenA--;
  18. }else{
  19. pHead2 = pHead2.next;
  20. lenB--;
  21. }
  22. }
  23. // 必定lenA == lenB;
  24. while(pHead1 != pHead2){// 引用类型; 指向同一个节点(对象)
  25. pHead1 = pHead1.next;
  26. pHead2 = pHead2.next;
  27. }
  28. // 一定相等了 pHead1 == pHead2 ;
  29. //1. 都为null
  30. // 2. 公共节点
  31. return pHead1;
  32. }
  33. private int length(ListNode node){
  34. int length = 0 ;
  35. //不可使用iterator,那是容器的迭代器; 当前是一个容器里面的节点;
  36. while(node != null){
  37. node = node.next;
  38. length++;
  39. }
  40. return length;
  41. }
  42. }
  1. 链表公共部分

image.png

链表中环的入口节点

image.png
image.png
image.png
image.png
image.png

HashSet

首先这题要我们找链表的环入口结点,最常规易懂的解法就是遍历整个链表结点,然后用哈希表来存储已访问过的结点,最后进行对比。 若该结点已存在哈希表中,则代表该结点是我们要找的环形链表的入口结点;否则把结点添加到哈希表中,继续往下遍历。

  1. public ListNode EntryNodeOfLoop(ListNode pHead) {
  2. HashSet<ListNode> set = new HashSet<ListNode>();
  3. while(pHead != null){
  4. // 一定要先判断pHead是否已经被set包含了;
  5. if(set.contains(pHead)){
  6. return pHead;
  7. }
  8. // pHead必定是不重复的.
  9. set.add(pHead);
  10. pHead = pHead.next;
  11. }
  12. }
  13. 时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);
  14. 空间复杂度:O(n),需要额外的n空间的hashset来存储结点。

快慢指针

  1. public ListNode EntryNodeOfLoop(ListNode pHead) {
  2. if(pHead ==null){
  3. return pHead;
  4. }
  5. ListNode slow = pHead;
  6. ListNode fast = pHead;
  7. while( fast != null && fast.next != null){
  8. fast = fast.next.next;
  9. slow = slow.next;
  10. // 记录下 快慢指针 第一次相遇的节点
  11. if(slow == fast) break;
  12. }
  13. // 快指针指向null,则无环
  14. if(fast == null || fast.next == null){
  15. return null;
  16. }
  17. fast = pHead; // 将快指针拎回头
  18. while( fast != slow){
  19. fast = fast.next;
  20. slow = slow.next; // 起点 和 第一次相遇的节点 以相同速度出发, 相遇即为入口
  21. }
  22. return fast;
  23. }
  24. }
  25. 时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);
  26. 空间复杂度:O(1),需要额外两个快慢指针来遍历结点。
  27. 时间复杂度:O(n) (其中n为链表中节点的数目,快慢指针走过的距离都不会超过链表的长度)
  28. 空间复杂度:O(1) (双指针使用常数大小的额外空间)

无环
day0702链表 - 图18
有环
day0702链表 - 图19
结论证明 :::info 1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。 ::: image.png

倒数第k个节点

快慢指针

:::info 起始先让快指针 fast 先走 day0702链表 - 图21 步,此时 fast 和 slow 之间距离为 day0702链表 - 图22,之后让 fast 和 slow 指针一起走(始终维持相对距离为 day0702链表 - 图23),当 fast 到达链表尾部,slow 即停在倒数第 day0702链表 - 图24 个节点处。 :::

  1. public class Solution {
  2. /**
  3. 快慢指针有 两倍速度, 有 先k步
  4. */
  5. public ListNode FindKthToTail (ListNode pHead, int k) {
  6. // write code here
  7. ListNode fast = pHead ;
  8. ListNode slow = pHead;
  9. int count =1 ;
  10. while(count <= k){
  11. // 快指针先走k步,若发现走着走着fast为null;说明k大于链表长度;
  12. if(fast == null){
  13. return null;
  14. }
  15. // if 判断必须放在最前面;
  16. count++;
  17. fast = fast.next;
  18. }
  19. // 双指针同时走
  20. while(fast != null){
  21. fast = fast.next;
  22. slow =slow.next;
  23. }
  24. return slow;
  25. }
  26. }
  27. 时间复杂度:O(N)
  28. 空间复杂度: O(1)

反转链表+头插法

  1. import java.util.*;
  2. /*
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next = null;
  6. * public ListNode(int val) {
  7. * this.val = val;
  8. * }
  9. * }
  10. */
  11. public class Solution {
  12. /**
  13. 快慢指针有 两倍速度, 有 先k步
  14. */
  15. public ListNode FindKthToTail (ListNode pHead, int k) {
  16. ListNode newHead = null;
  17. ListNode cur = pHead;
  18. int n = 0 ;
  19. while(cur != null){
  20. // 保存原链下一个
  21. ListNode temp = cur.next ;
  22. // 反转指向
  23. cur.next = newHead;
  24. // 将新链表元素更新为当前原链表的节点
  25. newHead = cur ;
  26. // 移动旧链表元素到 旧链表的下一个;
  27. cur = temp;
  28. // 遍历个数+1;
  29. n++;
  30. } // 跳出循环后, newHead将会是 新链表的头结点
  31. ListNode kHead = null ;
  32. ListNode p = newHead; // 从头结点移动, 头插法
  33. int count = 1;
  34. if(n < k){ // 无法等于
  35. // 链表长度不足, 就没有倒数第k个;
  36. return null;
  37. }
  38. while(count<=k){
  39. ListNode temp = p.next;
  40. p.next = kHead;
  41. kHead = p;
  42. p = temp;
  43. count++;
  44. }
  45. return kHead; //KHead是 头插法产生链表的头结点;
  46. }
  47. }

堆栈

  1. public ListNode FindKthToTail (ListNode pHead, int k) {
  2. // 边界条件判断;
  3. if(pHead == null || k == 0 ){
  4. return null;
  5. }
  6. //
  7. Stack<ListNode> stack = new Stack<>();
  8. while(pHead != null){
  9. stack.push(pHead);
  10. pHead = pHead.next;
  11. }
  12. // 观察栈的长度 是否小于k
  13. if(stack.size() < k){
  14. return null;
  15. }
  16. //弹栈
  17. ListNode newHead = stack.pop();
  18. // 只要第k个元素; 因为在 while()之前 已经弹栈一次了.
  19. while(--k > 0 ){
  20. newHead = stack.pop();
  21. }
  22. return newHead;
  23. }

删除链表中重复节点

迭代

  1. public ListNode deleteDuplication(ListNode pHead) {
  2. ListNode dummy = new ListNode(-1); //新建一个虚拟节点, 其数据域不是很重要
  3. ListNode tail = dummy;
  4. while(pHead != null){
  5. // 进入循环迭代
  6. // 尾结点 或 不同值,才会添加到新的链表;
  7. if(pHead.next == null || pHead.next.val!=pHead.val){
  8. tail.next = pHead;
  9. tail = pHead;
  10. }
  11. // 若出现 值相同,就持续移动到最后一位;
  12. while(pHead.next != null && pHead.val == pHead.next.val){
  13. pHead = pHead.next;
  14. }
  15. // 跳出连续相同的一段, 又向后移动了一位, 因为重复元素不保留;
  16. pHead = pHead.next;
  17. }
  18. tail.next = null;
  19. return dummy.next; // dummu是虚假de,dummy.next才是第一个真的;
  20. }

递归

image.png

image.png

拓展

image.png

删除链表节点

我的代码

  1. public ListNode deleteNode (ListNode head, int val) {
  2. // 传入为空;
  3. if(head == null || head.next == null){
  4. return null;
  5. }
  6. ListNode newHead = head;
  7. // 迭代整个链表
  8. while(head != null){
  9. if(head.val == val){
  10. head.next = head.next.next;
  11. }
  12. }
  13. return newHead;
  14. }

:::info public ListNode deleteDuplication(ListNode pHead) {
ListNode dummyNode = new ListNode(-1);
ListNode tail = dummyNode;
if(pHead == null){
return null;
}
while( pHead != null){
// 1. 当前与下一个节点值不等 ; pHead.next == null到达末尾, 不存在下一个节点了;
if( pHead.next == null || pHead.val != pHead.next.val ){
// 拼接到 虚拟节点尾部
tail.next = pHead;
// 更新tail
tail = pHead;
}
// 只要相等就一直处理;
while(pHead.next != null && pHead.val == pHead.next.val){

pHead = pHead.next;

// 没有事先保存好下一个节点的使用
// pHead = pHead.next;
// pHead.next = pHead;
}
pHead = pHead.next;
}
// 为了删除那个重复的唯一项

tail.next = null;
return dummyNode.next;



} :::

网友解法

  1. public ListNode deleteNode (ListNode head, int val) {
  2. if(head == null) return null;
  3. // 虚拟一个头结点
  4. ListNode dummyHead = new ListNode(-1);
  5. dummyHead.next = head; // 人为添加一个节点;
  6. ListNode pre = dummyHead ;
  7. while(head != null){
  8. // 当发现val相等
  9. if(head.val == val){
  10. break;
  11. }
  12. //更新双指针迭代;
  13. pre = head ;
  14. head = head.next;
  15. }
  16. // 结束上述循环 ,1 . break, 2. head == null
  17. if(head == null) return dummyHead.next; // 未找到需要删除值;
  18. pre.next = head.next; // 跳过 值相等的那个节点;
  19. return dummyHead.next;//
  20. }