编码技巧之循环【笔记】
循环控制
在循环中要定义一个循环不变式
循环不变式是一句断言定义各变量所满足的条件
与递归不同,在递归里面断言就是证明一个函数,而在循环中,是没法直接看到的,是心中默认的一个条件
循环的书写方法
定义一个循环不变式,并在循环体每次结束后保持循环不变式
和递归一样,先一般情况,然后再特殊情况
每次必须向前推进循环不变式中涉及的变量值,且每次推进的规模不能多,必须为1
例题一:链表反转
将1->2->3->4->5->null变成null<-1<-2<-3<-4<-5
与递归的想法不同,循环的话不能将第一个单独提出来,因为这样要考虑很多的边界问题,因此可以在链表中间切一刀,将前三个反转以后可以发现就是头和尾变化了,此时可以设置两个变量来维持
其中newhead指向反转成功的链表,currenthead指向还没有成功反转的链表,这就是循环不变式,每次循环都必须遵守,推进以后
只要当newhead从null移动到5的时候,currenthead移动到null的时候,就可以确定整个链表反转完成了
具体代码如下:
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;
curHead = next;
// 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))));
System.out.println("done");
}
}
例题二:链表中的节点删除(delete_if)
将1->2->3->2->5->null中的所有数值为2的节点全部删除,删除以后变为1->3->5->null
设置一个变量previous指向对应的节点,在删除后面的节点以后previous不需要移动,不然在遇到两个连续的需要删除的节点的时候会错过一个
循环不变式是要保证所有的数值为2的节点能够被正确的删除
如果头结点没有previous怎么办?
进行特殊处理或者增加虚拟的头结点
具体代码如下:
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();
}
}
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));
}
}