Dijkstra算法优化
原Dijkstra中,从distanceMap中获取未被固定的点时,我们通过遍历比较拿到最小的距离最小的节点,这里的时间复杂度是O(N^2),我们可以利用小根堆将其优化为 O(n*logn)
public static Map<Node<Integer, Integer>, Integer> dijkstra2(Graph<Integer, Integer> graph, Node<Integer, Integer> startNode) {
NodeHeap nodeHeap = new NodeHeap(graph.getNodes().size());
NodeRecord originPoint = new NodeRecord(startNode, 0);
nodeHeap.addOrUpdateOrIgnore(originPoint);
while (nodeHeap.size != 0){
NodeRecord<Integer, Integer> topRecord = nodeHeap.pop();
int jumpNodeDistance = topRecord.distance;
for(Edge<Integer> edge : topRecord.node.getEdges()){
NodeRecord<Integer, Integer> neighborNode = new NodeRecord(edge.getTo(), jumpNodeDistance + edge.getWeight());
nodeHeap.addOrUpdateOrIgnore(neighborNode);
}
}
return nodeHeap.distanceMap;
}
static class NodeRecord<N, E>{
Node<N, E> node;
int distance;
public NodeRecord(Node node, int distance){
this.node = node;
this.distance = distance;
}
}
/**
* 小根堆
*/
static class NodeHeap{
//实际节点的堆
NodeRecord<Integer, Integer>[] nodes;
//堆中 node对应的下标
Map<Node<Integer, Integer>, Integer> nodeIndexMap;
Map<Node<Integer, Integer>, Integer> distanceMap;
//堆中生效的节点数量
int size;
public NodeHeap(int size){
this.nodes = new NodeRecord[size];
this.nodeIndexMap = new HashMap<>();
this.distanceMap = new HashMap<>();
this.size = 0;
}
public NodeRecord pop(){
if(size == 0){
return null;
}
NodeRecord topNode = this.nodes[0];
this.nodes[0] = this.nodes[size-1];
size--;
int index = 0;
while (index*2+1 < size){
if(nodes[index].distance > nodes[index*2 +1].distance){
index = index*2+1;
continue;
}else if((index*2 + 2) < size && nodes[index].distance > nodes[index*2 +2].distance){
index = index*2+2;
continue;
}else {
break;
}
}
swap(0, index);
nodeIndexMap.put(topNode.node, -1);
return topNode;
}
public void addOrUpdateOrIgnore(NodeRecord nodeRecord){
if(!nodeIndexMap.containsKey(nodeRecord.node)){
//节点原本不存在,新增节点到小根堆内
addNode(nodeRecord);
}else if(nodeIndexMap.get(nodeRecord.node) == -1){
return;
}else{
int distance = distanceMap.get(nodeRecord.node);
if(distance > nodeRecord.distance){
distanceMap.put(nodeRecord.node, nodeRecord.distance);
}
}
}
private void addNode(NodeRecord nodeRecord){
int index = size;
nodes[index] = nodeRecord;
size++;
while (nodes[index].distance < nodes[(index-1)/2].distance){
NodeRecord parent = nodes[(index-1)/2];
if(nodeRecord.distance < parent.distance){
NodeRecord tem = parent;
nodes[(index-1)/2] = nodes[index];
nodes[index] = tem;
index = (index -1)/2;
}else{
break;
}
}
distanceMap.put(nodeRecord.node, nodeRecord.distance);
nodeIndexMap.put(nodeRecord.node, index);
}
private void swap(int i, int j){
nodeIndexMap.put( this.nodes[i].node, j);
nodeIndexMap.put(this.nodes[j].node, i);
NodeRecord tmp = this.nodes[i];
this.nodes[i] = this.nodes[j];
this.nodes[j] = tmp;
}
}
汉诺塔问题
遥想当年读书的时候,学到汉诺塔问题的时候,总是感觉那么的神奇不得这个思想的要领,只能死记硬背。
如今重新学习,会把它和“冰箱塞大象问题”联想在一块儿。那么,冰箱塞大象,具体分几部呢?3步,将冰箱打开,把大象放进去,关上冰箱。
那么,继续问,把长颈鹿塞进冰箱分几步呢?4步:把冰箱打开,把大象拿出来,把长颈鹿塞进去,关上冰箱门。
汉诺塔问题和这个尤为相似,主要思想就是我先忽略掉你的细节,而是从大问题本身开始。将大问题从思维上对它进行拆解,拆解成相同的子问题,而这些子问题可以通过谜之步骤进行完成。
在汉诺塔问题里面,我们将n个汉诺塔的问题拆解成。
1)将上n-1个汉诺塔从“原点”移动到“临时点”上
2)然后将第n个汉诺塔,从“原点”上移动到“目标”上
3)将在B上的n-1个汉诺塔,从“临时点”上移动到“目标点”上。
然后,将n-1个汉诺塔的移动视为一个新整体问题,重复1)、2)、3)即可
有3个汉诺塔棋子,有3个干A\B\C,将棋子从A移动到C去。步骤如下:
hanoi 1 from A move to C
hanoi 2 from A move to B
hanoi 1 from C move to B
hanoi 3 from A move to C
hanoi 1 from B move to A
hanoi 2 from B move to C
hanoi 1 from A move to C
public static void hanoi(int n, String origin, String target, String temp){
if(n<=1){
System.out.println("hanoi " + n + " from " + origin + " move to " + target);
return;
}
//1)将上n-1个汉诺塔从“原点”移动到“临时点”上;
hanoi(n-1, origin, temp, target);
// 2)然后将第n个汉诺塔,从“原点”上移动到“目标”上
System.out.println("hanoi " + n + " from " + origin + " move to " + target);
// 3)将在B上的n-1个汉诺塔,从“临时点”上移动到“目标点”上。
hanoi(n-1, temp, target, origin);
}
给定一个栈,请逆序这个栈
给你个栈,栈有n个元素,请讲栈内元素逆序。
如:栈底到栈顶元素有: 1、2、3、4、5 。逆序后,栈底到栈顶:5、4、3、2、1
要求:不使用其他额外的数据结构,利用递归将栈逆序。
思路:将栈底元素拿出来,然后放到栈顶。
public static void reverse(Stack<Integer> stack){
if(stack.empty()){
return;
}
int i = getLastElement(stack);
reverse(stack);
stack.push(i);
}
public static Integer getLastElement(Stack<Integer> stack){
Integer i = stack.pop();
if(stack.empty()){
return i;
}else{
Integer j = getLastElement(stack);
stack.push(i);
return j;
}
}