链表操作II

        看这篇文章之前,可以先看看链表操作I。这里再次分享两个链表题,这两个链表题有都一个共同的特点,就是在头结点之前添加一个虚拟头结点,也称为哑结点(dummyHead)。先给出链表结构的定义。

class ListNode{
	int val;
	ListNode next;
	public ListNode(){}
	public ListNode(int val){
		this.val = val;
	}
	public ListNode(int val, ListNode next){
		this.val = val;
		this.next = next;
	}
}

合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
如1->4->5->null,2->3->7->8->12->null两个升序链表合并结果为1->2->3->4->5->7->8->12->null。

class Solution {

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummyHead = new ListNode(-1);
        ListNode node = dummyHead;
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){
                node.next = list1;
                list1 = list1.next;
            }else{
                node.next = list2;
                list2 = list2.next;
            }
            node = node.next;
        }
        if(list1 != null){
            node.next = list1;
        }
        if(list2 != null){
            node.next = list2;
        }
        return dummyHead.next;
    }
}

        以(list1)1->4->8->null和(list2)2->5->6->12->14->null为例,说明合并两个升序链表的过程。首先定义一个虚拟头结点-1->null。之后定义一个node指针指向虚拟头结点。进入while循环。进入if-else判断,比较list1和list2首个元素的大小,1<2,所以node.next = list1,也就是-1->1->4->8->null。之后将list1指向下一个结点4,node指向1这个结点。
        比较4和2大小,4>2,则node.next = list2,也就是-1->1->2->5->6->12->14->null。之后将list2指向下一个结点5,node指向2这个结点。
        比较4和5大小,4 < 5,node.next = list1,也就是-1->1->2->4->8->null。list1指向下一个结点8,node指向4这个结点。
        比较8和5的大小,8 > 5,node.next = list2,也就是-1->1->2->4->5->6->12->14->null。list2移动到下一个结点6,node指向5这个结点。
        比较8和6的大小,8 > 6,则node.next = list2,也就是-1->1->2->4->5->6->12->14->null。list2移动到下一个结点12,node指向6这个结点。
        比较8和12的大小,8 < 12,则node.next = list1,也就是-1->1->2->4->5->6->8->null。list1移动到下一个结点null,node指向8这个结点。进入下一轮循环因list == null不满足条件跳出循环。
        由于list1==null,不满足if(list != null)这个if条件。由于list2指向12,不等于null,满足if(list2 != null)这个条件,将node->next = list2,也就是-1->1->2->4->5->6->8->12->14->null。也就是将list2中剩余没有遍历完的升序链表段拼接在结果链表的末尾。虚拟头结点-1的next即为最终的合并结果。

删除链表的倒数第N个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
如链表1->2->3->4->5->null,删除倒数第2个结点,结果为1->2->3->5->null。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode fast = dummyHead;
        ListNode slow = dummyHead;
        while(n >= 0){
            fast = fast.next;
            n--;
        }
        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return dummyHead.next;
    }
}

        这题的思路就是先遍历到倒数第N-1个结点,之后将第N-1个结点的next指向第N个结点的next,也就是第N-1个结点的next的next。现在的问题是如何通过一次遍历就找到链表的第N-1个结点。通过快慢指针,先让快指针先走N+1步,之后快慢指针同时走,当快指针走到链表末尾(null)时,slow就指向了链表的第N-1个结点。以1->2->3->4->null为例子,要删除3这个结点,也就是倒数第2个结点,定义fast,先走3步,fast指向了4这个结点,之后定义slow指针指向头结点,之后slow和fast同时走,slow走到2,fast走到null。以1->2->3->4->5->6->null为例子,要删除5这个结点,倒数第2个结点,fast先走3步,fast指向4,之后slow从1开始,fast从4开始,slow指向2,fast指向5,slow指向3,fast指向6,slow指向4,fast指向null。删除倒数第N个结点,fast先走N + 1步,和链表的长度没有关系。所以就算在链表头添加一个虚拟头结点,该规则也同样适用。但是添加虚拟头结点之后,一些特殊情况就可以采用相同的处理规则。
        这里为什么需要定义一个虚拟头结点呢?如果要删除的链表的头结点,就需要对这个特殊情况进行特殊处理。比如1->2->null,删除倒数第二个结点,fast要走3步,显然fast只能走2步,无法走第三步。就会报错,因此删除头结点的这种情况需要特殊处理。
        如果在原有链表上添加一个虚拟头结点,就无需特殊处理。设链表为1->null,添加一个虚拟头结点-1->1->null。此时删除1这个结点,也就是倒数第1个,fast走两步,fast指向null,slow指向虚拟头结点-1,因为fast为null,没有了slow和fast同时移动的过程。slow.next = slow.next.next,也就是-1->null,把1直接删除。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值