链表反转的拓展问题(算法通关村第二关白银挑战)

链表反转的拓展问题(算法通关村第二关白银挑战)


1指定区间反转

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

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

1.1头插法

  • 反转的整体思想是,在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。

  • 前面带虚拟节点的插入操作,每走一步都要考虑各种指针怎么指,既要将节点摘下来接到对应的位置上,还要保证后续节点能够找到。

  •   //1.1 头插法
          public LinkListNode reverseBetween(LinkListNode head, int left, int right){
              LinkListNode dummyNode = new LinkListNode(-1);
              dummyNode.next = head;
              LinkListNode prev = dummyNode;
              for(int i = 0; i < left-1; i++){
                  prev = prev.next;
              }
               LinkListNode cur = prev.next;
              LinkListNode next;
              for(int j = 0; j < right-left; j++){
                  next = cur.next;
                  cur.next = next.next;
                  next.next = prev.next;
                  prev.next = next;
              }
              return dummyNode.next;
          }
    

1.2穿针引线法

  • 先确定好需要反转的部分,也就是下图中 left 到 right 之间,然后再将三段链表拼接起来。这种方式类似裁缝一样,找准位置剪下来,再缝回去。

  • 算法步骤:

    • 第一步:先将待反转的区域反转;
    • 第二步:把 pre 的 next 指针指向反转以后的链表的头节点,把反转后的链表的尾结点的 next 指针指向 succ。
  •   //1.2 穿针引线法
          public LinkListNode reverseBetween2(LinkListNode head, int left, int right){
              LinkListNode dummyNode = new LinkListNode(-1);
              dummyNode.next = head;
              LinkListNode prev = dummyNode;
              //第一步:从虚拟头结点走 left-1 步,来到 left 节点的前一个节点
              for(int i = 0; i < left-1; i++){
                  prev = prev.next;
              }
              //第2步:从prev再走走 right - left + 1 步,来到 right节点
              LinkListNode rightNode = prev;
              for(int i = 0; i < right-left+1; i++){
                  rightNode = rightNode.next;
              }
              //第三步:切出一个子链表
              LinkListNode leftNode = prev.next;
              LinkListNode succ = rightNode.next;
              rightNode.next = null;
              //第四步:反转链表的子区间
              reverseLinkedList(leftNode);
              //第五步:接回到原来的链表中
              prev.next = rightNode;
              leftNode.next = succ;
              return dummyNode.next;
          }
          private void reverseLinkedList(LinkListNode head){
              //反转一个链表
              LinkListNode pre = null;
              LinkListNode cur = head;
              while(cur != null){
                  LinkListNode next = cur.next;
                  cur.next = pre;
                  pre = cur;
                  cur = next;
              }
          }
    

2两两交换链表中的节点

  • LeetCode24: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。

  • 输入:head = [1, 2, 3, 4]
    输出: [2, 1, 4, 3]
  •   public LinkListNode SwapPairs(LinkListNode head){
              LinkListNode dummyNode = new LinkListNode(-1);
              dummyNode.next = head;
              LinkListNode temp = dummyNode;
              while(temp.next != null && temp.next.next != null){
                  LinkListNode node1 = temp.next;
                  LinkListNode node2 = temp.next.next;
                  temp.next = node2;
                  node1.next = node2.next;
                  node2.next = node1;
                  temp = node1;
              }
              return dummyNode.next;
          }
    

