Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

Prelude

Tá an t-alt seo faoi chrainn chuardaigh dhénártha. Scríobh mé alt le déanaí faoi comhbhrú sonraí ag modh Huffman. Níor thug mé aird ar chrainn dhénártha i ndáiríre, toisc nach raibh na modhanna chun cuardach a dhéanamh, a chur isteach, a scriosadh ábhartha. Anois chinn mé alt a scríobh faoi chrainn. B'fhéidir go dtosóimid.

Is struchtúr sonraí é crann comhdhéanta de nóid atá ceangailte le himill. Is féidir linn a rá gur cás speisialta de ghraif é crann. Seo crann samplach:

Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

Ní crann cuardaigh dénártha é seo! Tá gach rud faoi ghearradh!

Téarmaíocht

Root

Fréamh crann Is é an nód topmost. Sa sampla, is é seo nód A. Sa chrann, ní féidir ach cosán amháin a threorú ón bhfréamh go dtí aon nód eile! Go deimhin, is féidir aon nód a mheas mar fhréamh an fhochrainn a fhreagraíonn don nód seo.

Tuismitheoirí/sliocht

Tá imeall amháin go díreach ag gach nóid seachas an fhréamh suas go dtí nód eile. Tugtar an nód os cionn an nód reatha tuismitheoir an nód seo. Tugtar nód atá suite faoi bhun an tsrutha agus ceangailte leis sliocht an nód seo. Glacaimis sampla. Tóg nód B, ansin beidh a thuismitheoir ina nód A, agus beidh a leanaí ina nóid D, E, agus F.

Bileog

Tugtar duilleog an chrainn ar nód nach bhfuil leanaí aige. Sa sampla, beidh nóid D, E, F, G, I, J, K ina duilleoga.

Is é seo an téarmaíocht bhunúsach. Pléifear coincheapa eile níos déanaí. Mar sin, is crann é crann dénártha ina mbeidh níos mó ná beirt leanaí ag gach nód. Mar a cheap tú, ní bheidh an crann ón sampla dénártha, toisc go bhfuil níos mó ná beirt leanaí ag nóid B agus H. Seo sampla de chrann dénártha:

Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

Is féidir aon fhaisnéis a bheith i nóid an chrainn. Is crann dénártha é crann cuardaigh dénártha a bhfuil na hairíonna seo a leanas aige:

  1. Is crainn chuardaigh dhénártha iad na fochrainn chlé agus ar dheis.
  2. Tá eochairluachanna sonraí ag gach nóid den fhochrann clé de nód treallach X níos lú ná luach eochrach sonraí an nód X féin.
  3. Tá eochairluachanna sonraí ag gach nóid den fhochrann ceart de nód treallach X atá níos airde ná nó cothrom le luach na heochrach sonraí de nód X féin.

Eochair - tréith éigin den nód (mar shampla, uimhir). Tá an eochair ag teastáil chun a bheith in ann an eilimint den chrann lena mbaineann an eochair seo a fháil. Sampla crann cuardaigh dénártha:

Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

radharc crann

