zoukankan      html  css  js  c++  java
  • MySQL-04 高级特性

    学习要点

    • MySQL系统函数
    • MySQL视图
    • MySQL存储过程
    • MySQL自定义函数
    • MySQL触发器
    • MySQL事务
    • MySQL事件

      

    MySQL系统函数

    数学函数

    • 获取整数的函数:CEIL(x)、CEILING(x)、FLOOR(x)
    CEIL(x)、CEILING(x):返回不小于x的BIGINT整数值。
    
    FLOOR(x):向下取整,返回BIGINT。
    

       

    • 获取随机数函数:RAND()、RAND(X)
    返回[0,1]之间的浮点数。X用来产生重复的随机数。
    

    上机练习1:#获得[1-99]之间的随机数

    SELECT FLOOR(RAND()*98)+1;
    

       

    • ROUND(x)、ROUND(x,y)、TRUNCATE(x,y)
    ROUND(x)四舍五入。
    
    ROUND(x,y)返回接近x的数,保留小数点y位。y为负数则去除小数点左边y位。
    
    TRUNCATE(x,y)返回舍去小数点后y位的数字。y为负数则去除小数点左边y位。
    

      

    字符串函数

    • 计算字符串字符数的函数:CHAR_LENGTH()
    • 计算字符串长度的函数:LENGTH()
    • 字母大小写转换函数:LOWER()、LCASE()、UPPER()、UCASE()
    • 字符串比较函数:STRCMP(S1,S2)  相等返回0,S1<S2返回-1,否则返回1
    • 字符串截取:SUBSTRING(Str,offset,len)。Offset为负数则从字符串结尾往字符串开始方向计算。
    • 获取当前日期函数:CURDATE()、CURRENT_DATE()
    • 获取当前时间函数:CRUTIME()、CURRENT_TIME()
    • 获得当前时间:now();
    • 数据表中自动插入时间戳:字段类型设置为:timestamp 默认值设置为:default current_timestamp

     

    日期和时间函数

    • 获取当前日期函数:CURDATE()、CURRENT_DATE()
    • 获取当前时间函数:CRUTIME()、CURRENT_TIME()
    • 获得当前时间:now();
    • 数据表中自动插入时间戳:字段类型设置为:timestamp 默认值设置为:default current_timestamp

     

    条件判断函数

    • IF(expr,v1,v2):expr表达式成立,返回V1,否则返回V2。
    • IFNULL(v1,v2):V1不为空,返回V1;否则返回V2。

    上机练习2:修改学生信息返回,地址为null返回‘地址为空’

     

    • CASE函数
    CASE 表达式
    
    WHEN V1 THEN …
    
    WHEN V2 THEN …
    
    WHEN V3 THEN …
    
    …
    
    ELSE …
    
    END
    

      

    用法示例:

     

    系统信息函数

    • 获取MySQL版本号:VERSION()
    • 获取当前进程的mysql访问id:CONNECTION_ID()

     

    • 获取最后一个生成的ID的值的函数:LAST_INSERT_ID();

    加密函数

    • PASSWORD(str)加密:系统加密使用,比如数据库管理员用户加密。
    • MD5(str)加密:应用程序使用。
    • ENCODE(被加密字符串,加密密码),DECODE(已加密字符串,解密密码)

     

    其他函数

    IP地址与数字相互转换函数:INET_ATION(‘a.b.c.d’)、INET_NTION(num)

    转换算法:a*256*256*256+b*256*256+c*256+d*1

     

    MySQL视图

    视图的定义

    • 视图是一个虚拟表:表示一张表的部分数据或多张表的综合数据;其结构和数据是建立在对表的查询基础上。
    • 实际上视图就是一条SELECT或者INSERT、UPDATE、DELETE语句。
    • 视图中不存放数据。数据存放在视图所引用的原始表中。
    • 一个原始表,根据不同用户的不同需求,可以创建不同的视图
    • MySQL从5.0版本开始支持视图。

    为什么使用视图

    • 不同的人员关注不同的数据
    • 保证信息的安全性

      

    视图的用途

    • 筛选表中的行
    • 防止未经许可的用户访问敏感数据
    • 降低数据库的复杂程度
    • 将多个物理数据库抽象为一个逻辑数据库

    如何创建视图

    1、方式一:通过视图创建工具可视化创建视图

     

    2、方式二:通过SQL语句创建视图

    语法格式:

    CREATE  [OR  REPLACE]  VIEW  view_name  AS  SELECT_statement

    创建单表视图(支持增删改查操作)

    创建多表视图(不推荐进行增删改)

    查看视图

    使用DESC查看视图结构

    使用 [ SHOW TABLE STATUS LIKE ‘视图名’] 查看视图基本信息

    使用 [SHOW CREATE VIEW ‘视图名’] 查看视图详细信息

    从information_schema.views表中查看数据库所有已定义的视图

    修改视图

    CREATE  [OR REPLACE]  view_name  AS  SELECT_statement
    

      

    语法同创建视图。

    或者:

    ALTER view_name  AS  SELECT_statement
    

      

    更新视图

    语法格式:

    UPDATE 视图名称  SET 字段=value
    

      

    注意:会更新原始表数据

    从视图中delete一条记录,也将会删除原始表数据。

    以下情况,无法通过视图更新操作:

    1、视图中不包含基本表中的非空字段;

    2、SELECT子句中使用了数学表达式;

    3、SELECT子句中使用了聚合函数;

    4、SELECT子句中使用了:DISTINCT,UNION,TOP,GROUP BY或者HAVING子句

     

    删除视图

    DROP  VIEW [ IF EXISTIS ] 视图名
    

    上机练习3:创建视图vw_getresult,实现以下功能

     

    MySQL存储过程

    存储过程定义

    存储过程就是一条或者多条SQL语句的集合,保存在服务器数据库里。

    • 优点:

    1、避免在网上从传输大量的SQL语句,提高网络速度;

    2、防止SQL语句被截取和盗用,提高信息安全性;

    3、存储过程保存在服务器端,已经被编译,执行效率高。

    • 缺点:移植性差。

    语法格式

    CREATE  PROCEDURE sp_name ( [proc_parameter])
    
    [characteristics … ]  routine_body
    

      

    • proc_parameter:存储过程参数,有三种类型

        [ IN | OUT | INOUT ] proc_parameter

    • characteristics:存储过程特性。

        包含语言、结果、SQL限制、权限、备注等信息的设置。

    • routine_body:用BEGIN … END 来表示的SQL代码块

     

    创建无参存储过程

    • 在控制台创建:

     

      DELIMITER:将MySQL的结束符设置为//。因为存储过程中的“;”会被控制台终端当成SQL的结束符号。重新定义结束符号:DELIMITER ; //。

    • 在navicat查询编辑器中创建存储过程

     

    • 执行存储过程

     

    • 删除存储过程

     

    • 查看数据库中已定义的存储过程或者函数

     

    • 查看存储过程定义

     

    上机练习4:创建存储过程sp_getteaminfo(),实现球队信息的查询。

      

    创建带输入参数的存储过程

    • 创建

     

    • 调用

     

    创建带输入输出参数的存储过程

    • 创建

     

    • 调用

    方式1:在控制台调用,设定自定义变量@变量名 接收输出参数。

     

    方式2:在navicat存储过程编辑器中输入参数运行,多个参数之间‘,’号隔开,字符串要使用单引号。

     

    上机练习5:创建存储过程sp_getstudentnamebyno(IN inno,OUT name CHAR(20)),实现学生信息查询。

    MySQL自定义函数

    语法格式

    CREATE  FUNCTION func_name ( [func_parameter])
    
    RETURNS type
    
    [characteristics … ]  routine_body
    

      

    • func_name自定义函数名
    • RETURNS type:函数返回的数据类型
    • func_parameter:自定义函数参数,有三种类型

        [ IN | OUT | INOUT ] func_parameter

    • characteristics:存储过程特性。

        包含语言、结果、SQL限制、权限、备注等信息的设置。

    • routine_body:用BEGIN … END 来表示的SQL代码块。

     

    自定义函数示例

     

    注意:

    1、和存储过程不同,函数的参数不需要指定IN、OUT、INOUT。

    2、函数内只有一条执行SQL语句,可以不使用BEGIN-END。否则需要BEGIN-END。

    3、 函数调用直接使用SELECT调用。

     

    上机练习6:创建函数fn_getstudentnamebyno(inno INT),实现学生信息查询。

      

    MySQL触发器

    触发器的定义

    触发器与存储过程一样,都是存储在MySQL的一段程序。

    存储过程的执行采用CALL调用的方式,而触发器的执行是由事件来触发执行的,这些事件包括:INSERT、UPDATE、DELETE。

    语法格式

    CREATE  TRIGGER  trigger_name  trigger_time  trigger_event
    
    ON  table_name  FOR  EACH  ROW  trigger_stmt

    参数说明:

    • trigger_name:触发器名称
    • trigger_time:触发时机,值为after或者before
    • trigger_event:触发事件——INSERT、UPDATE、DELETE
    • table_name:建立出发器的数据表
    • trigger_stmt:触发器程序体
    • FOR EACH ROW:通知触发器数据表行记录变更的时候触发

     

    创建一条语句的触发器

    创建表tgtest和触发器tg_sum_tritest_amount:

    #创建ACCOUNT表
    DROP TABLE IF EXISTS tgtest;
    CREATE TABLE IF NOT EXISTS tgtest(
    	id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT'编号',
      amount INT UNSIGNED NOT NULL COMMENT'金额'
    )ENGINE=INNODB DEFAULT CHARSET=UTF8;
    
    #创建触发器:向tritest插入数据前,对amount字段进行求和
    CREATE TRIGGER tg_sum_tritest_amount BEFORE INSERT 
        ON tgtest
       FOR EACH ROW SET @sum=@sum+NEW.amount;
    

      

    查看数据库中触发器: 

    SELECT * FROM information_schema.TRIGGERS;
    

      

    测试触发器:

    #测试触发器
    SET @sum=0;
    INSERT INTO tgtest VALUES(DEFAULT,3),(DEFAULT,5);
    SELECT @sum;
    

      

    测试结果:

    删除触发器:

    # 删除触发器
    DROP TRIGGER tg_sum_tritest_amount;
    

      

      

    创建多条执行语句的触发器

    问题:有两张MyISAM类型的数据表,一张是商品表,一张是订单表,当增删改订单表的时候,如何使用触发器修改商品表的库存?

    示例代码:

    #创建商品表 
    DROP TABLE IF EXISTS good; 
    CREATE TABLE IF NOT EXISTS good( 
     gid BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '商品编号', 
     gname char(20)  NOT NULL COMMENT '品名', 
     gcount INT UNSIGNED NOT NULL COMMENT '库存' 
    )ENGINE=MyISAM  DEFAULT CHARSET=UTF8 AUTO_INCREMENT=100000;
    
    
    
    #创建订单表 
    DROP TABLE IF EXISTS saleorder; 
    CREATE TABLE IF NOT EXISTS saleorder( 
     oid BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '订单编号', 
     gid BIGINT UNSIGNED NOT NULL COMMENT '商品id', 
     ocount INT UNSIGNED NOT NULL COMMENT '订单商品数量' 
    )ENGINE=MyISAM  DEFAULT CHARSET=UTF8 AUTO_INCREMENT=3502002016000;
    
    # 商品表插入数据
    INSERT INTO good(gname,gcount) VALUES('iphone x',100);
    SELECT * FROM good;
    
    
    # 创建订单时候修改商品表的库存 
    DROP TRIGGER tg_corder_mgood; 
    CREATE TRIGGER tg_corder_mgood AFTER INSERT ON saleorder 
    FOR EACH ROW 
    UPDATE good SET gcount=gcount-NEW.ocount WHERE gid=NEW.gid;
    
    
    # 下单 
    INSERT INTO saleorder(gid,ocount) VALUES(100000,11); 
    
    # 查看商品表和订单表的数据
    SELECT * FROM good; 
    SELECT * FROM saleorder;
    
    
    
    # 修改订单时候修改商品表的库存 
    DROP TRIGGER tg_morder_mgood; 
    CREATE TRIGGER tg_morder_mgood AFTER UPDATE ON saleorder 
    FOR EACH ROW 
    UPDATE good SET gcount=gcount+OLD.ocount-NEW.ocount WHERE gid=OLD.gid;
    
     
    # 修改订单 
    UPDATE saleorder SET ocount=ocount-1 WHERE oid=3502002016000; -- 订单商品数量-1
    SELECT * FROM good; 
    SELECT * FROM saleorder; 
      
     
    # 删除订单的时候修改商品表的库存 
    DROP TRIGGER tg_dorder_mgood; 
    CREATE TRIGGER tg_dorder_mgood AFTER DELETE ON saleorder 
    FOR EACH ROW 
    UPDATE good SET gcount=gcount+OLD.ocount WHERE gid=OLD.gid;
     
    # 删除订单 
    DELETE FROM saleorder WHERE oid=3502002016000; 
    SELECT * FROM good; 
    SELECT * FROM saleorder;

       

    NEW和OLD:使用“NEW.字段名”和“OLD.字段名”的SQL语句变量称为过渡变量。

    NEW:只能存在于INSERT语句中。“NEW.字段名”表示获取引起触发器触发的SQL语句中对应字段的值。

    OLD:只能存在于DELETE语句中。“OLD.字段名”表示获取引起触发器触发的SQL语句对应数据表中相应记录对应字段的值。

    只有在UPDATE语句中可以同时存在NEW和OLD关键字。

    上机练习7:创建多条语句触发器,实现订单表的增删改的同时触发修改商品表信息。 

      

    MySQL事务

    事务的定义

    • 事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作
    • 多个操作作为一个整体向系统提交,要么都执行、要么都不执行
    • 事务是一个不可分割的工作逻辑单元

    事务的特性

    事务必须具备以下四个属性,简称ACID 属性:

    • 原子性(Atomicity):事务是一个完整的操作,事务的各步操作是不可分的(原子的),要么都执行,要么都不执行。
    • 一致性(Consistency):当事务完成时,数据必须处于一致状态。
    • 隔离性(Isolation):并发事务之间彼此隔离、独立,它不应以任何方式依赖于或影响其他事务。
    • 永久性(Durability):事务完成后,它对数据库的修改被永久保持。
    • 注意:InnoDB和BDB引擎表才支持事务。
    • 问题:解决转账问题。

    事务综合示例:

    USE mybank;
     
    # 模拟银行数据表1
    DROP TABLE IF EXISTS bank1; 
    CREATE TABLE IF NOT EXISTS bank1( 
      id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '账号', 
      username CHAR(20) NOT NULL COMMENT '户名', 
      amount INT UNSIGNED NOT NULL COMMENT '余额' 
    )ENGINE=INNODB DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1120553244123;
     
       
    # 模拟银行数据表2
    DROP TABLE IF EXISTS bank2; 
    CREATE TABLE IF NOT EXISTS bank2( 
      id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '账号', 
      username CHAR(20) NOT NULL COMMENT '户名', 
      amount INT UNSIGNED NOT NULL COMMENT '余额' 
    )ENGINE=INNODB DEFAULT CHARSET=UTF8 AUTO_INCREMENT=2234753244567;
     
      
    # 两个银行分别初始化两个账户
    INSERT INTO bank1(id,username,amount) VALUES(DEFAULT,'张三',800); 
    INSERT INTO bank2(id,username,amount) VALUES(DEFAULT,'李四',1200);
     
      
    # 查询数据 
    SELECT * FROM bank1; 
    SELECT * FROM bank2;
     
     
    #模拟转账 
    -- 张三转账850元给李四
    UPDATE bank2 SET amount=amount+850 WHERE id=2234753244567; -- 李四
    UPDATE bank1 SET amount=amount-850 WHERE id=1120553244123; -- 张三
    
      
    /** 解决方案1 */
    #关闭自动事务提交 
    SET AUTOCOMMIT =0; -- 1表示开启
    UPDATE bank2 SET amount=amount+850 WHERE id=2234753244567; -- 李四
    UPDATE bank1 SET amount=amount-850 WHERE id=1120553244123; -- 张三 
    ROLLBACK; -- 异常则回滚
    COMMIT; -- 提交事务
      
    /** 解决方案2 */
    # 开启事务
    SET AUTOCOMMIT =1; -- 自动提交事务打开
    START TRANSACTION; -- 开始事务(事务打开的情况下需要调用该语句,否则无法回滚) 
    UPDATE bank2 SET amount=amount+850; 
    UPDATE bank1 SET amount=amount-850;  
    COMMIT; -- 提交事务(关闭事务) 
    ROLLBACK; -- 放弃事务(关闭事务)
     
      
     
    # 折返点 
    SELECT * FROM bank1; 
    SET AUTOCOMMIT =0;
    UPDATE bank1 SET amount=amount+1; 
    UPDATE bank1 SET amount=amount+1; 
    UPDATE bank1 SET amount=amount+1; 
    SAVEPOINT a_ponit; 
    UPDATE bank1 SET amount=amount+1; 
    UPDATE bank1 SET amount=amount+1; 
    UPDATE bank1 SET amount=amount+1; 
    UPDATE bank1 SET amount=amount+1; 
    ROLLBACK TO SAVEPOINT a_ponit;
    ROLLBACK; -- 全部回滚
    COMMIT; -- 提交事务
     
      
     
    # 使用存储过程实现转账事务处理
    -- 转账存储过程 
    DROP PROCEDURE IF EXISTS transamount; 
    CREATE PROCEDURE transamount () 
    BEGIN 
      DECLARE trans_error INT DEFAULT 0; -- 声明变量trans_error保存错误信息
      DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET trans_error = 1; -- 定义错误处理方式:continue或者exit,这里选择continue
      SET AUTOCOMMIT =0; -- 关闭MySQL事务自动提交
      START TRANSACTION; -- 开启事务 
        UPDATE bank1 SET amount=amount-850; 
        UPDATE bank2 SET amount=amount+850; 
      IF trans_error = 1 THEN 
             ROLLBACK; -- 错误则回滚
         SELECT '转账失败';
      ELSE
            COMMIT; -- 转账成功提交事务
       SELECT '转账成功'; 
      END IF; 
      SET AUTOCOMMIT =1; -- 打开MySQL事务自动提交 
    END;
     
      
    # 测试存储过程转账
    SELECT * FROM bank1; 
    SELECT * FROM bank2; 
    CALL transamount();
    

    上机练习8:创建存储过程实现转账事务要求。

      

    MySQL事件

    在系统管理或者数据库管理中,经常要周期性的执行某一个命令或者SQL语句。Mysql在5.1以后推出了事件调度器(Event Scheduler),和linux的cron功能一样,能方便地实现 mysql数据库的计划任务,而且能精确到秒。使用起来非常简单和方便。

    如何开启事件

    查看mysql服务器上的事件是否开启

    SHOW VARIABLES LIKE 'event_scheduler';
    SELECT @@event_scheduler;
    SHOW PROCESSLIST; -- 查看mysql服务器在运行线程
    

      

    如果看到event_scheduler为on或者PROCESSLIST中显示有event_scheduler的信息说明就已经开启了事件。如果显示为off或者在PROCESSLIST中查看不到event_scheduler的信息,那么就说明事件没有开启,我们需要开启它。

    如何开启事件?

    • 通过动态参数修改
    SET GLOBAL event_scheduler = ON;  //重启后失效
    • 更改配置文件然后重启
    my.ini文件的[mysqld]部分添加:event_scheduler=ON
    • 通过制定事件参数启动
    mysqld ... --event_scheduler=ON
    

      

    Mysql事件的语法

    • 创建事件语法
    CREATE
        [DEFINER = { user | CURRENT_USER }]  EVENT  [IF NOT EXISTS] event_name  ON SCHEDULE schedule
        [ON COMPLETION [NOT] PRESERVE]  [ENABLE | DISABLE | DISABLE ON SLAVE]  [COMMENT 'comment']
        DO event_body; 
    
    schedule:
        AT timestamp [+ INTERVAL interval] ... | EVERY interval [STARTS timestamp [+ INTERVAL interval] ...]  [ENDS timestamp [+ INTERVAL interval] ...]
    
    interval:
      quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}
    

      

    参数说明:

    DEFINER: 定义事件执行的时候检查权限的用户。

    ON SCHEDULE schedule: 定义执行的时间和时间间隔。

    ON COMPLETION [NOT] PRESERVE: 定义事件是一次执行还是永久执行,默认为一次执行,即NOT PRESERVE。

    ENABLE | DISABLE | DISABLE ON SLAVE: 定义事件创建以后是开启还是关闭,以及在从上关闭。如果是从服务器自动同步主上的创建事件的语句的话,会自动加上DISABLE ON SLAVE。

    COMMENT 'comment': 定义事件的注释。

    • 修改事件语法
    ALTER
        [DEFINER = { user | CURRENT_USER }]  EVENT event_name  [ON SCHEDULE schedule]  [ON COMPLETION [NOT] PRESERVE]
        [RENAME TO new_event_name]  [ENABLE | DISABLE | DISABLE ON SLAVE] [COMMENT 'comment']   [DO event_body]
    

       

    • 删除事件语法
    DROP EVENT [IF EXISTS] event_name
    

      

    Mysql事件应用举例

    • 简单应用
    # 开启事件(MySQL服务重启失效)
    SET GLOBAL event_scheduler = ON; 
    
    # 创建事件测试表 
    DROP TABLE IF EXISTS etest;
    CREATE TABLE IF NOT EXISTS etest ( 
      `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, 
      `data` datetime DEFAULT NULL 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1000;
     
    # 创建一个每隔10秒往test表中插入一条数据的事件(立刻开始运行) 
    DROP EVENT IF EXISTS ev_insert_data; 
    CREATE EVENT IF NOT EXISTS ev_insert_data ON SCHEDULE EVERY 10 SECOND 
        ON COMPLETION PRESERVE
        DO INSERT INTO etest(id,data) VALUES(DEFAULT,NOW());
    
    # 查询数据
    SELECT * FROM etest;
     
    # 创建一个1分钟后清空test表数据的事件
    DROP EVENT  IF EXISTS  ev_clear_data; 
    CREATE EVENT IF NOT EXISTS ev_clear_data 
        ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
        DO TRUNCATE TABLE etest;  
     
    # 某一时刻清除数据
    DROP EVENT  IF EXISTS  ev_clear_data_attime; 
    CREATE EVENT IF NOT EXISTS ev_clear_data_attime
        ON SCHEDULE AT TIMESTAMP '2018-10-10 00:00:00'
        DO TRUNCATE TABLE etest;  
     
    # 某个时间段执行事件
    DROP EVENT IF EXISTS ev_do_between_time; 
    CREATE EVENT IF NOT EXISTS ev_do_between_time 
        ON SCHEDULE EVERY 3 SECOND 
           STARTS '2018-08-22 21:49:00'
           ENDS '2018-08-22 21:49:00'+ INTERVAL  10 MINUTE 
        ON COMPLETION PRESERVE
        DO INSERT INTO etest(id,data) VALUES(DEFAULT,NOW());
    

       

    • MySQL事件通常用来调用存储过程 
    # 调用存储过程(一分钟后转账)
    DROP EVENT IF EXISTS ev_call_trans_proc; 
    CREATE EVENT IF NOT EXISTS ev_call_trans_proc
        ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
        DO call transamount();
    
    SELECT * FROM bank1;
    SELECT * FROM bank2;
    

      

      



    本博客文章未经许可,禁止转载和商业用途!

    如有疑问,请联系: 2083967667@qq.com


  • 相关阅读:
    [BZOJ 4318] OSU!
    [BZOJ 4720][NOIP 2016] 换教室
    [Tyvj 1729] 文艺平衡树
    [BZOJ 1500]维修数列 [Splay Tree从进阶到住院]
    [学习笔记] CDQ分治 从感性理解到彻底晕菜
    [COGS 1752] 摩基亚Mokia
    [Tyvj 1730] 二逼平衡树
    [学习笔记] Splay Tree 从入门到放弃
    [Tyvj 1728] 普通平衡树
    [BZOJ 3594] 方伯伯的玉米田
  • 原文地址:https://www.cnblogs.com/rask/p/8276828.html
Copyright © 2011-2022 走看看