3单链表相加

  • LeetCode369: 用一个非空单链表来表示一个非负整数,然后将这个整数加一。假设这个整数除了 0 本身,没有任何前导的 0。这个整数的各个数位按照高位在链表头部,低位在链表尾部的顺序排列。

  • 示例::
    输入:[1, 2, 3]
    输出:[1, 2 , 4]
  • 加法的计算过程:

    • 计算从低位开始,而链表从高位开始,所以处理就必须反转过来,此时可以用栈,也可以使用链表的反转来实现。
    • 基于栈的实现,先把给出的链表放到栈中,然后从栈中弹出栈顶数字digit, 加的时候要考虑一下进位的情况。
  •   public LinkListNode plusOne(LinkListNode head){
              Stack<Integer> st = new Stack();
              while(head != null){
                  st.push(head.val);
                  head = head.next;
              }
              int carry = 0;//记录进位
              LinkListNode dummy = new LinkListNode(0);
              int addr = 1;
              while(!st.isEmpty() || carry > 0){
                  int digit = st.empty() ? 0 : st.pop();
                  int sum = digit + addr + carry;
                  carry = sum >= 10 ? 1 : 0;
                  sum = sum >= 10 ? sum-10: sum;
                  LinkListNode cur = new LinkListNode(sum);
                  cur.next = dummy.next;
                  dummy.next = cur;
                  addr = 0;
              }
              return dummy.next;
          }
    

4链表加法

  • LeetCode445: 给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。他们的每个节点只储存一位数字。将这两数相加会返回一个新的链表。

  • 示例:
    输入:(6-> 1->7)+ (2-> 9 -> 5),即617 + 295
    输出:9 -> 1 -> 2,即912

4.1使用栈实现

  •   public LinkListNode addInListByStack(LinkListNode head1, LinkListNode head2){
              Stack<Integer> st1 = new Stack();
              Stack<Integer> st2 = new Stack();
              while(head1 != null){
                  st1.push(head1.val);
                  head1 = head1.next;
              }
              while(head2 != null){
                  st2.push(head2.val);
                  head2 = head2.next;
              }
              int carry = 0;
              LinkListNode dummy = new LinkListNode(0);
              while(!st1.isEmpty() || !st2.isEmpty() || carry > 0){
                  int digit1 = st1.empty() ? 0 : st1.pop();
                  int digit2 = st2.empty() ? 0 : st2.pop();
                  int sum = digit1 + digit2 + carry;
                  carry = sum/10;
                  sum = sum%10;
                  LinkListNode cur = new LinkListNode(sum);
                  cur.next = dummy.next;
                  dummy.next = cur;
              }
              return dummy.next;
          }
    

4.2使用链表反转实现

  • 使用链表反转,现将两个链表分别反转,最后计算完之后再将结果反转,一共有三次反转操作,所以必然将反转抽取出一个方法。

  •   public LinkListNode addInList(LinkListNode head1, LinkListNode head2){
              reverse(head1);
              reverse(head2);
              LinkListNode dummyNode = new LinkListNode(-1);
              LinkListNode cur = dummyNode;
              int carry = 0;//进位
              while(head1 != null || head2 != null){
                  int val = carry;
                  if(head1 != null){
                      val += head1.val;
                      head1 = head1.next;
                  }
                  if(head2 != null){
                      val += head2.val;
                      head2 = head2.next;
                  }
                  cur.next = new LinkListNode(val % 10);
                  carry = val/10;
                  cur = cur.next;
              }
              if(carry > 0){
                  cur.next = new LinkListNode(carry);
              }
              return reverse(dummyNode.next);
          }
          private LinkListNode reverse(LinkListNode head){
              //反转一个链表
              LinkListNode pre = null;
              LinkListNode cur = head;
              while(cur != null){
                  LinkListNode next = cur.next;
                  cur.next = pre;
                  pre = cur;
                  cur = next;
              }
              return pre;
          }
    

5.再论链表的回文序列问题

  • “快慢指针+一半反转”

  •   //再论链表的回文序列问题
          public boolean isPalindrome(LinkListNode head){
              if(head == null || head.next == null){
                  return false;
              }
              LinkListNode slow = head, fast = head;
              LinkListNode prev = head, prepre = null;
              while(fast != null || fast.next != null){
                  prev = slow;
                  slow = slow.next;
                  fast = fast.next;
                  //将前半段部分链表反转
                  prev.next = prepre;
                  prepre = prev;
              }
              if(fast != null){
                  slow = slow.next;
              }
              while(prev != null && slow != null){
                  if(prev.val != slow.val){
                      return false;
                  }
                  prev = prev.next;
                  slow = slow.next;
              }
              return true;
          }
    
    
    
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种一棵树leng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值