并查集

来源:互联网 发布:深圳java工程师招聘 编辑:程序博客网 时间:2024/05/22 17:06

并查集

并查集能很好地解决网络中两个节点是否连接的问题,但并查集不会给出节点之间的路径。正因为如此,并查集在判断节点的连通性时效率很高。其时间复杂度为O(1)。

并查集实现

思路:

  • 相连的节点具有相同的id值
  • 要实现p,q两个节点的合并,就需要将所有与p节点id值相同的节点的id改为q的id。
function unionFind(n){    this.id = [];    for (var i = 0; i < n; i++) {        //初始化时,每个节点的id值都不相同        this.id[i] = i;    }    this.find = function(p){        if (p >= 0 && p < this.id.length) {            return this.id[p];        }    }    this.isConnected = function(p,q){        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        var pId = this.id[p];        var qId = this.id[q];        if (pId == qId) {            return;        }        for (var i = 0; i < this.id.length; i++) {            if (this.id[i] == pId) {                this.id[i] = qId;            }        }    }}

并查集优化(1)

思路:

  • 为每个元素指定父节点, 初始化时每个元素的父节点都指向自身
  • 两个元素相连可以看做是一个元素是另一个元素的父节点
function unionFind2(n){    this.parent = [];    for (var i = 0; i < n; i++) {        //初始化时父节点等于自身        this.parent[i] = i;    }    this.find = function(p){        if (p < 0 || p >= this.parent.length) {            return -1;        }        //获得元素p的根节点        while(parent[p] != p ){            p = parent[p];        }        return p;    }    this.isConnected = function(p,q){        //两个元素根节点相等即代表相连        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        int pParent = find[p];        int qParent = find[q];        if (pParent == qParent) {            return;        }        //将一个元素的父节点设置为另一个元素,则代表两个元素相连        this.parent[pParent] = qParent;    }}

并查集优化(2)

思路:

  • 如果节点之间相连所形成的树层数太高的话,查到元素的根节点所耗费的时间会增加
  • 增加一个数组,用于记录以每个节点为根节点的树中子节点的数量
  • 每次合并元素之前,先判断一下待合并的元素所在的根节点哪个节点数量多,将数量少的根节点的父节点设置为数量多的根节点,并维护子节点数量数组
function unionFind2(n){    this.parent = [];    this.size = [];    for (var i = 0; i < n; i++) {        this.parent[i] = i;        //初始化时,每个节点的根节点都是自身,因此,size数量都为1        this.size[i] = 1;    }    this.find = function(p){        if (p < 0 || p >= this.parent.length) {            return -1;        }        while(parent[p] != p ){            p = parent[p];        }        return p;    }    this.isConnected = function(p,q){        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        int pParent = find[p];        int qParent = find[q];        if (pParent == qParent) {            return;        }        //合并时判断哪个分支数量多,将数量少的父节点设置为数量多的        if (this.size[pParent] > this.size[qParent]) {            this.parent[qParent] = pParent;            this.size[pParent] += this.size[qParent];        }else{            this.parent[pParent] = qParent;            this.size[qParent] += this.size[pParent];        }     }}

并查集优化(3)

思路:

  • 用节点数量判断应该怎样连接两个分支是不准确的,因为节点数量不能代表一个分支的节点层数
  • 用节点层数来判断怎样合并两个分支更加合理
function unionFind2(n){    this.parent = [];    //rank表示以每个节点为根节点的分支树的高度    this.rank = [];    for (var i = 0; i < n; i++) {        this.parent[i] = i;        //初始化时每个分支的高度都为1        this.rank[i] = 1;    }    this.find = function(p){        if (p < 0 || p >= this.parent.length) {            return -1;        }        while(parent[p] != p ){            p = parent[p];        }        return p;    }    this.isConnected = function(p,q){        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        int pParent = find[p];        int qParent = find[q];        if (pParent == qParent) {            return;        }        //将层数少的分支的父节点设置为层数大的分支的父节点,此时,分支层数不会发生变化        if (this.rank[pParent] > this.rank[qParent]) {            this.parent[qParent] = pParent;        }else if(this.rank[pParent] < this.rank[qParent]){            this.parent[pParent] = qParent;        } else{            //当两个分支层数相同时,连接在一起之后,其中一个分支的层数将+1            this.parent[pParent] = qParent;            this.rank[qParent] += 1;        }    }}

并查集优化(4)

思路:

  • 每一个分支的层数越小,查询的效率越高
  • 在find寻找某一元素的根节点的过程中,可以同时对分支的路径进行压缩,减少分支层数
function unionFind2(n){    this.parent = [];    this.rank = [];    for (var i = 0; i < n; i++) {        this.parent[i] = i;        this.rank[i] = 1;    }    this.find = function(p){        if (p < 0 || p >= this.parent.length) {            return -1;        }        while(parent[p] != p ){            parent[p] = parent[parent[p]];//路径压缩            p = parent[p];        }        return p;    }    this.isConnected = function(p,q){        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        int pParent = find[p];        int qParent = find[q];        if (pParent == qParent) {            return;        }        if (this.rank[pParent] > this.rank[qParent]) {            this.parent[qParent] = pParent;        }else if(this.rank[pParent] < this.rank[qParent]){            this.parent[pParent] = qParent;        } else{            this.parent[pParent] = qParent;            this.rank[qParent] += 1;        }    }}

并查集优化(5)

思路:

  • 路径压缩是减少分支层数的有效方法,最理想的路径压缩效果是:在每个分支中,所有子节点的父节点都为根节点,即分支的层数为2。
function unionFind2(n){    this.parent = [];    this.rank = [];    for (var i = 0; i < n; i++) {        this.parent[i] = i;        this.rank[i] = 1;    }    this.find = function(p){        if (p < 0 || p >= this.parent.length) {            return -1;        }        //第二种路径压缩方法,通过递归的方式将所有节点的父节点都设置为根节点        if (this.parent[p] != p ) {            this.parent[p] = find(this.parent[p]);        }        return this.parent[p];    }    this.isConnected = function(p,q){        return this.find(p) == this.find(q);    }    this.unionElements = function(p,q){        int pParent = find[p];        int qParent = find[q];        if (pParent == qParent) {            return;        }        if (this.rank[pParent] > this.rank[qParent]) {            this.parent[qParent] = pParent;        }else if(this.rank[pParent] < this.rank[qParent]){            this.parent[pParent] = qParent;        } else{            this.parent[pParent] = qParent;            this.rank[qParent] += 1;        }    }}
原创粉丝点击