面试题22:链表中倒数第k个结点

题目:输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个结点是值为4的结点。

public ListNode getKthFromEnd(ListNode head, int k) {
    if (head == null || k <= 0) {
        return null;
    }

    ListNode ahead = head;
    for (int i = 0; i < k - 1; i++) {
        if (ahead.next != null) {
            ahead = ahead.next;
        } else {
            return null;
        }
    }

    ListNode behind = head;
    while (ahead.next != null) {
        ahead = ahead.next;
        behind = behind.next;
    }
    return behind;
}

面试题23:链表中环的入口结点

题目:一个链表中包含环,如何找出环的入口结点?例如,在图3.8的链表中,环的入口结点是结点3。

  • 算法
    • 1.使用快慢指针找到环中任一结点
    • 2.计算环中结点数量
    • 3.以环中结点数量为间距使用前后指针找到环的入口结点
public ListNode entryNodeOfLoop(ListNode head) {
    ListNode meetNode = meetNode(head);
    if (meetNode == null) {
        return null;
    }

    int nodesInLoop = 1;
    ListNode ahead = meetNode;
    while (ahead.next != meetNode) {
        nodesInLoop++;
        ahead = ahead.next;
    }

    ahead = head;
    ListNode behind = head;
    for (int i = 0; i < nodesInLoop; i++) {
        ahead = ahead.next;
    }
    while (ahead != behind) {
        ahead = ahead.next;
        behind = behind.next;
    }
    return ahead;
}
private ListNode meetNode(ListNode head) {
    if (head == null) {
        return null;
    }

    ListNode slow = head.next;
    if (slow == null) {
        return null;
    }
    ListNode fast = slow.next;
    while (fast != null) {
        if (slow == fast) {
            return slow;
        }
        slow = slow.next;
        fast = fast.next;
        if (fast != null) {
            fast = fast.next;
        }
    }
    return null;
}

面试题24:反转链表

题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

  • 算法:
    • prev curr after 迭代往后操作
public ListNode reverseList(ListNode head) {
    ListNode curr = head;
    ListNode prev = null;
    while (curr != null) {
        ListNode after = curr.next;
        curr.next = prev;
        prev = curr;
        curr = after;
    }
    return prev;
}

面试题25:合并两个排序的链表

题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。例如输入图3.11中的链表1和链表2,则合并之后的升序链表如链表3所示。

  • 图3.11
  • 链表1:1 -> 3 -> 5
  • 链表2:1 -> 2 -> 4
  • 链表3:1 -> 1 -> 2 -> 3 -> 4 -> 5
// 迭代
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(-1);
    ListNode move = dummy;
    while (true) {
        if (l1 == null) {
            move.next = l2;
            break;
        }
        if (l2 == null) {
            move.next = l1;
            break;
        }

        if (l1.val < l2.val) {
            move.next = l1;
            l1 = l1.next;
        } else {
            move.next = l2;
            l2 = l2.next;
        }
        move = move.next;
    }
    return dummy.next;
}
// 递归
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if (l1 == null) {
        return l2;
    }
    if (l2 == null) {
        return l1;
    }

    if(l1.val < l2.val){
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else{
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
}

面试题26:树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构(约定空树不是任意一个树的子结构)。B是A的子结构, 即 A中有出现和B相同的结构和节点值。

public boolean isSubStructure(TreeNode A, TreeNode B) {
    if (A != null && B != null) {
        if (A.val == B.val) {
            if (treeAHasTreeB(A, B)) {
                return true;
            }
        }
        return isSubStructure(A.left, B) || isSubStructure(A.right, B);
    }
    return false;
}
private boolean treeAHasTreeB(TreeNode A, TreeNode B) {
    if (B == null) {
        return true;
    }
    if (A == null) {
        return false;
    }
    if (A.val != B.val) {
        return false;
    }
    return treeAHasTreeB(A.left, B.left) && treeAHasTreeB(A.right, B.right);
}