数据结构与算法
不相交集(并查集)与最大(小)堆的实现
不相交集
概念如下:不相交集
由于DisjointSet中各集合之间不可能存在相同的元素,那么我们不妨设计一颗树,它的所有子节点都表示它的元素,各集合之间相互并列,如果需要添加某个元素到集合下的话,将其作为该集合的子节点存入即可。
但是相应的,考虑到一般树的表示方法较为复杂(毕竟我们是为了简化不相交集的表示方式才选择的树结构),我们又可以选择其中较为简便的完全二叉树表示法,这里给出不相交集通过完全二叉树的数组实现。
代码如下 :
package ThirteenWeek;
public class DisjointSet {
static int[] node=new int[100];
private void Set(int[] n) {for(int i=0;i<n.length;i++) n[i]=-1;}
public DisjointSet(){Set(this.node);}
public DisjointSet(int[] tree){
this.node=tree;
Set(this.node);
}
/**
*
* @param tmp
* @return父节点位置
*/
public int search(int tmp){//tmp是指针
if(node[tmp] > 0) return node[tmp]=search(node[tmp]);//路径压缩,这样在下次遍历的时候可以直接通过它指向根节点而非通过父节点递归。
else return tmp;
}
/**
*
* @param tmp
* @return返回tmp所在树的大小
*/
public int value(int tmp){
if(node[tmp] > 0) return value(node[tmp]);
else return -node[tmp];
}
/**
*
* @param tamp
* @param tbmp
* @合并tamp和tbmp所在的树
*/
public void union(int tamp, int tbmp){
int a=search(tamp);
int b=search(tbmp);
if(a==b) System.out.println("There are already in the same tree...");
else{//union改进,比较让树高高的或者子节点多的接纳新树——union by Height/union by size
if(node[a] < node[b]){//a树的子节点更多
node[a]+=node[b];
node[b]=a;
}
else{
node[b]+=node[a];
node[a]=b;
}
}
}
}
检验 :
package ThirteenWeek;
public class DisjointSet_Test {
public static void main(String args[]){
DisjointSet d=new DisjointSet();
d.union(1,2);
d.union(3,4);
d.union(5,6);
d.union(1,6);
d.union(22,24);
d.union(3,26);
d.union(36,24);
System.out.println(d.search(6)); //头
System.out.println(d.value(6)); //大小
System.out.println(d.search(22)); //头
System.out.println(d.value(22)); //大小
}
}
结果 :
6
4
24
3
最大堆
概念如下:最大堆
同样是用数组实现的完全二叉树所表示的数据结构,最大堆的根节点的值最大(最小堆相反),而且相同的数据流可能会生成不同的堆,但要注意,这些堆之间是等价的,在这里需要注意最大堆的插入和删除元素的逻辑以及时间复杂度。
插入元素:
- 挨个插入,逐层上虑。
- 先整体插入数组(数据流),然后从倒数第二层开始,逐层下虑。
虽然看上去二者差别不大,但是从时间复杂度来讲,前者位O(n*log2n),后者位O(n)(准确来讲是O(2n))。
证明如下:
删除元素:
比较有意思的是如果我们删除的节点在最后一层,那倒没什么,直接删掉就是了,但如果我们删除的是节点,我们要做的是——覆盖,将这棵树(数组)最后一个元素放到当前节点的位置,删除最后一个元素,然后从覆盖的这个位置开始,对这个“最小”元素逐层下虑即可。
代码如下 :
package ThirteenWeek;
/**
* 最大堆嘿嘿嘿
* @author Hello
*
*/
public class Heap {
private int[] arr;
private int size;//既是size也是temp指针
public Heap(int maxSize){
arr=new int[maxSize+1];
arr[0]=Integer.MAX_VALUE;//数组的起始位置置特殊值,从“arr[1]”开始堆放元素
size=0;
}
public boolean isEmpty(){return size==0;}
public boolean isFull(){return size==arr.length-1;}
/**
* 挨个插入上虑,时间复杂度为O(n*log(n))
* @param data
* @return
*/
public boolean insert(int data){
if(isFull()){
System.out.println("Sorry, your heap is full...");
return false;
}
arr[++size]=data;
//开始上虑
for(int i=size;arr[i]>arr[i/2];i/=2){
int t=arr[i];
arr[i]=arr[i/2];
arr[i/2]=t;
}
return true;
}
/**
* 一起加载下虑,时间复杂度为O(2n)
* @param data
* @return
*/
public boolean insert(int[] data){
if(arr.length-1 < data.length){
System.out.println("Sorry, your data is too large...");
return false;
}
for(int i=0;i<data.length;i++) arr[++size]=data[i];//给arr赋值(初始化),不会计入排序时间复杂度中(size=data.length)
int parent=size/2;
//int floor=getFloor(parent);
//int floor_start=2*((int) Math.pow(2, floor))-1;//得到上一层的结束地址
while(parent >= 0){
FilterDown(parent);
parent--;
}
return true;
}
private void FilterDown(int f) {
// TODO Auto-generated method stub
if(f > size/2||f<=0) return;
if(2*f > size) return;
if(2*f+1 > size){
if(arr[f] < arr[2*f]) swap(f, 2*f);
return;
}
if(arr[f] > arr[2*f] && arr[f] > arr[2*f+1]) return;
if(arr[2*f] > arr[2*f+1]){
swap(f, 2*f);
FilterDown(2*f);
}
else{
swap(f, 2*f+1);
FilterDown(2*f+1);
}
}
private void swap(int f, int i) {
// TODO Auto-generated method stub
int t=arr[f];
arr[f]=arr[i];
arr[i]=t;
}
private int getFloor(int parent) {
// TODO Auto-generated method stub
for(int i=0;;i++)
if(Math.pow(2, i) <= parent && parent <= Math.pow(2, i+1)-1)
return i;
}
public int removeMax(){
if(isEmpty()){
System.out.println("Sorry, your heap is empty...");
return -1;
}
int max=arr[1];
int rep=arr[size];//是先返回arr[size]再做size--
size--;
arr[1]=rep;
int child, parent;
for(parent=1; 2*parent<=size; parent=child){
child=parent*2;//指向左子节点
if(child < size && arr[child+1] > arr[child]) child++;//如果parent有右子节点而且要比左子节点大
if(arr[parent] > arr[child]) break;//tmp与左右子节点中最大的比较,比他们都大的话就不用继续下虑了
//else
int t=arr[parent];
arr[parent]=arr[child];
arr[child]=t;
}
return max;
}
public void print(){
if(isEmpty()) System.out.println("Wao, your heap is empty...");
else if(isFull()) System.out.println("Wao, your heap is full...");
for(int i=1;i<size;i++) System.out.print(arr[i]+", ");
System.out.println();
}
}
检验 :
package ThirteenWeek;
public class Heap_Test {
public static void main(String args[]){
int[] b= {10,8,4,5,9,15,11,20};
Heap h=new Heap(b.length);
for(int i=0;i<b.length;i++) {
h.insert(b[i]);
}
System.out.println("插入后形成最大堆:");
h.print();
//下面测试删除后是否还是最大堆
for(int i=0;i<2;i++) {
System.out.println("删除元素:"+h.removeMax());
}
System.out.println("删除后是否还是最大堆:");
h.print();
//System.out.println(9/2);//=4
Heap h2=new Heap(b.length);
h2.insert(b);
System.out.println("插入后形成最大堆:");
h2.print();
//下面测试删除后是否还是最大堆
for(int i=0;i<2;i++) {
System.out.println("删除元素:"+h2.removeMax());
}
System.out.println("删除后是否还是最大堆:");
h2.print();
}
}
结果:
插入后形成最大堆:
Wao, your heap is full…
20, 15, 11, 9, 8, 4, 10,
删除元素:20
删除元素:15
删除后是否还是最大堆:
11, 9, 10, 5, 8,
插入后形成最大堆:
Wao, your heap is full…
20, 10, 15, 8, 9, 4, 11,
删除元素:20
删除元素:15
删除后是否还是最大堆:
11, 10, 5, 8, 9,