算法通关村第二关——链表反转问题解析(黄白级)

1.指定区间反转

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

我的思考:如果链表只有一个结点直接返回。

如果不是,遍历链表的同时,确定指针位置。反转leftNode为头结点,rightNode为尾结点的链表,再修改指针指向。

第二种方法,用链表循环的方式,将为区间尾结点插入到头结点之前。问题:如果left right 触碰到了区间边界怎么处理。

头插法:也就是第二种方法。区间尾结点插到头结点上。

如果使用链表循环的方式,首先要确定位置,left为1的时候情况分开来,然后保存第三部分头结点right.next,没有就不保存,这个时候就可以不管第三部分头结点了。然后直接对链表进行链表循环,倒数第right - left +1 个结点开始循环。但是有一个明显的问题,这段代码不注意就会出现环,因为我们没有把循环部分从链表中断开,而且修改指针会很麻烦。所以不能用链表循环的思路来做。

其实在第一次遍历到反转区间的时候,我们可以把反转区间这个大问题分解成为:将当前right指针指向的结点,插到已经反转完成的区域之前。
那么这个函数的开始部分是right指向left.next的时候。结束部分是,right移动次数 == right-1;

1.1头插法(重要)

理论成立,开始尝试。
代码:

lass Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {

        if(head == null || head.next==null || right==left)
        return head;

        int left2=left;
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode leftNode = head;

        while(left2-->1)
        {
            dummyNode=dummyNode.next;
        }
        leftNode = dummyNode.next;
        

        ListNode curNode = leftNode.next;
        ListNode temp ;

        for(int i=0;i<right-left;i++)
        {
            if(i==0 )
            {
                leftNode.next = null;
            }

            if(curNode==null || curNode.next==null)
                return head;
           
            temp = curNode.next;

            curNode.next = leftNode;
            dummyNode.next= curNode;
            leftNode = curNode;
            curNode = temp;
        }

        while(head.next!=null)
        {
        head=head.next;
        }

        head.next=curNode;

        return dummyNode;
        
    }
}

这段代码问题很大。重写

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 设置 dummyNode 是这一类问题的一般做法
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode pre = dummyNode;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode cur = pre.next;
        ListNode next;
        for (int i = 0; i < right - left; i++) {
            next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummyNode.next;
    }
}

1.2穿针引线法

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        ListNode pre = dummyNode;
        // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
        // 建议写在 for 循环里,语义清晰
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }

        // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
        ListNode rightNode = pre;
        for (int i = 0; i < right - left + 1; i++) {
            rightNode = rightNode.next;
        }

        // 第 3 步:切断出一个子链表(截取链表)
        ListNode leftNode = pre.next;
        ListNode curr = rightNode.next;

        // 注意:切断链接
        pre.next = null;
        rightNode.next = null;

        // 第 4 步:同第 206 题,反转链表的子区间
        reverseLinkedList(leftNode);

        // 第 5 步:接回到原来的链表中
        pre.next = rightNode;
        leftNode.next = curr;
        return dummyNode.next;
    }

    private void reverseLinkedList(ListNode head) {
        // 也可以使用递归反转一个链表
        ListNode pre = null;
        ListNode cur = head;

        while (cur != null) {
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
    }
}

2.两两交换链表中的结点(重要)

其实只要把指定区间反转中的 头插法 理解透彻就能轻松举一反三。、

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

我的思考:因为肯定要处理头结点,所以加一个虚拟头结点用来方便操作。然后每一组交换的前一个结点都能找到,0 2 4 6……。然后每一次交换用两个局部变量存储两个结点,就能轻松反转两个结点。

代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode pre = dummyHead;

        while(pre.next!=null && pre.next.next!=null)
        {
        ListNode node1 = pre.next;
        ListNode node2 = pre.next.next;
        node1.next = node2.next;
        node2.next = node1;
        pre.next = node2;
        pre = pre.next.next;
        }

        return dummyHead.next;
    }
}

3.单链表加1(作业+栈的解决办法)

用一个非空单链表,表示一个非负整数,将这个整数加一。

我的思考:首先,这条题关键在于进位。有两种情况,0和其他数。

如果当前结点进位了,就存下这个,保存到虚拟节点中。如果上一个结点有数字就加上去,没有就把虚拟节点接在链表上。

又进位了就重复这个过程,直到虚拟节点+高位结点<10。

问题关键,高位在表头,所以我们先反转链表,再反转回来。

代码:

public class linkListHomeWork {

        public static void main(String[] args) {

            //链表初始化
            ListNode head = initList();
            printListval(head);
            
					 //反转链表
            System.out.println();
            ListNode versehead = reverseList(head);
            printListval(versehead);
            
					 //链表加一
            ListADD(versehead,0);
            
					 //链表反转
            System.out.println();
            ListNode versehead2 = reverseList(versehead);
            printListval(versehead2);

        }

//打印链表的值
    public static void printListval(ListNode head)
    {
        ListNode temp = head;
        while(temp!=null)
        {
            System.out.print("{"+temp.val+"}");
            if(temp.next!=null)
                System.out.print(",");;
            temp=temp.next;
        }

    }

 //初始化方法(单链表测试)
        public static ListNode initList(){

            ListNode head = new ListNode(9);
            ListNode list = head;
           for (int i = 2; i < 10; i++){
               ListNode cur = new ListNode(9);
               list.next=cur;
               list= list.next;
           }
            return head;
       }

//高位表尾单链表加一
       public static void ListADD (ListNode cur,int carry)
       {
           cur.val = cur.val+1;
           carry  = cur.val / 10 ;
           cur.val = cur.val%10;
           if(carry==1&&cur.next!=null) {
               ListADD(cur.next, carry);
           }else if(carry==1&&cur.next==null)
           {
               ListNode node = new ListNode(1);
               cur.next =node;
           }

       }

	//递归反转整个链表
    public static ListNode reverseList(ListNode head)
    {
        if(head == null||head.next==null)
            return head;

      ListNode newhead =  reverseList(head.next);

       head.next.next=head;
       head.next=null;
       return newhead;
    }

}
  //结点结构
class ListNode {
    String name;
    String language;

    int val;
    ListNode next;

    ListNode() {
    }

    ListNode(int val)
    {
        this.val = val;
    }
    ListNode(String name, String language) {
        this.name = name;
        this.language = language;
    }

    ListNode(String name, String language, int val,ListNode next) {
        this.name = name;
        this.language = language;
        this.next = next;
        this.val=val;
    }
}

4.链表加法

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

k个一组反转

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~Yogi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值