索引堆插入的问题

来源:4-8 索引堆(Index Heap)

拖车板牙爵士

2017-02-24

如果给索引堆插入新值的时候,如果插入的索引有误,是个固定值,这样就不能形成一个堆了,这种插入相同索引的问题应该怎么解决

maxheap.insert(6,Math.floor(Math.random()*100));

http://szimg.mukewang.com/58afb05b00013a5011870331.jpg

class ImdexMaxHeap {
    constructor(){
        this.data = [];
        this.index = [];
        this.reverse = [0];
        this.count = 0;
    }
    size() {
        return this.count;
    }
    isEmpty() {
        return this.count === 0;
    }
    insert(i,item) {//插入元素,传入的i对用户而言,是从0索引的
        if(i+1 >=1){
            i+=1
        }
        this.data[this.count+1] = item;
        this.index[this.count+1] = i;
        this.reverse[i] = this.count+1
        this.count++;
        this.shiftUp(this.count);
    }
    shiftUp(k) {
        while (k > 1 && this.data[Math.floor(this.index[k]/2)] < this.data[this.index[k]]){
            [this.index[Math.floor(k/2)],this.index[k]] = [this.index[k],this.index[Math.floor(k/2)]]
            this.reverse[this.index[Math.floor(k/2)]] = Math.floor(k/2)
            this.reverse[this.index[k]] = k
            k = Math.floor(k / 2);
        }
    }
    shiftDown(k){
        while (2*k <= this.count){
            let j = 2*k;
            if(j+1 <= this.count && this.data[this.index[j+1]] > this.data[this.index[j]]){
                j = j+1
            }
            if(this.data[this.index[k]] >= this.data[this.index[j]]){
                break
            }
            [this.index[k],this.index[j]] = [this.index[j],this.index[k]]
            this.reverse[this.index[k]] = k;
            this.reverse[this.index[j]] = j;
            k = j;
        }
    }
    extractMax(){
        if(this.count > 0){
            let ret = this.data[this.index[1]]
            //console.log(this.data[1]+"----------------------------"+this.data[this.count])
            if([this.index[1],this.index[this.count]] = [this.index[this.count],this.index[1]]){
                this.reverse[this.index[1]] = 1;
                this.reverse[this.index[this.count]] = 0;
                this.index = this.index.slice(0,this.count)
                this.count--
            }
            this.shiftDown(1)
            return ret;
        }
    }
    extractMaxIndex(){
        if(this.count > 0){
            let ret = this.index[1]-1;
            //console.log(this.data[1]+"----------------------------"+this.data[this.count])
            if([this.index[1],this.index[this.count]] = [this.index[this.count],this.index[1]]){
                this.reverse[this.index[1]] = 1;
                this.reverse[this.index[this.count]] = 0;
                this.index = this.index.slice(0,this.count)
                this.count--
            }
            this.shiftDown(1)
            return ret;
        }
    }
    getItem(i) {
        this.contain(i)
        return this.data[i+1]
    }
    contain(i){
        if(i+1 >=1 && i+1 <= this.data.length){
            return this.reverse[i+1] !=0;
        }
    }
    change(i,item) {
        this.contain(i)
        i += 1;
        this.data[i] = item
        for(let j = 1; j<= this.count;j++){
            if(this.index[j] ===i){
                this.shiftUp(j);
                this.shiftDown(j)
                return
            }
        }
    }
    change2(i,item) {
        this.contain(i)
        i += 1;
        this.data[i] = item
        let j = this.reverse[i]
        this.shiftUp(j);
        this.shiftDown(j)
    }
}
function main() {
    let maxheap = new ImdexMaxHeap();
    for (let i = 0; i < 20;i++){
        maxheap.insert(i,Math.floor(Math.random()*100));
    }
    console.log(maxheap.data+"---------"+maxheap.index+"---------"+maxheap.reverse);
    maxheap.extractMax()
    console.log(maxheap.data+"---------"+maxheap.index+"------------"+maxheap.reverse);
}
main()


写回答

1回答

liuyubobobo

2017-02-24

我不确定我是否正确理解了你的问题。但是对于我们设计的这个结构,用户在调用insert(i,e)前,是需要自行判断一下 contain(i)的,如果返回false,才可以调用insert(i,e),否则的话,只能使用change(i,e)。当然,为了安全起见,我们在insert里做这样的一个assert更严谨:


assert( !contain(i) );


这样,如果用户没有进行contain判断的话,插入重复索引,会抛出错误,就好像数组越界一样,是用户使用不当。当然,在实际的生产环境中,错误处理应该抛出异常一类的。


感谢你的提醒,现在我将github上的insert代码增加了这个assert,最终是这个样子:

// 传入的i对用户而言,是从0索引的    
void insert(int i, Item item){    
    assert( count + 1 <= capacity );    
    assert( i + 1 >= 1 && i + 1 <= capacity );    
    // 再插入一个新元素前,还需要保证索引i所在的位置是没有元素的。    
    assert( !contain(i) );    

    i += 1;    
    data[i] = item;    
    indexes[count+1] = i;    
    reverse[i] = count+1;    
    count++;    
    shiftUp(count);    
}


不过,我们也可以将接口设计成,如果insert的索引i是已经存在的话,就相应的调用change逻辑。如果我们要在IndexHeap中重载[]这个运算符的话,这是一个好的设计。换句话说,如果调用myIndexHeap[6] = 42,先查看6这个索引在我们的索引堆中是否存在,若存在,则修改其对应元素为42(调用change);否则,将索引6的元素42插入我们的索引堆(调用insert)。


希望解决了你的问题:)


5
2
liuyubobobo
回复
拖车板牙爵士
感谢你的问题。之前我的代码确实不够严谨,我已经将github上的代码加入了这个assert,同时,在上面的回答中,补充了完整的insert代码。再次感谢!加油!:)
2017-02-24
共2条回复

算法与数据结构(C++版) 面试/评级的算法复习技能包

课程专为:短时间内应对面试、升职测评等艰巨任务打造

11187 学习 · 1614 问题

查看课程