算法-玩转链表反转问题

1、逆转整个链表

链表反转问题是经常遇到的,最简单的就是反转整个链表了,出自Leetcode206-反转链表,比较经典而且优美的解决方式就是头插法了,此外也有递归法。方式很简单,不多做说明,代码如下

	//头插法,非递归
	public ListNode reverseNode(ListNode head){
		ListNode curr=head;
		ListNode prev=null;
		while(curr!=null){
			ListNode temp=curr.next;
			curr.next=prev;
			prev=curr;
			curr=temp;
		}
		return curr;
	}
	//递归法
    public 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;
    }

2、每K个反转一次链表

题目来源于 Leetcode25题-K 个一组翻转链表 是一道Hard的题。

问题描述:
K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5

这个问题其实可以看作第一题的扩展,第一题是本题在 k=链表长度的条件下 ,链表反转的一个特例

第一题是判断链表终止反转只有一个条件,就是遍历的链表指针==null,但是在每K个翻转一次的情况下,需要多一个条件,就是遍历K次进行一次小的终止

具体步骤如下:

1、声明一个冗余的头节点,作为反转后的节点头
2、声明一个指向当前节点头,也就是已排好序的节点头的指针,并将其指向冗余节点头
3、声明当前节点指针,指向正在遍历的节点
4、开始遍历所有节点
5、在遍历K个节点的过程中,将其反转,在遍历这K个节点前保存它的头部,这个头部就是反转后的尾部
6、反转完成后,检查是不是真的遍历了K个节点,如果的确遍历了K个节点,执行步骤7,否则执行8
7、将2中指明的节点头指向5中反转链表后的头部,然后再将头结点指针切换为5中保留的节点头部。这样就可以将其连接在一起
8、在本题中有个要求很特殊,如果不够K个,那就不反转了,但其实我们在5中已经将其反转了,所以只需要当前k的值(K已经减去一些了)作为终止反转的而条件即可。也就是再将剩余的k反转回来
9、最后返回1中声明的冗余节点的下一个节点。

按照上述步骤进行,那么问题就很好解决了,代码如下

    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy=new ListNode(-1);
        ListNode currHead=dummy;//前一段头插法得到链表的尾部
        ListNode curr=head;//现在的节点
        while(curr!=null){
            int count=k;
            ListNode endNode=curr;
            ListNode prev=null;//前一个结点
            while(curr!=null&&count-->0){//k个一组反转链表 例如12345 翻转一次prev=21,curr=345
                ListNode temp=curr.next;
                curr.next=prev;
                prev=curr;
                curr=temp;
            }
            if(count>0){//如果count>0说明最后剩下的不用翻转,所以在下面将这一段重新翻转,接到后面就可以
                curr=prev;
                prev=null;
                while(curr!=null){
                    ListNode temp=curr.next;
                    curr.next=prev;
                    prev=curr;
                    curr=temp;
                }
                currHead.next=prev;
            }else{
                currHead.next=prev;//前一段头插结果的尾部接上这段头插的头部
                currHead=endNode;//本段结果的尾部
            }

        }
        return dummy.next;
    }

3、两两交换链表的两个节点

本题目也是来源于Leetcode24-两两交换链表中的节点 难度medium

题目描述:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.

其实这个题也是2的一个变体,只不过K固定为2而已,而且题目已经暗示链表长度是2的倍数,所以我们不再需要考虑剩余节点不够的问题,其实直接把2的代码中k给固定为2后就能直接套在这个题目上

实现代码如下:

扫描二维码关注公众号,回复: 10255822 查看本文章
    public ListNode swapPairs(ListNode head) {
        ListNode dummy=new ListNode(-1);
        ListNode currHead=dummy;
        ListNode curr=head;

        while(curr!=null){
            int k=2;
            ListNode prev=null;
            ListNode last=curr;
            while(curr!=null&&k-->0){
                ListNode temp=curr.next;
                curr.next=prev;
                prev=curr;
                curr=temp;
            }
            //prev是倒置后的头
            //last是倒置后的链表尾
            currHead.next=prev;
            currHead=last;
        }
        return dummy.next;
    }
发布了28 篇原创文章 · 获赞 9 · 访问量 2416

猜你喜欢

转载自blog.csdn.net/qq_23594799/article/details/105084514