注:所有图片均来自CyC2018。
如果一个链表包含环,如何找出环的入口节点?
如何确定链表中存在环?维护两个指针,一个走得快,一个走得慢,如果走得快的遇到了走得慢的,就说明存在环。如果走得快的走到了末尾,还未遇到走得慢的,则不存在环。
如何找到环的入口?先让一个指针前进环中节点个数步,然后两者一起前进,相遇的地方即是环的入口节点。
如何找到环中节点个数?当两个指针相遇时,一定在环内部,继续向前走,每走一步就+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;
}实现一个函数,输入一个头节点,反转该链表并输出反转后的头节点
为了保证反转的过程中链表不断开,需要维护三个指针,一个指向当前节点,一个指向当前节点的前一个节点,一个指向当前节点的后一个节点。
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;
}输入两个递增排序的链表,合并这两个链表并使新链表仍然是递增排序的。
两个链表各自维护一个指针,比较指针元素大小,小的应该先进行连接,然后将小的指针后移,重复这个过程。
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;
}输入两棵二叉树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);
}输入一棵二叉树,输出它的镜像。
遍历该树所有节点,如果有子节点,就交换子节点。
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);
}判断一棵二叉树是不是对称的,如果一棵二叉树跟它镜像一样,那么就是对称的。
思路比较巧妙,二叉树的三种遍历方式都是先遍历左节点,再遍历右节点,那么可不可以定义一种遍历方式,先遍历右节点,后遍历左节点呢(刚好可以用来解决对称)?如果两种方式遍历的序列一样,那么就是镜像的,当然还需要处理特殊情况,把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);
}输入一个矩阵,按照从外向里以顺时针依次打印出每一个数字,从左上顶点开始。
思路就是按照“圈数”来打印,循环打印多少圈。经过分析发现,最后一圈的第一个数字,一定在(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]);
}
}