Agus mé ag dul ar aghaidh, cuirfidh mé roinnt píosaí cód (b'fhéidir neamhiomlán) san áireamh chun do thuiscint a fheabhsú. Beidh an cód iomlán ag deireadh an ailt.

Tá an crann déanta suas de nóid. Struchtúr nód:

public class Node<T> {
    private T data;
    private int key;
    private Node<T> leftChild;
    private Node<T> rightChild;

    public Node(T data, int key) {
        this.data = data;
        this.key = key;
    }
    public Node<T> getLeftChild() {
        return leftChild;
    }

    public Node<T> getRightChild() {
        return rightChild;
    }
//...остальные методы узла
}

Tá beirt pháistí ag gach nód (is féidir go mbeidh an Leanbh clé agus/nó leanaí ar dheis ar neamhní). Is dócha gur thuig tú sa chás seo gurb iad na sonraí uimhreacha na sonraí atá stóráilte sa nód; eochair - eochair nód.

Rinneamar amach an snaidhm, anois déanaimis labhairt faoi na fadhbanna práinneacha faoi chrainn. Anseo ina dhiaidh seo, ciallóidh an focal "crann" an coincheap de chrann cuardaigh dénártha. Struchtúr crann dénártha:

public class BinaryTree<T> {
     private Node<T> root;

    //методы дерева
}

Mar réimse ranga, ní gá dúinn ach fréamh an chrainn, mar ón bhfréamh, ag baint úsáide as na modhanna getLeftChild() agus getRightChild(), is féidir leat a fháil ar aon nód an chrainn.

Algartam Crann

Cuardaigh

Ligean le rá go bhfuil crann tógtha agat. Conas eilimint a aimsiú leis an eochair? Ní mór duit a bhogadh go seicheamhach ón fhréamh síos an crann agus an luach eochair a chur i gcomparáid le eochair an nód seo chugainn: má tá eochair níos lú ná an eochair an nód seo chugainn, ansin téigh go dtí an sliocht clé den nód, más mó - ar dheis, má tá na heochracha comhionann - tá an nód atá ag teastáil le fáil! Cód ábhartha:

public Node<T> find(int key) {
    Node<T> current = root;
    while (current.getKey() != key) {
        if (key < current.getKey())
            current = current.getLeftChild();
        else
            current = current.getRightChild();
        if (current == null)
            return null;
    }
    return current;
}

Má éiríonn an sruth ar neamhní, ansin tá atriall bainte amach ag deireadh an chrainn (ag leibhéal coincheapúil, tá tú in áit nach bhfuil ann sa chrann - leanbh duille).

Smaoinigh ar éifeachtúlacht an algartam cuardaigh ar chrann cothrom (crann ina ndéantar nóid a dháileadh níos mó nó níos lú go cothrom). Ansin beidh an éifeachtacht chuardaigh O(log(n)), agus an logartaim bonn 2. Féach: má tá n eilimint i gcrann cothrom, ansin ciallaíonn sé seo go mbeidh log(n) bonn 2 leibhéal an chrainn. Agus sa chuardach, le haghaidh céim amháin den timthriall, téann tú síos leibhéal amháin.

cuir isteach

Má tá tuiscint agat ar an croílár an chuardaigh, ansin ní bheidh sé deacair duit a thuiscint a chur isteach. Ní mór duit ach dul síos go dtí an duilleog an chrainn (de réir na rialacha a shliocht a thuairiscítear sa chuardach) agus a bheith ina shliocht - chlé nó ar dheis, ag brath ar an eochair. Cur i bhfeidhm:

   public void insert(T insertData, int key) {
        Node<T> current = root;
        Node<T> parent;
        Node<T> newNode = new Node<>(insertData, key);
        if (root == null)
            root = newNode;
        else {
            while (true) {
                parent = current;
                if (key < current.getKey()) {
                    current = current.getLeftChild();
                    if (current == null) {
                         parent.setLeftChild(newNode);
                         return;
                    }
                }
                else {
                    current = current.getRightChild();
                    if (current == null) {
                        parent.setRightChild(newNode);
                        return;
                    }
                }
            }
        }
    }

Sa chás seo, i dteannta leis an nód reatha, is gá faisnéis a stóráil faoi thuismitheoir an nód reatha. Nuair a éiríonn an t- reatha ar neamhní, beidh an bhileog a theastaíonn uainn san athróg tuismitheora.
Is léir go mbeidh an éifeachtúlacht ionsáite mar an gcéanna le héifeachtúlacht an chuardaigh - O(log(n)).

Scrios

Is é an scriosadh an oibríocht is casta a chaithfear a dhéanamh le crann. Tá sé soiléir go mbeidh sé riachtanach ar dtús teacht ar an eilimint go bhfuil muid ag dul a bhaint. Ach cad ansin? Más rud é go simplí a leagann muid a thagairt do null, ansin caillfidh muid faisnéis maidir leis an subtree arb é a fhréamh an nód seo. Roinntear modhanna bainte crann i dtrí chás.

An chéad chás. Níl aon leanaí ag an nód atá le baint.

Mura bhfuil leanaí ag an nód atá le scriosadh, ciallaíonn sé gur duilleog é. Dá bhrí sin, is féidir leat na réimsí leftChild nó rightLeanaí a thuismitheoir a chur ar neamhní.

An dara cás. Tá leanbh amháin ag an nód atá le baint

Níl an cás seo an-deacair freisin. Rachaimid ar ais chuig ár sampla. Cuir i gcás go gcaithfimid eilimint le heochair 14 a scriosadh. Aontaigh, ós rud é gurb é an leanbh ceart den nód le heochair 10, go mbeidh eochair níos mó ná 10 ag aon duine dá sliocht (sa chás seo, an ceann ceart), ionas gur féidir leat é a “ghearradh” go héasca ón gcrann, agus an tuismitheoir a nascadh go díreach le leanbh an nód atá á scriosadh, i.e. ceangail an nód le heochair 10 le nód 13. Bheadh ​​an scéal cosúil dá mbeadh orainn nód a scriosadh arb é leanbh clé a thuismitheoir é. Smaoinigh air duit féin - analaí cruinn.

An tríú cás. Tá beirt pháistí ag Nód

An cás is deacra. A ligean ar ghlacadh le breathnú ar shampla nua.

Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

Cuardaigh comharba

Ligean le rá go gcaithfimid an nód a bhaint leis an eochair 25. Cé a chuirfimid ina áit? Caithfidh duine dá lucht leanúna (sliocht nó sliocht de shliocht) a bheith comharba(an té a thógfaidh áit an nód bainte).

Cén chaoi a bhfuil a fhios agat cé ba cheart a bheith ina chomharba? Go hintinneach, is é seo an nód sa chrann a bhfuil a eochair an chéad cheann is mó as an nód á bhaint. Is é seo a leanas an algartam. Ní mór duit dul chuig a leanbh ceart (i gcónaí go dtí an ceann ceart, toisc go raibh sé ráite cheana féin go bhfuil eochair an chomharba níos mó ná eochair an nód a scriosadh), agus ansin dul tríd an slabhra de leanaí clé den leanbh ceart seo. Sa sampla, ní mór dúinn nascleanúint a dhéanamh chuig an nód le eochair 35, agus ansin siúl síos an slabhra de chuid leanaí chlé go dtí an duilleog - sa chás seo, is éard atá sa slabhra seo ach an nód le eochair 30. Go docht ag labhairt, táimid ag lorg an nód is lú sa sraith de nód níos mó ná an nód atá ag teastáil.

Dénártha Crann nó conas a ullmhú crann cuardaigh dénártha

Cód an mhodha cuardaigh comharba:

    public Node<T> getSuccessor(Node<T> deleteNode) {
        Node<T> parentSuccessor = deleteNode;//родитель преемника
        Node<T> successor = deleteNode;//преемник
        Node<T> current = successor.getRightChild();//просто "пробегающий" узел
        while (current != null) {
            parentSuccessor = successor;
            successor = current;
            current = current.getLeftChild();
        }
        //на выходе из цикла имеем преемника и родителя преемника
        if (successor != deleteNode.getRightChild()) {//если преемник не совпадает с правым потомком удаляемого узла
            parentSuccessor.setLeftChild(successor.getRightChild());//то его родитель забирает себе потомка преемника, чтобы не потерять его
            successor.setRightChild(deleteNode.getRightChild());//связываем преемника с правым потомком удаляемого узла
        }
        return successor;
    }

Cód iomlán an mhodha scriosta:

public boolean delete(int deleteKey) {
        Node<T> current = root;
        Node<T> parent = current;
        boolean isLeftChild = false;//В зависимости от того, является ли  удаляемый узел левым или правым потомком своего родителя, булевская переменная isLeftChild будет принимать значение true или false соответственно.
        while (current.getKey() != deleteKey) {
            parent = current;
            if (deleteKey < current.getKey()) {
                current = current.getLeftChild();
                isLeftChild = true;
            } else {
                isLeftChild = false;
                current = current.getRightChild();
            }
            if (current == null)
                return false;
        }

        if (current.getLeftChild() == null && current.getRightChild() == null) {//первый случай
            if (current == root)
                current = null;
            else if (isLeftChild)
                parent.setLeftChild(null);
            else
                parent.setRightChild(null);
        }
        else if (current.getRightChild() == null) {//второй случай
            if (current == root)
                root = current.getLeftChild();
            else if (isLeftChild)
                parent.setLeftChild(current.getLeftChild());
            else
                current.setRightChild(current.getLeftChild());
        } else if (current.getLeftChild() == null) {
            if (current == root)
                root = current.getRightChild();
            else if (isLeftChild)
                parent.setLeftChild(current.getRightChild());
            else
                parent.setRightChild(current.getRightChild());
        } 
        else {//третий случай
            Node<T> successor = getSuccessor(current);
            if (current == root)
                root = successor;
            else if (isLeftChild)
                parent.setLeftChild(successor);
            else
                parent.setRightChild(successor);
        }
        return true;
    }

Is féidir an chastacht a chomhfhogasú chuig O(log(n)).

An t-uasmhéid/íosmhéid a fháil i gcrann

Ar ndóigh, conas a fháil ar an luach íosta / uasta sa chrann - ní mór duit dul go seicheamhach tríd an slabhra na n-eilimintí clé / dheis an chrainn, faoi seach; nuair a shroicheann tú an duilleog, beidh sé mar an eilimint íosta/uasmhéid.

    public Node<T> getMinimum(Node<T> startPoint) {
        Node<T> current = startPoint;
        Node<T> parent = current;
        while (current != null) {
            parent = current;
            current = current.getLeftChild();
        }
        return parent;
    }

    public Node<T> getMaximum(Node<T> startPoint) {
        Node<T> current = startPoint;
        Node<T> parent = current;
        while (current != null) {
            parent = current;
            current = current.getRightChild();
        }
        return parent;
    }

Castacht - O(log(n))

Seachbhóthar Siméadrach

Is éard is Traversal ann ná cuairt a thabhairt ar gach nód den chrann chun rud éigin a dhéanamh leis.

Algartam trasnaithe siméadrach athfhillteach:

  1. Déan beart ar an bpáiste clé
  2. Déan beart leat féin
  3. Déan caingean ar an leanbh ceart

Cód:

    public void inOrder(Node<T> current) {
        if (current != null) {
            inOrder(current.getLeftChild());
            System.out.println(current.getData() + " ");//Здесь может быть все, что угодно
            inOrder(current.getRightChild());
        }
    }

Conclúid

Ar deireadh! Murar mhínigh mé rud éigin nó mura bhfuil aon tuairimí agam, táim ag fanacht sna tuairimí. Mar a gealladh, seo é an cód iomlán.

Nód.java:

public class Node<T> {
    private T data;
    private int key;
    private Node<T> leftChild;
    private Node<T> rightChild;

    public Node(T data, int key) {
        this.data = data;
        this.key = key;
    }

    public void setLeftChild(Node<T> newNode) {
        leftChild = newNode;
    }

    public void setRightChild(Node<T> newNode) {
        rightChild = newNode;
    }

    public Node<T> getLeftChild() {
        return leftChild;
    }

    public Node<T> getRightChild() {
        return rightChild;
    }

    public T getData() {
        return data;
    }

    public int getKey() {
        return key;
    }
}

BinaryTree.java:

public class BinaryTree<T> {
    private Node<T> root;

    public Node<T> find(int key) {
        Node<T> current = root;
        while (current.getKey() != key) {
            if (key < current.getKey())
                current = current.getLeftChild();
            else
                current = current.getRightChild();
            if (current == null)
                return null;
        }
        return current;
    }

    public void insert(T insertData, int key) {
        Node<T> current = root;
        Node<T> parent;
        Node<T> newNode = new Node<>(insertData, key);
        if (root == null)
            root = newNode;
        else {
            while (true) {
                parent = current;
                if (key < current.getKey()) {
                    current = current.getLeftChild();
                    if (current == null) {
                         parent.setLeftChild(newNode);
                         return;
                    }
                }
                else {
                    current = current.getRightChild();
                    if (current == null) {
                        parent.setRightChild(newNode);
                        return;
                    }
                }
            }
        }
    }

    public Node<T> getMinimum(Node<T> startPoint) {
        Node<T> current = startPoint;
        Node<T> parent = current;
        while (current != null) {
            parent = current;
            current = current.getLeftChild();
        }
        return parent;
    }

    public Node<T> getMaximum(Node<T> startPoint) {
        Node<T> current = startPoint;
        Node<T> parent = current;
        while (current != null) {
            parent = current;
            current = current.getRightChild();
        }
        return parent;
    }

    public Node<T> getSuccessor(Node<T> deleteNode) {
        Node<T> parentSuccessor = deleteNode;
        Node<T> successor = deleteNode;
        Node<T> current = successor.getRightChild();
        while (current != null) {
            parentSuccessor = successor;
            successor = current;
            current = current.getLeftChild();
        }

        if (successor != deleteNode.getRightChild()) {
            parentSuccessor.setLeftChild(successor.getRightChild());
            successor.setRightChild(deleteNode.getRightChild());
        }
        return successor;
    }

    public boolean delete(int deleteKey) {
        Node<T> current = root;
        Node<T> parent = current;
        boolean isLeftChild = false;
        while (current.getKey() != deleteKey) {
            parent = current;
            if (deleteKey < current.getKey()) {
                current = current.getLeftChild();
                isLeftChild = true;
            } else {
                isLeftChild = false;
                current = current.getRightChild();
            }
            if (current == null)
                return false;
        }

        if (current.getLeftChild() == null && current.getRightChild() == null) {
            if (current == root)
                current = null;
            else if (isLeftChild)
                parent.setLeftChild(null);
            else
                parent.setRightChild(null);
        }
        else if (current.getRightChild() == null) {
            if (current == root)
                root = current.getLeftChild();
            else if (isLeftChild)
                parent.setLeftChild(current.getLeftChild());
            else
                current.setRightChild(current.getLeftChild());
        } else if (current.getLeftChild() == null) {
            if (current == root)
                root = current.getRightChild();
            else if (isLeftChild)
                parent.setLeftChild(current.getRightChild());
            else
                parent.setRightChild(current.getRightChild());
        } 
        else {
            Node<T> successor = getSuccessor(current);
            if (current == root)
                root = successor;
            else if (isLeftChild)
                parent.setLeftChild(successor);
            else
                parent.setRightChild(successor);
        }
        return true;
    }

    public void inOrder(Node<T> current) {
        if (current != null) {
            inOrder(current.getLeftChild());
            System.out.println(current.getData() + " ");
            inOrder(current.getRightChild());
        }
    }
}

PS

Degeneration go O(n)

B'fhéidir gur thug go leor agaibh faoi deara: cad a tharlóidh má éiríonn leat an crann a bheith neamhchothrom? Mar shampla, cuir nóid sa chrann le heochracha méadaithe: 1,2,3,4,5,6... Ansin beidh an crann beagán i gcuimhne ar liosta nasctha. Agus tá, caillfidh an crann a struchtúr crann, agus dá bhrí sin éifeachtacht rochtana sonraí. Tiocfaidh castacht oibríochtaí cuardaigh, ionsáite agus scriosta mar a chéile le liosta nasctha: O(n). Is é seo ceann de na míbhuntáistí is tábhachtaí, i mo thuairim, a bhaineann le crainn dhénártha.

Ní féidir ach le húsáideoirí cláraithe páirt a ghlacadh sa suirbhé. Sínigh isteach, le do thoil.

Níl mé ar Habré le fada an lá, agus ba mhaith liom a fháil amach cad iad na hailt ar na hábhair ar mhaith leat níos mó a fheiceáil?

  • Struchtúir Sonraí

  • Algartam (DP, atarlú, comhbhrú sonraí, etc.)

  • Feidhmiú struchtúir sonraí agus halgartaim sa saol fíor

  • Cláir do Android i Java

  • Java cláir gréasáin cláir

Vótáil 2 úsáideoir. Staon úsáideoir amháin.

Foinse: www.habr.com

Add a comment