Skip to content

Latest commit

 

History

History
242 lines (196 loc) · 8.09 KB

File metadata and controls

242 lines (196 loc) · 8.09 KB

注:所有图片均来自CyC2018

1~22

23_链表中环的入口节点

如果一个链表包含环,如何找出环的入口节点?

EntryNodeInListLoop

如何确定链表中存在环?维护两个指针,一个走得快,一个走得慢,如果走得快的遇到了走得慢的,就说明存在环。如果走得快的走到了末尾,还未遇到走得慢的,则不存在环。

如何找到环的入口?先让一个指针前进环中节点个数步,然后两者一起前进,相遇的地方即是环的入口节点。

如何找到环中节点个数?当两个指针相遇时,一定在环内部,继续向前走,每走一步就+1,直到再次回到相遇的节点。

private static ListNode meetingNode(ListNode head) {
    if (head == null)
        return null;
    //一开始都从head出发,slow和fast相等,所以先走一步
    ListNode slow = head.next;
    //只有一个节点,不存在环
    if (slow == null)
        return null;
    ListNode fast = slow.next;
    while (fast != null && slow != null) {
        if (fast == slow)
            return fast;
        slow = slow.next;
        fast = fast.next;
        //fast多走一步
        if (fast != null)
            fast = fast.next;
    }
    return null;
}

private static ListNode entryNodeOfLoop(ListNode head) {
    ListNode meetingNode = meetingNode(head);
    if (meetingNode == null)
        return null;
    int loops = 1;
    ListNode node1 = meetingNode;
    //得到环内节点数
    while (node1.next != meetingNode) {
        node1 = node1.next;
        ++loops;
    }
    node1 = head;
    //node1先走loops步
    for (int i = 0; i < loops; i++) {
        node1 = node1.next;
    }
    //两者再以相同的速度移动
    ListNode node2 = head;
    while (node1 != node2) {
        node1 = node1.next;
        node2 = node2.next;
    }
    return node1;
}

24_反转链表

实现一个函数,输入一个头节点,反转该链表并输出反转后的头节点

ReverseList

为了保证反转的过程中链表不断开,需要维护三个指针,一个指向当前节点,一个指向当前节点的前一个节点,一个指向当前节点的后一个节点。

private static ListNode reverse(ListNode head) {
    ListNode reverseHead = null;
    ListNode curNode = head;
    ListNode preNode = null;
    while (curNode != null) {
        ListNode nextNode = curNode.next;
        if (nextNode == null)
            reverseHead = curNode;
        curNode.next = preNode;
        preNode = curNode;
        curNode = nextNode;
    }
    return reverseHead;
}

25_合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表仍然是递增排序的。

MergeSortedLists

两个链表各自维护一个指针,比较指针元素大小,小的应该先进行连接,然后将小的指针后移,重复这个过程。

private static ListNode merge(ListNode realHead1, ListNode realHead2) {
    if (realHead1 == null)
        return realHead2;
    else if (realHead2 == null)
        return realHead1;
    ListNode mergedHead;
    //链表1比链表2小,将链表1作为mergedHead,之后,将链表1的下一个节点,和链表2当前节点传入
    if (realHead1.value < realHead2.value) {
        mergedHead = realHead1;
        mergedHead.next = merge(realHead1.next, realHead2);
    } else {
        mergedHead = realHead2;
        mergedHead.next = merge(realHead1, realHead2.next);
    }
    return mergedHead;
}

26_树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。

从A的根节点开始,看是否和B的根节点相等,相等,就下去继续判断剩下的节点是否相等。不等,就用A的左右子树,作为根节点继续判断。

private static boolean hasSubtree(BinaryTreeNode root1, BinaryTreeNode root2) {
    boolean result = false;
    if (root1 != null && root2 != null) {
        if (root1.value == root2.value)
            result = doseTree1HaveTree2(root1, root2);
        if (!result)
            result = hasSubtree(root1.left, root2);
        if (!result)
            result = hasSubtree(root1.right, root2);
    }
    return result;
}

private static boolean doseTree1HaveTree2(BinaryTreeNode root1, BinaryTreeNode root2) {
    if (root2 == null)
        return true;
    if (root1 == null)
        return false;
    if (root1.value != root2.value)
        return false;
    return doseTree1HaveTree2(root1.left, root2.left) && doseTree1HaveTree2(root1.right, root2.right);
}

27_二叉树的镜像

输入一棵二叉树,输出它的镜像。

遍历该树所有节点,如果有子节点,就交换子节点。

private static void mirrorRecursively(BinaryTreeNode node) {
    if (node == null)
        return;
    if (node.left == null && node.right == null)
        return;
    //交换子节点
    BinaryTreeNode tempNode = node.left;
    node.left = node.right;
    node.right = tempNode;
    //递归交换子节点的子节点
    if (node.left != null)
        mirrorRecursively(node.left);
    if (node.right != null)
        mirrorRecursively(node.right);
}

28_对称二叉树

判断一棵二叉树是不是对称的,如果一棵二叉树跟它镜像一样,那么就是对称的。

思路比较巧妙,二叉树的三种遍历方式都是先遍历左节点,再遍历右节点,那么可不可以定义一种遍历方式,先遍历右节点,后遍历左节点呢(刚好可以用来解决对称)?如果两种方式遍历的序列一样,那么就是镜像的,当然还需要处理特殊情况,把NULL考虑进来。

private static boolean isSymmetrical(BinaryTreeNode node) {
    return isSymmetrical(node, node);
}

private static boolean isSymmetrical(BinaryTreeNode node1, BinaryTreeNode node2) {
    if (node1 == null && node2 == null)
        return true;
    if (node1 == null || node2 == null)
        return false;
    if (node1.value != node2.value)
        return false;
    return isSymmetrical(node1.left, node2.right) &&
                isSymmetrical(node1.right, node2.left);
}

29_顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针依次打印出每一个数字,从左上顶点开始。

思路就是按照“圈数”来打印,循环打印多少圈。经过分析发现,最后一圈的第一个数字,一定在(i,i)上,如果i*2 > cols && i*2 > rows就结束循环。

对于每一圈,又分成四个步骤,从左到右打印,从上到下打印,从右到左打印,从下到上打印。

从左到右打印是必须的,无论是只有一行的矩阵,还是只有一列的矩阵。

int endX = cols - 1 - start;
int endY = rows - 1 - start;
for (int i = start; i <= endX; i++) {
    System.out.printf("%d->", nums[start][i]);
}

从上到下不是必须的,如果只有一行,那么就没有这个过程。所以从上到下的条件是endY > start

if (start < endY) {
    for (int i = start + 1; i <= endY; i++) {
        System.out.printf("%d->", nums[i][endX]);
     }
}

从右到左也不是必须的,如果只有一列,那么就没有这个过程。所以从右到左的条件必须是endY > start && endX > start

if (start < endX && start < endY) {
    for (int i = endX - 1; i >= start; i--) {
        System.out.printf("%d->", nums[endY][i]);
    }
}

最后一步从下到上也不是必须的,如果只有两行,那么就没有这个过程,所以条件行数必须大于2,endX > start && endY -1 > start

if (start < endX && start < endY - 1) {
    for (int i = endY - 1; i >= start + 1; i--) {
        System.out.printf("%d->", nums[i][start]);
    }
}