交换(前提,a和b在内存里是两块独立的区域(值可以一样))(不建议使用)
int a=n;
int b=m;
a=a^b; //a=n^m
b=a^b; //b=n^m^m=n^0=n
a=a^b; //a=n^m^n=m^0=m
//在数组中要保证 i!=j 否则会被磨成0
//交换arr的i和j位置上的值
public void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
1)在一个整形数组arr[]中,只有一个数出现了奇数次,其他都出现了偶数次,怎么找到这个奇数次的数
(时间复杂度为O(N) 空间复杂度为O(1) )
int err=0;
for(int i=0;i<arr.length;++i){
err=arr[i]^err;
}
return err;
/*
[2,1,2,1,3,3,1,1,3]
在异或运算中,可以看成
[1,1,1,1,2,2,3,3,3]
[ 0 , 0 , 3 ]
*/
2)在一个整形数组arr[]中,只有两个数出现了奇数次,其他都出现了偶数次,怎么找到这两个奇数次的数
(时间复杂度为O(N) 空间复杂度为O(1) )
思路:假设err第八位为1
再创建一个err’继续异或数组,此时err’异或第八位不是1(0)的数,
a(b)==err’
int err=0;
for(int i=0;i<arr.length;++i){
err^=arr[i];
}
//err=a^b
//err!=0
//err必然有一个位置上是1
int rightOne = err&(~err+1);//取出最右边的1
/*
err=1001111
~err=0110000
~err+1=0110001
err&(~err+1)=0000001
*/
int onlyOne=0;//err'
for(int i=0;i<arr.length;++i){
if(arr[i]&rightOne!=0){
onlyOne^=arr[i];
}
}
System.out.print(onlyOne+" "+(err^onlyOne));
时间复杂度O(N^2)
额外空间复杂度O(1)
核心思想:找到数组中最小的值,放到第一位,然后以此类推
if(arr==null||arr.length<2){
return;
}
for(int i=0;i<arr.length;++i){
int maxIdex=i;
for(int j=i+1;j<arr.length;++i){//找i~N-1上最小值的下标
minIndex=arr[j]<arr[minIndex]?j:minIndex;
}
swap(arr,i,minIdex);
}
public static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
时间复杂度O(N^2)
额外空间复杂度O(1)
核心思想:
[5,8,6,84,3,853,5,54,6,3,31,1]
if(arr==null||arr.length<2){
return;
}
for(int i=arr.length-1;i>0;--i){
for(int j=0;j<i;++i){//0~i
if(arr[j]>arr[j+1]){
swap(arr,i,i+1);
}
}
}
时间复杂度O(N^2)~O(N)
额外空间复杂度O(1)
核心思想:将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表
if(arr==null||arr.length<2){
return;
}
//0~0有序
//0~i想有序
for(int i=1;i>arr.length;++i){//0~i做到有序
for(int j=i-1;j>=0&&arr[j]>arr[j+1];--j){
swap(arr,i,i+1);
}
}
求中值:mid=L+(R-L)/2
//arr[L....R]范围上求最大值
public static int process(int[] arr,int L,int R){
if(L==R){//arr[L...R]范围上只有一个数,直接返回,base case
return arr[L];
}
int mid=L+((R-L)>>1);//中点
int leftMax=process(arr,L,mid);
int rightMax=process(arr,mid,R);
return Math.max(leftMax,rightMax);
}
T(N)=a*T(N/b)+O(N^d)
时间复杂度 O(N*logN)
额外空间复杂度O(N)
public static void mergeSort(int[] arr){
if(arr==null||arr.length<2){
return arr;
}
process(arr,0,arr.length-1)
}
public static int process(int[] arr,int L,int R){
if(L==R){//arr[L...R]范围上只有一个数,直接返回,base case
return arr[L];
}
int mid=L+((R-L)>>1);//中点
process(arr,L,mid);
process(arr,mid+1,R);
merge(arr,L,mid,R);
}
public static void merge(int[] arr,int L,int mid,int R){
int[] help=new int[R-L+1];
int i=0;
int p1=L;
int p2=mid+1;
while(p1<=mid&&p2<=R){
help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
//两个while只能中一个
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=R){
help[i++]=arr[p2++];
}
for(i=0;i<help.length;i++){
arr[L+i]=help[i];
}
}
小和问题:
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和
例:[1,3,4,2,5] 1左边比1小的数,没有;3左边比3小的数,1;4左边比4小的数,1、3;2左边比2小的数,1;5左边比5小的数,1、3、4、2;所有小和为1+1+3+1+1+3+4+2=16
public static int smallSum(int[] arr){
if(arr==null||arr.length<2){
return 0;
}
return process(arr,0,arr.length-1);
}
public static int process(int[] arr,int l,int r){
if(l==r){
return 0;
}
int mid=l+((r-1)>>1);
return process(arr,l,mid)
+process(arr,mid+1,r)
+merge(arr,l,mid,r);
}
public static int merge(int[] arr,int L,int m,int r){
int[] help=new int[r-L+1];
int i=0;
int p1=L;
int p2=m+1;
int res=0;
while(p1<=m&&p2<=r){
res+=arr[p1]<arr[p2]?(r-p2+1)*arr[p1]:0;//(r-p2+1)右组当前多少个数比arr[p1]大
help[i++]=arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<m){
help[i++]=arr[p1++];
}
while(p2<m){
help[i++]=arr[p2++];
}
for(i=0;i<help.length;i++){
arr[L+i]=help[i];
}
return res;
}
public static void quickSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
quickSort(arr,0,arr.length-1);
}
//arr[1...r]排序
public static void quickSort(int[] arr,int L,int R){
if(L<R){
swap(arr,L+(int)(Math.random()*(R-L+1)),R);
int[] p=partition(arr,L,R);
quickSort(arr,L,p[0]-1);//<区
quickSort(arr,p[1]+1,R);//>区
}
}
//这是一个处理arr[1..r]的函数
//默认以arr[r]做划分,arr[r]->p p
//返回等于区域(左边界,右边界),所以返回一个长度为2的数组res,res[0] res[1]
public static int[] partition(int[] arr,int L,int R){
int less=L-1;//<区右边界
int more=R;//>区左边界
while(L<more){//L表示当前数的位置 arr[R]->划分值
if(arr[L]<arr[R]){//当前值<划分值
swap(arr,++less,L++);
}else if(arr[L]>arr[R]){//当前值>划分值
swap(arr,--more,L);
}else{
L++;
}
}
swap(arr,more,R);
return new int[]{less+1,more};
}
时间复杂度O(N*logN)
排序
建立大根堆
public static void heapSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
for(int i=0;i<arr.length;i++){//O(N)
heapInsert(arr,i);//O(logN)
}
int heapSize=arr.length;
sawp(arr,0,--heapSize);
while(heapSize>0){//o(N)
heapify(arr,0,heapSize);//O(logN)
swap(arr,0,--heapSize);//O(1)
}
}
插入
//某个数在index位置,能否往下移动
public static void heapInsert(int[] arr,int index){
while(arr[index]>arr[(index-1)/2]){
swap(arr,index,(index-1)/2);
index=(index-1)/2;
}
}
取出最大值,并变回大根堆
//某个数在index位置,能否往下移动
public static void heapify(int[] arr,int index,int heapSize){
int left=index*2+1;//左孩子的下标
while(left<heapSize){//下方还有孩子的时候
//判断有无右孩子,并判断两个孩子中,谁的值大,把下标给largest
int largest=left+1<heapSize && arr[left+1]>arr[left]?arr[left+1]:arr[left];
//父和较大的孩子之间,谁的值大,把下标给largest
largest=arr[argest]>arr[index]?largest:index;
if(largest==index){
break;
}
swap(arr,largest,index);
index=largest;
left=index*2+1;
}
}
PriorityQueue heap=new PriorityQueue<>(); 底层代码就是小根堆
public void sortedArrDistanceLessk(){
//默认小根堆
PriorityQueue<Interger> heap=new PriorityQueue<>();
int index=0;
for(;index<Math.min(arr.length,k);index++){
heap.add(arr[index]);
}
int i=0;
for(; index<arr.length;i++,index++){
heap.add(arr[index]);
arr[i]=heap.poll();
}
while(!heap.isEmpty()){
arr[i++]=heap.poll();
}
}
//返回负数的时候,第一个参数排在前面
//返回正数的时候,第二个参数排在前面
//返回0的时候,无所谓谁在前面
public int compare(Student o1,Student o2){
return o1.id-o2.id;
}
public static void radixSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
radixSort(arr,0,arr.length-1,maxbits(arr));
}
public static int maxbits(int[] arr){
int max=Interger.MIN_VALUE;
for(int i=0;i<arr.length;i++){
max=Math.max(max,arr[i]);
}
int res=0;
while(max!=0){
res++;
max/=10;
}
return res;
}
//arr[begin..end]排序
public static void radixSort(int[] arr,int L,int R,int digit){
final int radix=0;
int i=0,j=0;
//有多少数准备多少个辅助空间
int[] bucket=new int[R-L+1];
for(int d=1;d<digit;d++){//有多少位就进出几次
/*10个空间
count[0]当前位(d位)是0的数字有多少个
count[1]当前位(d位)是(0,1)的数字有多少个
count[2]当前位(d位)是(0,1,2)的数字有多少个
count[i]当前位(d位)是(0~i)的数字有多少个
*/
int[] count=new int[radix];//count[0..9]
for(i=L;i<=R;i++){
j=getDigit(arr[i],d);
count[j]++;
}
for(i=1;i<radix;i++){
count[i]=count[i]+count[i-1];
}
for(i=R;i>=L;i--){
j=getDigit(arr[i],d);
bucket[count[j]-1]=arr[i];
count[j]--;
}
for(i=L,j=0;i<=R;i++,j++){
arr[i]=bucket[j];
}
}
}
同样值的个体之间,如果不因为排序而改变相对次序,就是这个排序是具有稳定性的;否则没有
稳定对基础类型没什么用,对非基础类型有用
不稳定排序:
稳定排序
目前没有找到时间复杂度O(N*logN),额外空间复杂度O(1),又稳定的排序
时间复杂度 | 空间复杂度 | 稳定性 | |
---|---|---|---|
选择 | O(N^2) | O(1) | × |
冒泡 | O(N^2) | O(1) | √ |
插入 | O(N^2) | O(1) | √ |
归并 | O(N*logN) | O(N) | √ |
快排(随) | O(N*logN) | O(logN) | × |
堆 | O(N*logN) | O(1) | × |
能用快排就用快排
有空间要求用堆排
归并具有稳定性
基于比较的排序能不能做到时间在 O(N*logN)以下?
目前没有
能不能做到时间复杂度 O(N*logN),空间在O(N)以下,具有稳定性?
目前没有
常见的坑:
有序表的固定操作
题目给定两个可能有环也可能无环的单链表,头节点head1和head2。请实 现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返 回null
要求如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。
解决第一个问题,判断有无环:
1、用哈希表
用哈希表解决——set
将结点一个一个入到哈希表中,判断有无相同结点,有就是一个环并且是第一个相同的结点就是入环结点
2、快慢指针
快慢指针
快慢指针都在第一个节点
快指针走两步,慢指针走一步,当他们相遇了就是有环,他们不会在环里转超过两圈
相遇了之后,快指针回到第一个结点,一次走一步,慢指针停在原地,接着一次走一步,他们在相遇一定是环的第一个节点loop
(设环外m,环内n节点,相遇时慢指针在环内走k步。此时慢走m+k步,快比慢多走m+k(快在绕圈圈等着慢),结论(m+k)%n=0。快回起点走m到环入口,此时慢在环内走了k+m步%n=0,即在环入口相遇)
判断是否相交
判断两个都是无环是否相交:
判断是否再同一个内存里面
假设head1,head2
head1: len1=m,end1
head2: len2=n,end2
让长节点走差值,然后短节点在开始走,最后相交的一定是第一个节点
判断一个有环,一个无环是否相交
不可能相交的
两个都有环
先序(头左右)
后序(左右头)
中序(左头右)
递归
public static void inOrderRecur(Node head){
if(head==null){
return;
}
inOrderRecur(head.left);
System.out.print(head.value+" ");
inOrderRecur(head.right);
}
非递归
public static void inOrderRecur(Node head){
System.out.print("in-order");
if(head!=null){
Stack<Node> stack=new Stack<>();
while(!stack.isEmpty()||head!=null){
if(head!=null){
stack.push(head);
head=head.left;
}else{
head=stack.pop();
System.out.print(head.value+" ");
head=head.right;
}
}
}
}
如何直观打印一颗二叉树
prinThree(head)
返回二叉树最大宽度
public static void w(Node head){
if(head==null){
return;
}
Queue<Node> queue=new LinkedList<>();
queue.add(head);
HashMap<Node,Integer> leveMap=new HashMap<>();//存放每个节点再那一层
levelMap.put(head,1);
int curLevel=1;//当前节点再那一层
int curLevelNodes=0;//当前层发现几个节点
int max=Integer.MIN_VALUE//全局某一层最大节点数,让当前值处于全局最小
while(!queue.isEmpty){
Node cur=queue.poll();
int curNodeLevel=levelMap.get(cur);//获取弹出节点层数
if(curNodeLevel==curLevel){
curLevelNodes++;
}else{
max=Math.max(max,curLevelNodes);//上一层可以结算了,更新max
curLevel++;
curLevelNodes=1;
}
if(cur.left!=null){
leveMap.put(cur.left,curNodeLevel+1);
queue.add(cur.left);
}
if(cur.right!=null){
leveMap.put(cur.right,curNodeLevel+1);
queue.add(cur,right);
}
}
max=Math.max(max,curLevelNodes);
return max;
}
搜索二叉树:对于每一个子树来说,左孩子比父节点小,右孩子比父节点大
中序遍历全部都是升序就是搜索二叉树
public statc int perValue=Integer.MIN_VALUE;
public static boolean isBST(Node head){
if(head==null){
return true;
}
boolean isleftBst=isBST(head.left);
if(!isleftBst){
return false;
}
if(head.value<=preValue){
return false;
}else{
perValue=head.value;
}
return isBST(head.right);
}
public static boolean inOrderRecur(Node head){
System.out.print("in-order");
int perValue=Integer.MIN_VALUE;
if(head!=null){
Stack<Node> stack=new Stack<>();
while(!stack.isEmpty()||head!=null){
if(head!=null){
stack.push(head);
head=head.left;
}else{
head=stack.pop();
if(head.value<=preValue){
return false;
}else{
perValue=head.value;
}
head=head.right;
}
}
}
return ture;
}
public static class ReteurnData{
public boolean isBST;
public int min;
public int max;
public ReteurnData(boolean isB,int min,int max){
isBST=isB;
min=min;
max=max;
}
}
public static ReteurnData process(Node x){
if(x==null){
return null;
}
ReteurnData leftData=process(x.left);
ReteurnData rightData=process(x.right);
int min=x.value;
int max=x.value;
if(leftData!=null){
min=Math.min(min,leftDdata.min);
max=Math.max(max,leftDdata.max);
}
if(rightData!=null){
min=Math.min(min,rightData.min);
max=Math.max(max,rightData.max);
}
boolean isBST =true;
if(leftData!=null && (leftData.isBST||leftData.max<=x.value)){
isBST=false;
}
if(rightData!=null && (rightData.isBST||rightData.min>=x.value)){
isBST=false;
}
return new ReteurnData(isBST,min,max);
}
public static boolean isCBT(Node head){
if(head==null){
return true;
}
List<Node> queue=new LinkedList<>();
//是否遇到左右孩子不全情况
boolean leaf=false;
Node l=null;
Node r=null;
list.add(head);
while(queue.isEmpty){
Node head=queue.pop();
l=head.left;
r=head.right;
if((leaf && (r!=null || l!=null)) || (r!=null && l==null)){
return false;
}
if(r==null && l!=null){
leaf=true;
}
if(l!=null){
queue.add(l)
}
if(r!=null){
queue.add(r);
}
}
return true;
}
最大深度:c
节点数:N
N=2^c-1
public static boolean isF(Node head){
if(head==null){
return true;
}
Info data=process(head);
return data.nodes==(1<<(data.height)-1);
}
public static class Info{
public int height;
public int nodes;
public Info(int hei,int nod){
height=hei;
nodes=nod;
}
}
public static Info ptocess(Node x){
if(x==null){
return new Info(0,0);
}
Info lefeData=process(x.left);
Info rightData=process(x.right);
int height=Math.max(leftData.height,rightData.heighgt)+1;
int nodes=leftData.nodes+rightData.nodes+1;
return new Indo(height,nodes);
}
public static boolean isBalanced(Node head){
return process(head).isBalanced;
}
public static class ReturnType{
public boolean isBalanced;
public int heignt;
public ReturnType(boolean isB,int heignt){
isBalanced=isB;
heignt=heignt;
}
}
public static ReturnType process(Node x){
if(x==null){
return new ReturnType(true,0);
}
ReturnType leftData=process(x.left);
ReturnType rightData=process(x.right);
int height=Math.max(leftData.height,rightData.height)+1;
boolean isBalanced=leftData.height&&rightData.height&&Math.abs(leftData.height-rightData.height)<2;
return new ReturnType(isBalanced,height);
}
给定两个二叉树的节点node1和node2,找到他们的最低公共祖先节点
//o1,o2一定属于head为头的二叉树
//返回o1,o2的最低公共祖先
public static Node lca(Node head,Node o1,Node 02){
HashMap<Node,Node> fatherMap=new HashMap<>();
fatherMap.put(head,head);
process(head,fatherMap);
HashSet<Node> set01=new HashSet<>();
set01.add(o1);
Node cur=o1;
while(cur!=fatherMap.get(cur)){
set01.add(cur);
cur=fatherMap.get(cur);
}
set.add(head);
cur=o2;
while(cur!=fatherMap.get(cur)){
cur=fatherMap.get(cur);
if( set01.contains(cur)){
return cur;
}
}
}
public static void process(Node head, HashMap<Node,Node> fatherMap){
if(head==null){
return;
}
fatherMap.put(head.left,head);
fatherMap.put(head.right,head);
process(head.left,fatherMap);
process(head.right,fatherMap);
}
在K二MP叉算树法中扩找展到题一目个节二点的后继节点
【题目】 现在有一种新的二叉树节点类型如下:
public class Node {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int val) {
value = val;
}
}
该结构比普通二叉树节点结构多了一个指向父节点的parent指针。
假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节 点的parent指向null。
只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。
在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。
public static Node getSuccessorNode(Node node){
if(node==null){
return node;
}
if(node.right!=null){
return getLeftMost(node.left);
}else{//无右子树
Node parent=node.parent;
while(parent!=null&&parent.left!=node){//当前节点是其父亲节点右孩子
node=parent;
parent=node.parent;
}
return parent;
}
}
public static NodegetLeftMost(Node node){
if(node==null){
return node;
}
while(node.left!=null){
node=node.left;
}
return node;
}
二叉树的序列化和反序列化
就是内存里的一棵树如何变成字符串形式,又如何从字符串形式变成内存里的树 如何判断一颗二叉树是不是另一棵二叉树的子树?
public static Node reconByPreString(String preStr) {
String[] values = preStr.split("_");
Queue<String> queue = new LinkedList<String>();
for (int i = 0; i != values.length; i++) {
queue.offer(values[i]);
}
return reconPreOrder(queue);
}
public static Node reconPreOrder(Queue<String> queue) {
String value = queue.poll();
if (value.equals("#")) {
return null;
}
Node head = new Node(Integer.valueOf(value));
head.left = reconPreOrder(queue);
head.right = reconPreOrder(queue);
return head;
}
折纸问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后 展开。
此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。
如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从 上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。
请从上到下打印所有折痕的方向。 例如:N=1时,打印: down N=2时,打印: down down up
public static void printAllFolds(int N) {
printProcess(1, N, true);
}
public static void printProcess(int i, int N, boolean down) {
if (i > N) {
return;
}
printProcess(i + 1, N, true);
System.out.println(down ? "down " : "up ");
printProcess(i + 1, N, false);
}
public static void main(String[] args) {
int N = 1;
printAllFolds(N);
}
Node
public class Node {
public int value;
public int in;
public int out;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges;
public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
创建图
public class GraphGenerator {
public static Graph createGraph(Integer[][] matrix) {
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
Integer weight = matrix[i][0];
Integer from = matrix[i][1];
Integer to = matrix[i][2];
if (!graph.nodes.containsKey(from)) {
graph.nodes.put(from, new Node(from));
}
if (!graph.nodes.containsKey(to)) {
graph.nodes.put(to, new Node(to));
}
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
Edge newEdge = new Edge(weight, fromNode, toNode);
fromNode.nexts.add(toNode);
fromNode.out++;
toNode.in++;
fromNode.edges.add(newEdge);
graph.edges.add(newEdge);
}
return graph;
}
}
package class06;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
public class Code01_BFS {
public static void bfs(Node node) {
if (node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
HashSet<Node> map = new HashSet<>();
queue.add(node);
map.add(node);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.value);
for (Node next : cur.nexts) {
if (!map.contains(next)) {
map.add(next);
queue.add(next);
}
}
}
}
}
package class06;
import java.util.HashSet;
import java.util.Stack;
public class Code02_DFS {
public static void dfs(Node node) {
if (node == null) {
return;
}
Stack<Node> stack = new Stack<>();
HashSet<Node> set = new HashSet<>();
stack.add(node);
set.add(node);
System.out.println(node.value);
while (!stack.isEmpty()) {
Node cur = stack.pop();
for (Node next : cur.nexts) {
if (!set.contains(next)) {
stack.push(cur);
stack.push(next);
set.add(next);
System.out.println(next.value);
break;
}
}
}
}
}
适用范围:要求有向图,且有入度为0的节点,且没有环
package class06;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Code03_TopologySort {
// directed graph and no loop
public static List<Node> sortedTopology(Graph graph) {
//key:某一个node
//value:剩下的入度
HashMap<Node, Integer> inMap = new HashMap<>();
//入度为0的点,才能进这个队列
Queue<Node> zeroInQueue = new LinkedList<>();
for (Node node : graph.nodes.values()) {
inMap.put(node, node.in);
if (node.in == 0) {
zeroInQueue.add(node);
}
}
//拓扑排序结果,依次加入result
List<Node> result = new ArrayList<>();
while (!zeroInQueue.isEmpty()) {
Node cur = zeroInQueue.poll();
result.add(cur);
for (Node next : cur.nexts) {
inMap.put(next, inMap.get(next) - 1);
if (inMap.get(next) == 0) {
zeroInQueue.add(next);
}
}
}
return result;
}
}
适用范围:要求无向图
package class06;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
//undirected graph only
public class Code04_Kruskal {
// Union-Find Set
public static class UnionFind {
private HashMap<Node, Node> fatherMap;
private HashMap<Node, Integer> rankMap;
public UnionFind() {
fatherMap = new HashMap<Node, Node>();
rankMap = new HashMap<Node, Integer>();
}
private Node findFather(Node n) {
Node father = fatherMap.get(n);
if (father != n) {
father = findFather(father);
}
fatherMap.put(n, father);
return father;
}
public void makeSets(Collection<Node> nodes) {
fatherMap.clear();
rankMap.clear();
for (Node node : nodes) {
fatherMap.put(node, node);
rankMap.put(node, 1);
}
}
public boolean isSameSet(Node a, Node b) {
return findFather(a) == findFather(b);
}
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node aFather = findFather(a);
Node bFather = findFather(b);
if (aFather != bFather) {
int aFrank = rankMap.get(aFather);
int bFrank = rankMap.get(bFather);
if (aFrank <= bFrank) {
fatherMap.put(aFather, bFather);
rankMap.put(bFather, aFrank + bFrank);
} else {
fatherMap.put(bFather, aFather);
rankMap.put(aFather, aFrank + bFrank);
}
}
}
}
public static class EdgeComparator implements Comparator<Edge> {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> kruskalMST(Graph graph) {
UnionFind unionFind = new UnionFind();
unionFind.makeSets(graph.nodes.values());
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
for (Edge edge : graph.edges) {//M条边
priorityQueue.add(edge);//O(logM)
}
Set<Edge> result = new HashSet<>();
while (!priorityQueue.isEmpty()) {//M条边
Edge edge = priorityQueue.poll();//O(logM)
if (!unionFind.isSameSet(edge.from, edge.to)) {//O(1)
result.add(edge);
unionFind.union(edge.from, edge.to);
}
}
return result;
}
}
适用范围:要求无向图
package class06;
import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
// undirected graph only
public class Code05_Prim {
public static class EdgeComparator implements Comparator<Edge> {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> primMST(Graph graph) {
//解锁的边进入小根堆
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
HashSet<Node> set = new HashSet<>();
Set<Edge> result = new HashSet<>();//依次挑选的边在result里
for (Node node : graph.nodes.values()) {//随便挑选了一个点
//node是开始点
if (!set.contains(node)) {
set.add(node);
for (Edge edge : node.edges) {//由一个点,解锁所有相连的边
priorityQueue.add(edge);
}
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll();//弹出解锁的边中,最小的边
Node toNode = edge.to;//可能的一个新的点
if (!set.contains(toNode)) {//不含有的时候,
set.add(toNode);
result.add(edge);
for (Edge nextEdge : toNode.edges) {
priorityQueue.add(nextEdge);
}
}
}
}
}
return result;
}
// 请保证graph是连通图
// graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
// 返回值是最小连通图的路径之和
public static int prim(int[][] graph) {
int size = graph.length;
int[] distances = new int[size];
boolean[] visit = new boolean[size];
visit[0] = true;
for (int i = 0; i < size; i++) {
distances[i] = graph[0][i];
}
int sum = 0;
for (int i = 1; i < size; i++) {
int minPath = Integer.MAX_VALUE;
int minIndex = -1;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] < minPath) {
minPath = distances[j];
minIndex = j;
}
}
if (minIndex == -1) {
return sum;
}
visit[minIndex] = true;
sum += minPath;
for (int j = 0; j < size; j++) {
if (!visit[j] && distances[j] > graph[minIndex][j]) {
distances[j] = graph[minIndex][j];
}
}
}
return sum;
}
public static void main(String[] args) {
System.out.println("hello world!");
}
}
适用范围:没有权值为负数的边
package class06;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
// no negative weight
public class Code06_Dijkstra {
public static HashMap<Node, Integer> dijkstra1(Node head) {
//从head出发到所有结点的最小距离
//key:从head出发到达key
//value:从head出发到key的最小距离
//如果在表中,没有T的记录,含义是从head出发到T这个点的距离为正无穷
HashMap<Node, Integer> distanceMap = new HashMap<>();
distanceMap.put(head, 0);
//已经求过距离的节点,存在selectedNodes中,以后再也不碰
HashSet<Node> selectedNodes = new HashSet<>();
Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
while (minNode != null) {
int distance = distanceMap.get(minNode);
for (Edge edge : minNode.edges) {
Node toNode = edge.to;
if (!distanceMap.containsKey(toNode)) {
distanceMap.put(toNode, distance + edge.weight);
}
distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
}
selectedNodes.add(minNode);
minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
}
return distanceMap;
}
public static Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap,
HashSet<Node> touchedNodes) {
Node minNode = null;
int minDistance = Integer.MAX_VALUE;
for (Entry<Node, Integer> entry : distanceMap.entrySet()) {
Node node = entry.getKey();
int distance = entry.getValue();
if (!touchedNodes.contains(node) && distance < minDistance) {
minNode = node;
minDistance = distance;
}
}
return minNode;
}
public static class NodeRecord {
public Node node;
public int distance;
public NodeRecord(Node node, int distance) {
this.node = node;
this.distance = distance;
}
}
public static class NodeHeap {
private Node[] nodes;
private HashMap<Node, Integer> heapIndexMap;
private HashMap<Node, Integer> distanceMap;
private int size;
public NodeHeap(int size) {
nodes = new Node[size];
heapIndexMap = new HashMap<>();
distanceMap = new HashMap<>();
this.size = 0;
}
public boolean isEmpty() {
return size == 0;
}
public void addOrUpdateOrIgnore(Node node, int distance) {
if (inHeap(node)) {
distanceMap.put(node, Math.min(distanceMap.get(node), distance));
insertHeapify(node, heapIndexMap.get(node));
}
if (!isEntered(node)) {
nodes[size] = node;
heapIndexMap.put(node, size);
distanceMap.put(node, distance);
insertHeapify(node, size++);
}
}
public NodeRecord pop() {
NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
swap(0, size - 1);
heapIndexMap.put(nodes[size - 1], -1);
distanceMap.remove(nodes[size - 1]);
nodes[size - 1] = null;
heapify(0, --size);
return nodeRecord;
}
private void insertHeapify(Node node, int index) {
while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private void heapify(int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
? left + 1 : left;
smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
if (smallest == index) {
break;
}
swap(smallest, index);
index = smallest;
left = index * 2 + 1;
}
}
private boolean isEntered(Node node) {
return heapIndexMap.containsKey(node);
}
private boolean inHeap(Node node) {
return isEntered(node) && heapIndexMap.get(node) != -1;
}
private void swap(int index1, int index2) {
heapIndexMap.put(nodes[index1], index2);
heapIndexMap.put(nodes[index2], index1);
Node tmp = nodes[index1];
nodes[index1] = nodes[index2];
nodes[index2] = tmp;
}
}
public static HashMap<Node, Integer> dijkstra2(Node head, int size) {
NodeHeap nodeHeap = new NodeHeap(size);
nodeHeap.addOrUpdateOrIgnore(head, 0);
HashMap<Node, Integer> result = new HashMap<>();
while (!nodeHeap.isEmpty()) {
NodeRecord record = nodeHeap.pop();
Node cur = record.node;
int distance = record.distance;
for (Edge edge : cur.edges) {
nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
}
result.put(cur, distance);
}
return result;
}
}
y(Node node, int index) {
while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private void heapify(int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
? left + 1 : left;
smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
if (smallest == index) {
break;
}
swap(smallest, index);
index = smallest;
left = index * 2 + 1;
}
}
private boolean isEntered(Node node) {
return heapIndexMap.containsKey(node);
}
private boolean inHeap(Node node) {
return isEntered(node) && heapIndexMap.get(node) != -1;
}
private void swap(int index1, int index2) {
heapIndexMap.put(nodes[index1], index2);
heapIndexMap.put(nodes[index2], index1);
Node tmp = nodes[index1];
nodes[index1] = nodes[index2];
nodes[index2] = tmp;
}
}
public static HashMap dijkstra2(Node head, int size) {
NodeHeap nodeHeap = new NodeHeap(size);
nodeHeap.addOrUpdateOrIgnore(head, 0);
HashMap result = new HashMap<>();
while (!nodeHeap.isEmpty()) {
NodeRecord record = nodeHeap.pop();
Node cur = record.node;
int distance = record.distance;
for (Edge edge : cur.edges) {
nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
}
result.put(cur, distance);
}
return result;
}
}