rope-structure
Version:
A Rope Data Structure implementation which is used for efficient text editing and used widely in text editors.
2 lines (1 loc) • 3.78 kB
JavaScript
var n=class{left=null;right=null;parent=null;weight=0;value="";type="LEAF";height=1;constructor(t=""){this.value=t,this.weight=t.length,this.type="LEAF"}},s=class{root=null;LEAF_LENGTH_THRESHOLD=1024;REBALANCE_RATIO=1.5;constructor(t=""){t&&(this.root=this.createOptimalTree(t))}createOptimalTree(t,e=0,r=t.length){if(r-e<=this.LEAF_LENGTH_THRESHOLD)return new n(t.substring(e,r));let h=Math.floor((e+r)/2),o=new n;return o.type="INTERNAL",o.left=this.createOptimalTree(t,e,h),o.right=this.createOptimalTree(t,h,r),this.updateNodeMetadata(o),o}updateNodeMetadata(t){if(t.type==="LEAF"){t.weight=t.value.length,t.height=1;return}let e=t.left?t.left.height:0,r=t.right?t.right.height:0;t.weight=this.getNodeWeight(t.left),t.height=Math.max(e,r)+1}getNodeWeight(t){return t?t.type==="LEAF"?t.value.length:this.getNodeWeight(t.left)+this.getNodeWeight(t.right):0}getBalanceFactor(t){if(!t)return 0;let e=t.left?t.left.height:0,r=t.right?t.right.height:0;return e-r}rotateRight(t){let e=t.left,r=e.right;return e.right=t,t.left=r,e.parent=t.parent,t.parent=e,r&&(r.parent=t),this.updateNodeMetadata(t),this.updateNodeMetadata(e),e}rotateLeft(t){let e=t.right,r=e.left;return e.left=t,t.right=r,e.parent=t.parent,t.parent=e,r&&(r.parent=t),this.updateNodeMetadata(t),this.updateNodeMetadata(e),e}balance(t){this.updateNodeMetadata(t);let e=this.getBalanceFactor(t);return e>1?this.getBalanceFactor(t.left)>=0?this.rotateRight(t):(t.left=this.rotateLeft(t.left),this.rotateRight(t)):e<-1?this.getBalanceFactor(t.right)<=0?this.rotateLeft(t):(t.right=this.rotateRight(t.right),this.rotateLeft(t)):t}insert(t,e){if(t<0||t>this.length())throw new Error("Index out of bounds");if(!this.root){this.root=new n(e);return}let r=new n(e),[i,h]=this.split(this.root,t);if(i){let o=this.concat(i,r);this.root=this.concat(o,h)}else this.root=this.concat(r,h)}delete(t,e){if(t===e)return;if(t<0||e>this.length()||t>=e)throw new Error("Invalid range");let[r,i]=this.split(this.root,t),[h,o]=this.split(i,e-t);this.root=r?this.concat(r,o):o}charAt(t){if(!this.root||t<0||t>=this.length())throw new Error("Index out of bounds");let e=this.root,r=t;for(;e.type==="INTERNAL";)if(r<e.weight){if(!e.left)throw new Error("Invalid rope structure");e=e.left}else{if(!e.right)throw new Error("Invalid rope structure");r-=e.weight,e=e.right}let i=e.value[r];if(i===void 0)throw new Error("Index out of bounds");return i}substring(t,e=this.length()){if(t<0||e>this.length()||t>e)throw new Error("Invalid range");if(t===e)return"";if(t===0&&e===this.length())return this.toString();let[r,i]=this.split(this.root,t),[h,o]=this.split(i,e-t),u=h?this.toString(h):"",l=r?this.concat(r,h):h;return this.root=o?this.concat(l,o):l,u}concat(t,e){if(!t)return e;if(!e)return t;if(t.type==="LEAF"&&e.type==="LEAF"&&t.value.length+e.value.length<=this.LEAF_LENGTH_THRESHOLD)return t.value+=e.value,t.weight=t.value.length,t;let r=new n;return r.type="INTERNAL",r.left=t,r.right=e,t.parent=r,e.parent=r,this.updateNodeMetadata(r),this.balance(r)}split(t,e){if(t.type==="LEAF"){if(e===0)return[null,t];if(e===t.value.length)return[t,null];let r=new n(t.value.substring(0,e)),i=new n(t.value.substring(e));return[r,i]}if(e<t.weight){let[r,i]=this.split(t.left,e),h=i?this.concat(i,t.right):t.right;return[r,h]}else{let[r,i]=this.split(t.right,e-t.weight);return[r?this.concat(t.left,r):t.left,i]}}length(){return this.root?this.getNodeWeight(this.root):0}toString(t=this.root){return t?t.type==="LEAF"?t.value:this.toString(t.left)+this.toString(t.right):""}isBalanced(){let t=e=>{if(!e)return 0;let r=t(e.left);if(r===-1)return-1;let i=t(e.right);return i===-1||Math.abs(r-i)>1?-1:Math.max(r,i)+1};return t(this.root)!==-1}rebalance(){let t=this.toString();this.root=this.createOptimalTree(t)}};export{n as RopeNode,s as default};