面试中常考的循环问题
一、循环控制
循环不变式(loop invariant)
循环书写方法
1.定义循环不变式,并在循环体每次结束后保持循环不变式
2.先一般、后特殊
3.每次 必须 向前推进循环不变式中涉及的变量值
4.每次推进的规模必须为 1
例 1.链表反转
步骤:
1.定义循环不变式
【在链表中间"切一刀",循环进行到一半、切在 3 和 4 之间,循环了3次,已经将1、2、3这几个节点处理完毕了,4、5还没有动,这就是循环,前几个节点处理完毕后,形态如下图所示】
2.newHead 指向反转成功的链表、currentHead 指向还没有反转的链表
3.将 newHead 和 currentHead 向前推进 1 ;
4. newHead 必须指向已经反转完成的链表的头,但什么都没有做,已经反转完成链表的头是 Null,故 newHead 指向 Null;
current 指向还没有进行反转的链表的头,但此时还没有进行反转,故 current 指向的是 1 ;
上述满足循环不变式
5.开始向右推进,推进到 newHead 指向 5 , currentHead 指向 1;
代码实现:LinkedListReverser.java
package interview.loop;
import java.util.ArrayList;
import java.util.Arrays;
import interview.common.Node;
import interview.recursion.LinkedListCreator;
public class LinkedListReverser {
public <T> Node<T> reverseLinkedList(Node<T> head) {
Node<T> newHead = null;
Node<T> curHead = head;
// Loop invariant:
// newHead points to the linked list already reversed.
// curHead points to the linked list not yet reversed.
// Loop invariant holds.
while(curHead != null) { //正常工作条件
// Loop invariant holds.
Node<T> next = curHead.getNext();
curHead.setNext(newHead); //成功反转
newHead = curHead; //将 newHead 右移
curHead = next; //将 curHead 右移
// Loop invariant holds.
}
// Loop invariant holds.
return newHead;
}
public static void main(String[] args) {
LinkedListCreator creator = new LinkedListCreator();
LinkedListReverser reverser = new LinkedListReverser();
Node.printLinkedList(reverser.reverseLinkedList( //测试用例
creator.createLinkedList(new ArrayList<>())));
Node.printLinkedList(reverser.reverseLinkedList(
creator.createLinkedList(Arrays.asList(1))));
Node.printLinkedList(reverser.reverseLinkedList(
creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5))));
reverser.reverseLinkedList(
creator.createLargeLinkedList(1000000));
System.out.println("done");
}
}
输出:
例 2 链表中删除节点
将所有元素为 2 的节点删除掉
删除后
假设第一个 2 已经被删除掉了
删除第二个 2 :将 currentNext 指向 5 即可,3 的继承指向 5 即可;
要将 3 的 next 的指针指向 5 ,首先要有一个指针 previous 指向 5
然后 jvm 的垃圾回收机制会将 2 销毁掉
问题:头节点没有 previous 怎么办?
1.特殊处理
2.增加虚拟头节点
代码实现:LinkedListDeletor.java
package interview.loop;
import java.util.ArrayList;
import java.util.Arrays;
import interview.common.Node;
import interview.recursion.LinkedListCreator;
public class LinkedListDeletor {
public <T> Node<T> deleteIfEquals(Node<T> head, T value) {
while (head != null && head.getValue() == value) {
head = head.getNext();
}
if (head == null) {
return null;
}
Node<T> prev = head;
// Loop invariant: list nodes from head up to prev has been
// processed. (Nodes with values equal to value are deleted.)
while(prev.getNext() != null) {
if (prev.getNext().getValue() == value) {
// delete it
prev.setNext(prev.getNext().getNext());
} else {
prev = prev.getNext(); //不需要删除则将 prev 向后移动一个
}
}
return head;
}
public static void main(String[] args) {
LinkedListCreator creator = new LinkedListCreator();
LinkedListDeletor deletor = new LinkedListDeletor();
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(1, 2, 3, 2, 5)),
2));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(1, 2, 3, 2, 2)),
2));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(1, 2, 3, 2, 2)),
1));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(2, 2, 3, 2, 2)),
2));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(2, 2, 2, 2, 2)),
2));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(2)),
2));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(Arrays.asList(2)),
1));
Node.printLinkedList(deletor.deleteIfEquals(
creator.createLinkedList(new ArrayList<Integer>()),
1));
}
}
输出: