C++跳表(SkipList)查询、插入、删除动态演示版

#include<iostream>
#include<vector>
#include<random>
#include<string>
using namespace std;

template<class obj>
class Node {
public:
    size_t       key;
    obj*         value;
    vector<Node*>next;
    Node() {}
    Node(size_t k, obj* v, size_t lv) :
                 key(k), value(v), next(lv, nullptr) {}
};

template<class obj>
class SkipList :protected Node<obj> {
public:
    //跳表允许的最高层默认为10层,跳表的当前最高层初始化为1
    SkipList(size_t lv = 10) :level(lv), cur_lv(1) {
        //头指针指向头结点,头结点初始化为level层,每层指向空。
        //头结点不输出,所以头结点的key值任取一个较大的正整数。
        head = new Node<obj>(RAND_MAX, nullptr, level);
    }
    Node<obj>*  search(const size_t&) const;
    void        insert(const size_t&, obj*);
    bool        erase(const  size_t&);
    void        output() const;
    void        outputNode() const;
private:
    size_t      randomLevel();
    size_t      level;       //跳表允许的最高层。
    size_t      cur_lv;      //当前跳表的最高层。
    Node<obj>*  head;
};

//查找索引对应的结点
template<class obj>
Node<obj>* SkipList<obj>::search(const size_t& k) const {
    Node<obj>* temp = head;
    //从当前最高层开始遍历,一直向下遍历到最底层
    for (int i = cur_lv - 1; i >= 0; i--) {
        while (temp->next[i] != nullptr && temp->next[i]->key < k)
            //当前指针向右移动
            temp = temp->next[i];
    }
    //找到返回指针地址,否则返回空。
    if (temp->next[0] != nullptr && temp->next[0]->key == k)
        return temp->next[0];
    return nullptr;
}

//插入索引对应的结点
template<class obj>
void SkipList<obj>::insert(const size_t& k, obj* o) {
    //分配一个随机索引给新结点
    size_t lv = randomLevel();
    //更新当前索引至最大数
    cur_lv = max(lv, cur_lv);
    Node<obj>* node = new Node<obj>(k, o, lv);
    Node<obj>* temp = head;
    //从当前最高层开始遍历,一直向下遍历到最底层
    for (int i = cur_lv - 1; i >= 0; i--) {
        while (temp->next[i] != nullptr && temp->next[i]->key < k)
            //当前指针向右移动
            temp = temp->next[i];
        /*
        * 插入索引的核心代码
        * 假如新结点的随机索引为3层,当前跳表最高层为5层,那么。
        * 那么只能插入1,2,3层索引,4,5层索引不用插入。所以
        * 3>5-1 为假 不执行if语句段
        * 3>4-1 为假 不执行if语句段
        * 3>3-1 为真 执行if语句段。
        * 3>2-1 为真 执行if语句段。
        * 3>1-1 为真 执行if语句段。
        * 所以共执行了3次if语句段。
        * 可以一边看代码一边画图理解。
        */
        if (lv > i) {
            node->next[i] = temp->next[i];
            temp->next[i] = node;
        }
    }
}

//删除索引对应的结点
template<class obj>
bool SkipList<obj>::erase(const size_t& k) {
    Node<obj>* node=nullptr;
    Node<obj>* temp = head;
    //从当前最高层开始遍历,一直向下遍历到最底层
    for (int i = cur_lv - 1; i >= 0; i--) {
        while (temp->next[i] != nullptr && temp->next[i]->key < k)
            //当前执行向右移动
            temp = temp->next[i];
        //如果找到该结点,更新链接关系。
        if (temp->next[i] != nullptr && temp->next[i]->key == k) {
            node = temp->next[i];
            temp->next[i] = node->next[i];
        }
    }
    /*
    * 删除索引的核心代码
    * 将结点删除后,如果head结点的下一个结点为空,说明是空层,
    * 当前跳表的最高层要减去一层。
    * 以此类推。
    * 此部分代码可以边看代码边画图理解。
    */
    while (cur_lv > 0 && head->next[cur_lv-1] == nullptr)
        cur_lv--;

    //如果node为空,说明没找到该节点,返回假,否则返回真。
    if (node == nullptr)
        return false;
    else {
        delete node;
        node = nullptr;
        return true;
    }
}

//遍历输出每层索引
template<class obj>
void SkipList<obj>::output() const {
    for (int i = cur_lv -1; i >= 0; i--) {
        Node<obj>* temp = head;
        cout << "第"<<i<<"层:\t";
        while (temp->next[0] != nullptr) {
            temp = temp->next[0];
            if (temp->next.size() > i)
                cout << temp->key << "\t";
            else
                cout << "\t";
        }
        cout << endl << endl;
    }
}

//遍历输出每个结点key和value
template<class obj>
void SkipList<obj>::outputNode() const {
    Node<obj>* temp = head;
    while (temp->next[0]) {
        temp = temp->next[0];
        cout << "key: ";
        cout.setf(ios::left) << cout.width(10);
        cout << temp->key;


        cout << "vaule: x = ";
        cout.setf(ios::left) << cout.width(10);
        cout << temp->value->x;

        cout << "y = ";
        cout.setf(ios::left) << cout.width(10);
        cout << temp->value->y;

        cout << "z = ";
        cout.setf(ios::left) << cout.width(10);
        cout << temp->value->z << endl;
    }
    cout << endl;
}

//new一个结点后随机生成该结点的层数
template<class obj>
size_t SkipList<obj>::randomLevel() {
    size_t lv = 1;
    while (rand() % 2)
        lv++;
    //如果生成的结点比最大层level还大,返回最小数。
    return min(lv,level);
}

//类Node value值对象
class XYZ {
public:
    XYZ(int x_, int y_, int z_) {
        x = x_; y = y_; z = z_;
    }
    int x, y, z;
};

//清屏函数
void _CLEARSCREEN(const SkipList<XYZ>& sl) {
    system("cls");
    sl.outputNode();
    sl.output();
}

//检查输入的是否是合法数字
size_t isNumeric(string const& str) {
    auto it = str.begin();
    while (it != str.end()) {
        //如果不是数字成功,返回一个大数,否则检测下一个字符。
        if (!isdigit(*it))
            return 0X3B9AC9FF;
        it++;
    }
    //如果字符串为空,返回一个大数,否则将其转换成int类型。
    return str.empty() ? 0X3B9AC9FF : stoi(str);
}

void find(const SkipList<XYZ> &sl) {
    _CLEARSCREEN(sl);
    int cnt = 0;
    string key;
    while (true) {
        cout << "请输入要查询的key(按q或Q退出查询):";
        getline(cin, key);
        _CLEARSCREEN(sl);
        if (key == "q" || key == "Q")
            break;
        size_t k = isNumeric(key);
        auto ret = sl.search(k);
        if (ret) {
            cout << "查询成功key:" << k << "的值为:"
                 << " x = " << ret->value->x
                 << " y = " << ret->value->y
                 << " z = " << ret->value->z << endl;
            cnt = 0;
        }
        else
            cout << "查询失败"<<++cnt<<"次" << endl;
    }
}

void add(SkipList<XYZ> &sl) {
    _CLEARSCREEN(sl);
    int cnt = 0;
    string key;
    while (true) {
        cout << "请输入要插入的key(按q或Q退出查询):";
        getline(cin, key);
        _CLEARSCREEN(sl);
        if (key == "q" || key == "Q")
            break;
        size_t k = isNumeric(key);
        if (k == 0X3B9AC9FF)
            cout << "非法key值,插入失败" << ++cnt << "次"<< endl;
        else if (sl.search(k) != nullptr) {
            cout << "key:" << k << " 已存在!" << endl;
            cout << "非法key值,插入失败" << ++cnt << "次" << endl;
        }else{
            sl.insert(k, new XYZ(rand() % 100 - 50, rand() % 100 - 50, rand() % 100 - 50));
            _CLEARSCREEN(sl);
            cout << "插入成功!" << endl;
            cnt = 0;
        }
    }
}

void del(SkipList<XYZ> &sl) {
    _CLEARSCREEN(sl);
    int cnt = 0;
    string key;
    while (true) {
        cout << "请输入你要删除的key(按q或Q退出查询):";
        getline(cin, key);
        _CLEARSCREEN(sl);
        if (key == "q" || key == "Q")
            break;
        size_t k = isNumeric(key);
        if (k == 0X3B9AC9FF) {
            cout << "非法key值,删除失败" << ++cnt << "次" << endl;
        }else {
            if (sl.erase(k)){
                cnt = 0;
                _CLEARSCREEN(sl);
                cout << "删除成功!" << endl;
            }else {
                _CLEARSCREEN(sl);
                cout << "要删除的key:" << k << "不存在!" << endl;
                cout << "非法key值,删除失败" << ++cnt << "次" << endl;
            }
        }
    }
 }

int main() {
    SkipList<XYZ> sl;
    srand((unsigned)time(0));

    
#if 1
    //随机初始化,可以将if后面的1改为0,运行程序手动输入索引,但value值会自动随机初始化。
    for (int i = 0; i < 7; i++) {
        size_t key = rand() % 100;

        //过滤重复key
        while (sl.search(key) != nullptr)
            key = rand() % 100;

        //插入随机生成的key和value
        sl.insert
        (
            key, new XYZ
            (
                rand() % 100 - 50,
                rand() % 100 - 50,
                rand() % 100 - 50
            )
        );

    }
#endif#
    
    //刷新屏幕
    _CLEARSCREEN(sl);

    int cnt = 0;
    int err = 0;
    string s;

    //进入循环菜单
    while (true) {
        cout << "1.查询\t2.插入\t3.删除\t0.退出" << endl;
        cout << "请选择编号:";
        getline(cin, s);
        try {
            switch (isNumeric(s)) {
            case 1:
                find(sl);
                cnt = 0;
                err = 0;
                break;
            case 2:
                add(sl);
                cnt = 0;
                err = 0;
                break;
            case 3:
                del(sl);
                cnt = 0;
                err = 0;
                break;
            case 0:
                exit(-1);
            default:
                err = 0;
                _CLEARSCREEN(sl);
                cout << "***输入错误" << ++cnt << "次***" << endl;
                break;
            }
        }
        catch (out_of_range&) {
            cnt = 0;
            _CLEARSCREEN(sl);
            cout << "发生异常" << ++err << "次.原因key值过大!" << endl;
        }
    }
    system("pause");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值