代码随想录算法训练营第四天 | 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

24. 两两交换链表中的节点

记录之前的代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head==null) return head;

        ListNode fakeNode = new ListNode();
        fakeNode.next = head;

        ListNode left = fakeNode, mid=head, right=head.next;

        while(right!=null){
            mid.next = right.next;
            right.next = mid;
            left.next = right;

            mid = mid.next;
            if (mid == null){ break; }

            left = right.next;
            right = mid.next;
        }

        return fakeNode.next;
    }
}

第一次尝试:未注意到,除了两两之间需要转换,其实每一对之间也要转换next指针。
不要忘记虚拟头节点啊!!

class Solution {
    public ListNode swapPairs(ListNode head) {
        // 边缘条件
        if (head==null || head.next==null) return head;
        
        // 
        ListNode pre = head, cur = head.next, temp, resHead = head.next;
        while(cur!=null){
            // swap
            temp = cur.next;
            cur.next = pre;
            pre.next = temp;

            // 循环条件更新
            pre = temp;
            if(pre==null){
                break;
            }else{
                cur = pre.next;
            }
        }
        return resHead;
    }
}

修正:

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0, head);

        // 边缘条件
        if (head==null || head.next==null) return head;
        
        // 
        ListNode pre = dummy, cur = head, next = head.next, temp;
        while(next!=null){
            // swap
            temp = next.next;
            next.next = cur;
            cur.next = temp;

            // swap之后 循环条件更新
            pre.next = next; // 重点:每组之间
            pre = cur;
            cur = temp;
            if(cur==null){
                break;
            }else{
                next = cur.next;
            }
            
        }
        return dummy.next;
    }
}

随想录中还有递归解法,同样尝试实现了一个:

class Solution {
    public ListNode swapPairs(ListNode head) {
        // 终止条件
        if(head==null || head.next==null) return head; // 返回值:返回的是两组之中,交换之后的,第一个节点
        // 递归逻辑
        ListNode node1 = head.next;
        ListNode temp = head.next.next;
        node1.next = head;
        head.next = swapPairs(temp);
        return node1;
    }
}

总结

查看代码随想录版本,并总结。

  1. 命名:面对众多指针的情况,尽量使用cur, node1, node2 。明确cur的位置,有助于进行循环。
  2. 交换顺序:如果更改交换顺序,则可以不用temp节点。
  3. 递归解法:可以先交换……虽然还是要临时节点来存储。
// 将步骤 2,3 交换顺序,这样不用定义 temp 节点
public ListNode swapPairs(ListNode head) {
    ListNode dummy = new ListNode(0, head);
    ListNode cur = dummy;
    while (cur.next != null && cur.next.next != null) {
        ListNode node1 = cur.next;// 第 1 个节点
        ListNode node2 = cur.next.next;// 第 2 个节点
        
        cur.next = node2; // 步骤 1
        node1.next = node2.next;// 步骤 3
        node2.next = node1;// 步骤 2
        cur = cur.next.next;
    }
    return dummy.next;
}
// 递归版本
class Solution {
    public ListNode swapPairs(ListNode head) {
        // base case 退出提交
        if(head == null || head.next == null) return head;
        // 获取当前节点的下一个节点
        ListNode next = head.next;
        // 进行递归
        ListNode newNode = swapPairs(next.next);
        // 这里进行交换
        next.next = head;
        head.next = newNode;

        return next;
    }
} 

19.删除链表的倒数第N个节点

双指针法:需要想清楚

  1. 删除倒数第n个,则指针指向倒数第n+1个。
  2. 遍历到链表尾的条件:二选一 —— 指向null 、指向最后一个节点。
  3. 很容易推导出:right与left之间相距n。
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        // right指向倒数第一个,left指向倒数第n+1个。相差n。
        ListNode left = dummy, right = dummy;
        // 先将right遍历。 循环结束时,相差n。
        for(int i=0; i<n; i++){
            if(right.next == null) return dummy.next;
            right = right.next;
        }
        while(right.next!=null){
            right = right.next;
            left = left.next;
        }
        left.next = left.next.next;
        return dummy.next;
    }
}

面试题 02.07. 链表相交

注意:交点不是数值相等,而是指针相等。

想试试合并链表同步移动法。但是超时:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        
        while(curA!=curB){
            curA = curA.next;
            curB = curB.next;
            if (curA==null) curA = headB;
            if (curB==null) curB = headA;
        }
        return curA;
    }
}

与代码随想录对比:

(版本二) 合并链表实现同步移动
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
		// p1 指向 A 链表头结点,p2 指向 B 链表头结点
		ListNode p1 = headA, p2 = headB;
		while (p1 != p2) {
			// p1 走一步,如果走到 A 链表末尾,转到 B 链表
			if (p1 == null) p1 = headB;
			else            p1 = p1.next;
			// p2 走一步,如果走到 B 链表末尾,转到 A 链表
			if (p2 == null) p2 = headA;
			else            p2 = p2.next;
		}
		return p1;
    }
}

主要问题在于:while的判断条件,并未处理curA=null,或curB=null时的情况。
不应该跳过null:如果两者同时等于null,只会有两种情况。都能够保证不会无限循环。

  1. 两链表长度相同,并且没有交点。(在组合链表之前)
  2. 两链表长度不同,并且没有交点。(在组合链表之后)
    即,组合链表时,两链表之间应该插入一个null,而不是单纯连在一起。

代码区别

对于正常解法,应该是算出两者长度之差,然后在倒数第n个节点(假设是n),同时开始遍历。

142.环形链表II

这道题算是很难的一道题(对我来说),因为需要额外的数学推导。

 /*
    两者最终相交时,进行了c次循环
    fast:走了2c
    slow:走了1c
    长度 x, y, z
    
    已知:
    x + y + n(y+z) = 2c
    x + y          = 1c
    得出:
        n(y+z) = x+y
    得出:
        x = n (y + z) - y 化为 x = (n - 1) (y + z) + z
    求x:
        n取1,2,3……不同值时,总有 —— 
            在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
            让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
        n的含义:index1在环里一共转了n圈。
 */
 public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while(true){
            if (fast==null || fast.next==null) return null;
            fast = fast.next.next;
            slow = slow.next;
            if(fast==slow) break;
        }
        ListNode node1 = head, node2 = fast;
        while(node1!=node2){
            node1 = node1.next;
            node2 = node2.next;
        }
        return node1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值