【链表】算法例题

目录

 八、 链表

57. 环形链表 ① ×

58. 两数相加 ② √

59. 合并两个有序链表 ① √-

60. 随机链表的复制 ②

61. 反转链表II ② ×

62. K个一组翻转链表 ③

63. 删除链表的倒数第N个结点 ② √-

64. 删除排序链表中的重复元素II ②  √-

65. 旋转链表 ② √-

66. 分隔链表 ② √

67. LRU缓存 ②


八、 链表

57. 环形链表 ① ×

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

力扣解析:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

方法2:(0ms)

    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) return false;
        int n = 10010;
        while (n -- > 0) {
            head = head.next;
            if (head == null) return false;
        }
        return true;
    }

58. 两数相加 ② √

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零

    方法1:(1ms)

        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            ListNode head = new ListNode();
            ListNode temp = head;
            int more = 0;
            int res = 0;
            while (!(l1 == null && l2 == null)){
                res = (l1 != null? l1.val : 0) + (l2 != null? l2.val : 0) + more;
                if (res > 9){
                    res = res - 10;
                    more = 1;
                }else {
                    more = 0;
                }
                ListNode newNode = new ListNode(res);
                temp.next = newNode;
                temp = temp.next;
                if (l1 != null){
                    l1 = l1.next;
                }
                if (l2 != null){
                    l2 = l2.next;
                }
            }
            if (more == 1){
                temp.next = new ListNode(more);
            }
            return head.next;
        }

    59. 合并两个有序链表 ① √-

    将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

    示例 1:

    输入:l1 = [1,2,4], l2 = [1,3,4]
    输出:[1,1,2,3,4,4]
    示例 2:

    输入:l1 = [], l2 = []
    输出:[]
    示例 3:

    输入:l1 = [], l2 = [0]
    输出:[0]

    提示:

    两个链表的节点数目范围是 [0, 50]
    -100 <= Node.val <= 100
    l1 和 l2 均按 非递减顺序 排列

      方法1:(0ms)

          public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
              if (list1 == null && list2 == null) {
                  return null;
              } else if (list1 == null && list2 != null) {
                  return list2;
              } else if (list1 != null && list2 == null) {
                  return list1;
              }
              ListNode head = new ListNode();
              ListNode left = list1;
              ListNode right = list2;
              ListNode temp = head;
              while (left != null && right != null) {
                  if (left.val < right.val) {
                      temp.next = left;
                      left = left.next;
                  } else {
                      temp.next = right;
                      right = right.next;
                  }
                  temp = temp.next;
              }
              if (left == null) {
                  temp.next = right;
              } else if (right == null) {
                  temp.next = left;
              }
              return head.next;
          }
      

      方法2:(0ms  递归)

          public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
              if (list1 == null) {
                  return list2;
              }
              else if (list2 == null) {
                  return list1;
              }
              else if (list1.val < list2.val) {
                  list1.next = mergeTwoLists(list1.next, list2);
                  return list1;
              }
              else {
                  list2.next = mergeTwoLists(list1, list2.next);
                  return list2;
              }
          }

      60. 随机链表的复制 ②

      61. 反转链表II ② ×

      给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

      示例 1:

      输入:head = [1,2,3,4,5], left = 2, right = 4
      输出:[1,4,3,2,5]
      示例 2:

      输入:head = [5], left = 1, right = 1
      输出:[5]

      提示:

      链表中节点数目为 n
      1 <= n <= 500
      -500 <= Node.val <= 500
      1 <= left <= right <= n

        力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        方法1:(0ms)

        public ListNode reverseBetween(ListNode head, int left, int right) {
        	ListNode temp = head;
        	ListNode newHead = new ListNode();
        	ListNode newTemp = newHead;
        	ListNode cur = null;
        	cur.next = newTemp;
        	for (int i = 0; i < left; i++) {
        		newTemp.next = temp;
        		newTemp = newTemp.next;
        		temp = temp.next;
        			cur = cur.next;
        	}
        	ListNode midTemp = null;
        	for (int i = left; i < right; i++) {
        		midTemp = temp;
        		midTemp.next = cur.next;
        		cur.next = midTemp;
        		temp = temp.next;
        	}
        	while (temp != null){
        		newTemp.next = temp;
        		temp = temp.next;
        		newTemp = newTemp.next;
        	}
        	return newHead.next;
        }

        方法2:(0ms)

            public ListNode reverseBetween(ListNode head, int m, int n) {
                // 定义一个dummyHead, 方便处理
                ListNode dummyHead = new ListNode(0);
                dummyHead.next = head;
        
                // 初始化指针
                ListNode g = dummyHead;
                ListNode p = dummyHead.next;
        
                // 将指针移到相应的位置
                for(int step = 0; step < m - 1; step++) {
                    g = g.next; p = p.next;
                }
        
                // 头插法插入节点
                for (int i = 0; i < n - m; i++) {
                    ListNode removed = p.next;
                    p.next = p.next.next;
        
                    removed.next = g.next;
                    g.next = removed;
                }
        
                return dummyHead.next;
            }
        
        作者:贾卷积
        链接:https://leetcode.cn/problems/reverse-linked-list-ii/solutions/138910/java-shuang-zhi-zhen-tou-cha-fa-by-mu-yi-cheng-zho/
        

        62. K个一组翻转链表 ③

        63. 删除链表的倒数第N个结点 ② √-

        给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

        示例 1:

        输入:head = [1,2,3,4,5], n = 2
        输出:[1,2,3,5]
        示例 2:

        输入:head = [1], n = 1
        输出:[]
        示例 3:

        输入:head = [1,2], n = 1
        输出:[1]

        提示:

        链表中结点的数目为 sz
        1 <= sz <= 30
        0 <= Node.val <= 100
        1 <= n <= sz

          进阶:你能尝试使用一趟扫描实现吗?

          方法1:(0ms)

          public static ListNode removeNthFromEnd(ListNode head, int n) {
          	if (head.next == null){
          		return null;
          	}
          	ListNode fast = head;
          	ListNode slow = new ListNode(-1);
          	slow.next = head;
          	int index = 1;
          	while (fast.next != null){
          		if (index > n - 1){
          			slow = slow.next;
          		}
          		fast = fast.next;
          		index++;
          	}
          	slow.next = slow.next.next;
          	return slow.val == -1? slow.next : head;
          }
              public ListNode removeNthFromEnd(ListNode head, int n) {
                  if (head.next == null && n == 1){
                      return null;
                  }
                  int count = 1;
                  ListNode temp = head;
                  while (temp.next != null){
                      count++;
                      temp = temp.next;
                  }
                  if (n == count){
                      return head.next;
                  }else {
                      int cnt = 1;
                      temp = head;
                      while (cnt < count - n) {
                          cnt++;
                          temp = temp.next;
                      }
                      temp.next = temp.next.next;
                      return head;
                  }
              }

           方法2:(0ms)

          解题思路:
          整体思路是让前面的指针先移动 n 步,之后前后指针共同移动直到前面的指针到尾部为止
          首先设立预先指针 pre
          设预先指针 pre 的下一个节点指向 head,设前指针为 start,后指针为 end,二者都等于 pre
          start 先向前移动n步
          之后 start 和 end 共同向前移动,此时二者的距离为 n,当 start 到尾部时,end 的位置恰好为倒数第 n 个节点
          因为要删除该节点,所以要移动到该节点的前一个才能删除,所以循环结束条件为 start.next != null
          删除后返回 pre.next,为什么不直接返回 head 呢,因为 head 有可能是被删掉的点
          时间复杂度:O(n)

          作者:画手大鹏
          链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solutions/7803/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/
           

              //通过快慢指针来解决,类似于你要删除中间元素的题
              public ListNode removeNthFromEnd(ListNode head, int n) {
                  //定义一个伪节点,用于返回结果
                  ListNode dumpy = new ListNode(0);
                  dumpy.next = head;
                  //定义一个快指针,指向伪节点,用于遍历链表
                  ListNode prev = dumpy;
                  //定一个慢指针,
                  ListNode tail = dumpy;
                  //让快指针先移动 n 步
                  while(n >0){
                      prev = prev.next;
                      n--;
                  }
                  //只要快指针不指向空,就继续循环
                  while(prev.next !=null){
                      //让快慢指针同时移动
                      tail = tail.next;
                      prev = prev.next;
                  }
                  //这时慢指针移动到的位置就是,要删除节点的前一个节点
                  //所以只要删除当前节点的下一个节点
                  tail.next = tail.next.next;
                  //返回为指针指向的头节点
                  return dumpy.next;
              }

          64. 删除排序链表中的重复元素II ②  √-

          给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

          示例 1:

          输入:head = [1,2,3,3,4,4,5]
          输出:[1,2,5]
          示例 2:

          输入:head = [1,1,1,2,3]
          输出:[2,3]

          提示:

          链表中节点数目在范围 [0, 300] 内
          -100 <= Node.val <= 100
          题目数据保证链表已经按升序 排列

            方法1:(0ms)

                public static ListNode deleteDuplicates(ListNode head) {
                    if (head == null || head.next == null){
                        return head;
                    }
                    ListNode pre = new ListNode(0);
                    pre.next = head;
                    ListNode newNode = pre;
                    while (head != null && head.next != null){
                        if (head.val != head.next.val) {   // 用值做比较,而不是两个节点做比较,节点一定不相等
                            head = head.next;
                            pre = pre.next;
                        }else {
                            while (head.next != null && head.next.val == head.val){  // 用值做比较
                                head = head.next;
                            }
                            head = head.next;
                            pre.next = head;
                        }
                    }
                    return newNode.next;
                }
            

            方法1:0ms

            public static ListNode deleteDuplicates(ListNode head) {
            	ListNode pre = new ListNode(0);
            	pre.next = head;
            	ListNode temp = pre;
            	while (temp.next != null){
            		if (temp.next.next == null || temp.next.val != temp.next.next.val){
            			temp = temp.next;
            		} else {
            			int nodeVal = temp.next.val;
            			while (temp.next != null && temp.next.val == nodeVal) {
            				temp.next = temp.next.next;
            			}
            		}
            	}
            	return pre.next;
            }

            解题思路:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

            65. 旋转链表 ② √-

            给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

            示例 1:

            输入:head = [1,2,3,4,5], k = 2
            输出:[4,5,1,2,3]
            示例 2:

            输入:head = [0,1,2], k = 4
            输出:[2,0,1]

            提示:

            链表中节点的数目在范围 [0, 500] 内
            -100 <= Node.val <= 100
            0 <= k <= 2 * 109

            方法1:(1842ms)

                public static ListNode rotateRight(ListNode head, int k) {
                    if (head == null || head.next == null || k == 0){
                        return head;
                    }
                    ListNode temp = head;
                    ListNode pre = new ListNode(0);
                    pre.next = head;
                    int cnt = 0;
                    while (cnt < k - 1){
                        if (temp.next == null){
                            temp = head;
                        }else {
                            temp = temp.next;
                        }
                        cnt++;
                    }
                    while (temp != null){
                        pre = pre.next;
                        temp = temp.next;
                    }
                    ListNode cur = pre;
                    while (pre.next != null){
                        pre = pre.next;
                    }
                    while (head != cur){
                        pre.next = new ListNode(head.val);
                        pre = pre.next;
                        head = head.next;
                    }
                    return cur;
                }

            方法2:(0ms)

                public static ListNode rotateRight(ListNode head, int k) {
                    if(head == null || head.next == null || k <= 0) {
                        return head;
                    }
                    int n = 0;
                    ListNode p = head;
                    while(p != null) {
                        p = p.next;
                        n++;
                    }
                    k = k%n;
                    if(k == n || k == 0) {
                        return head;
                    }
                    ListNode slow = head , fast = head;
                    for(int i=0; i<n-1; i++) {
                        if(i >= k) {
                            slow = slow.next;
                        }
                        fast = fast.next;
                    }
            
                    ListNode ans = head;
                    ans = slow.next; //slow为新链表头结点的前一个节点 
                    slow.next = null; //在旧链表中将新链表头结点与之前的节点斩断
                    fast.next = head;  //旧链表的尾节点连接上旧链表的头结点
                    return ans;
                }

            66. 分隔链表 ② √

            给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

            你应当 保留 两个分区中每个节点的初始相对位置。

            示例 1:

            输入:head = [1,4,3,2,5,2], x = 3
            输出:[1,2,2,4,3,5]
            示例 2:

            输入:head = [2,1], x = 2
            输出:[1,2]

            提示:

            链表中节点的数目在范围 [0, 200] 内
            -100 <= Node.val <= 100
            -200 <= x <= 200

            方法1:(0ms)202402

                public static ListNode partition(ListNode head, int x) {
                    if (head == null || head.next == null){
                        return head;
                    }
                    ListNode pre = new ListNode(0);
                    pre.next = head;
                    ListNode temp = head;
                    ListNode newList = pre;
                    while (temp != null && temp.val < x){
                        temp = temp.next;
                        pre = pre.next;
                    }
                    if (temp == null){
                        return head;
                    }
                    ListNode cur = temp;
                    ListNode newPre = new ListNode(0);
                    newPre.next = temp;
                    ListNode newCur = cur;
                    while (cur != null){
                        if (cur.val < x){
                            newCur = cur.next;
                            pre.next = new ListNode(cur.val);
                            pre = pre.next;
                            cur = newCur;
                            newPre.next = cur;
                        }else {
                            cur = cur.next;
                            newPre = newPre.next;
                        }
                    }
                    pre.next = temp;
                    return newList.next;
                }
            

            方法1:(0ms) 20250502

            public ListNode partition(ListNode head, int x) {
            	ListNode small = new ListNode(0);
            	ListNode big = new ListNode(0);
            	ListNode smallTemp = small;
            	ListNode bigTemp = big;
            	while (head != null){
            		if (head.val < x){
            			smallTemp.next = new ListNode(head.val);
            			smallTemp = smallTemp.next;
            		}else {
            			bigTemp.next = new ListNode(head.val);
            			bigTemp = bigTemp.next;
            		}
            		head = head.next;
            	}
            	smallTemp.next = big.next;
            	return small.next;        
            }
            

            方法2:(0ms):力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

                public ListNode partition(ListNode head, int x) {
                    ListNode smlDummy = new ListNode(0), bigDummy = new ListNode(0);
                    ListNode sml = smlDummy, big = bigDummy;
                    while (head != null) {
                        if (head.val < x) {
                            sml.next = head;
                            sml = sml.next;
                        } else {
                            big.next = head;
                            big = big.next;
                        }
                        head = head.next;
                    }
                    sml.next = bigDummy.next;
                    big.next = null;
                    return smlDummy.next;
                }
            
            作者:Krahets
            链接:https://leetcode.cn/problems/partition-list/solutions/2362068/86-fen-ge-lian-biao-shuang-zhi-zhen-qing-hha7/
            来源:力扣(LeetCode)
            著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

            67. LRU缓存 ②

            评论
            添加红包

            请填写红包祝福语或标题

            红包个数最小为10个

            红包金额最低5元

            当前余额3.43前往充值 >
            需支付:10.00
            成就一亿技术人!
            领取后你会自动成为博主和红包主的粉丝 规则
            hope_wisdom
            发出的红包
            实付
            使用余额支付
            点击重新获取
            扫码支付
            钱包余额 0

            抵扣说明:

            1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
            2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

            余额充值