链表快还是数组快? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
3dwelcome
V2EX    算法

链表快还是数组快?

  •  
  •   3dwelcome 2022-04-18 10:11:51 +08:00 8764 次点击
    这是一个创建于 1321 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近在 B 站看一些 C++并发教学视频,我发现在服务器很多算法里,非常喜欢用链表。

    然而由于现代 CPU 设计高速缓存的原因,我网上查性能对比环节,几乎 90%情况下,都是数组性能比链表好。



    https://dzone.com/articles/c-benchmark-%E2%80%93-stdvector-vs

    那么问题来了,是不是书本上链表算法已经过时,大部分情况下,用数组替代会更好一些呢?
    67 条回复    2022-09-05 16:04:27 +08:00
    xiaowei0823
        1
    xiaowei0823  
       2022-04-18 10:16:07 +08:00 via iPhone
    数组查询效率高,插入删除效率还是链表更优
    helone
        2
    helone  
       2022-04-18 10:16:13 +08:00
    但是数据并非只查询啊,也要增加删除
    duke807
        3
    duke807  
       2022-04-18 10:18:24 +08:00 via Android
    方便用的候然首用

    用不方便的候才用表(譬如要放中的某元素,或者在中插入一新元素)

    想一步加快速度,用 rb-tree 之的
    haisayu
        4
    haisayu  
       2022-04-18 10:20:03 +08:00
    conclusion
    to conclude, we can get some facts about each data structure:

    - std::vector is insanely faster than std::list to find an element
    - std::vector always performs faster than std::list with very small data
    - std::vector is always faster to push elements at the back than std::list
    - std::list handles large elements very well, especially for sorting or inserting in the front
    these are the simple conclusions on usage of each data structure:

    - for number crunching : use std::vector
    - for linear search : use std::vector
    - for random insert/remove : use std::list (if data size very small (< 64b on my computer), use std::vector)
    - for big data size : use std::list (not if intended for searching)

    你给的链接说的挺清楚了。不同场景不一样。
    3dwelcome
        5
    3dwelcome  
    OP
       2022-04-18 10:20:49 +08:00


    再发一张图,这是链表引以为傲的快速插入,理论上应该远快于数组。然而事实上,性能测试里还是被数组给灭掉了。
    senghoo
        6
    senghoo  
       2022-04-18 10:20:54 +08:00
    主要看使用情景:
    1. 数据量
    2. 长度是否固定
    3. 是否会有随机读写
    4. 是否有随机位置插入,删除
    等等。。
    BeautifulSoap
        7
    BeautifulSoap  
       2022-04-18 10:22:04 +08:00
    不知道 lz 说的教程的场景和应用具体是怎样的

    数组面对长度不确定、需要截断拼接、往中间插入 /删除 N 个元素等等操作的时候性能是很低的(你需要新建个数组然后把旧数据都搬过去,当然也有针对个别情况比如数组内插入或删除元素有高性能的优化算法)
    3dwelcome
        8
    3dwelcome  
    OP
       2022-04-18 10:22:06 +08:00
    @helone
    @xiaowei0823 你们没看完文章,随机插入测试环节,还是数组快。虽然这点很反直觉。。
    villivateur
        9
    villivateur  
       2022-04-18 10:24:40 +08:00 via Android
    @3dwelcome 插入 1000 个数太少了,一级缓存都不止这么点,可以试试一百万这个数量级
    duke807
        10
    duke807  
       2022-04-18 10:25:15 +08:00 via Android
    @3dwelcome

    插入快?你用的是假

    真中插入一元素,插入之後的所有元素都要一次 copy 移动存位置
    3dwelcome
        11
    3dwelcome  
    OP
       2022-04-18 10:26:37 +08:00
    @haisayu “你给的链接说的挺清楚了。不同场景不一样。”

    推荐 big data size 采用 std::list, 可是正常情况谁会直接存大对象。

    一般大数据,不论是用数组还是链表,都是直接存对象指针的。

    也是说,基本没有 big data size 的情况。。
    mainjzb
        12
    mainjzb  
       2022-04-18 10:27:21 +08:00
    ? 明显是楼主没看完文章
    增加到 1kb 的时候链表赢了
    takato
        13
    takato  
       2022-04-18 10:29:35 +08:00
    取决于最经常的操作是什么样的,许多数据结构在不同的方面表现都有所不同。

    即使仅仅是查找操作,我们这里可能要考虑这样的问题:
    高速缓存是否也是一种空间换时间的策略?
    高速缓存是否是无限的?
    假如数据量超过缓存以后性能会如何变化?
    duke807
        14
    duke807  
       2022-04-18 10:30:03 +08:00 via Android
    你的接是 vector vs list ,都不是好吧

    c/c++ 的是似 xxx_type xxx_variable[100]; 的
    3dwelcome
        15
    3dwelcome  
    OP
       2022-04-18 10:35:15 +08:00
    @duke807 “想一步加快速度,用 rb-tree 之的”

    rb-tree 底层应该也是链表之类实现的,毕竟是基于节点的算法。

    我在想特定的条件下,红黑树实现改成数组,也许对性能影响也不大?
    iqoo
        16
    iqoo  
       2022-04-18 10:39:51 +08:00
    iptables vs nftables
    duke807
        17
    duke807  
       2022-04-18 10:42:29 +08:00 via Android
    @3dwelcome
    “一般大数据,不论是用数组还是链表,都是直接存对象指针的。”

    只是你人喜好

    我喜直接把大存到,而不是通指

    在 MCU 境(或者 rt preempt linux 用空),我喜申一全局用做存分配,每存都是固定大小的一元素,可以避免存碎片,同可以保性。具做法是上初始化的候,把每元素成表,在一叫 free 的表,其它 free head 上取下存使用,用完到 free head 。
    xtreme1
        18
    xtreme1  
       2022-04-18 10:44:34 +08:00
    @3dwelcome
    rbtree 当然可以用数组实现,但 rbtree 最大高度可以到 2*log2(n+1),
    所以要存 1000 个 node ,你就要分配 1048576 个 node 的内存
    villivateur
        19
    villivateur  
       2022-04-18 10:46:44 +08:00
    @duke807 真相了,list 和 vector 好像理论上都是链表
    vishun
        20
    vishun  
       2022-04-18 10:46:50 +08:00
    @3dwelcome 主要是链表要想插入前要先查找啊,查找也是 o(n),如果通过索引等其它方式能直接找到位置的话那绝对是链表快,所以也不是很反直觉啊。
    3dwelcome
        21
    3dwelcome  
    OP
       2022-04-18 10:48:22 +08:00
    @xtreme1 rbtree 是自平衡的树吧,感觉正常来用,不会达到最大高度。
    BeautifulSoap
        22
    BeautifulSoap  
       2022-04-18 10:48:53 +08:00
    @3dwelcome 我觉得老哥你可以学一下数据结构,了解下 Vector 这东西底层是怎么实现的。

    随机性这东西是链表的弱项,链表每次访问指定节点都需要从表头 or 表尾重新遍历,所以复杂度是 O(n),而数组随机访问的复杂度是 O(1)。但是相应的,数组(这里是 Vector )插入数据最坏的情况下是需要将整个数组内容拷贝到新数组的。

    链表适合极其大量数据,并且经常需要插入删除,但是又不是彻彻底底完全随机的那种。
    duke807
        23
    duke807  
       2022-04-18 10:49:01 +08:00 via Android
    直接用,增元素,除了有要拷

    有元素的引用者也受到影,因引用可能失效

    於 list (或 rbtree ),怎增元素,有元素的使用者也不受到影
    yehoshua
        24
    yehoshua  
       2022-04-18 10:50:43 +08:00 via Android
    链表适合数据多,经常插入排序等维护。从计算机体系来说,数组是直接访问,链表需要查询。所以一般情况下数组速度会快很多。但是数组维护起来很麻烦。如果是经常需要维护的数据,数组并不合适。
    BeautifulSoap
        25
    BeautifulSoap  
       2022-04-18 10:51:45 +08:00
    @villivateur 咦,难道我数据结构学错了,我印象里 c++的 Vector 不是基于数组(array)的吗
    duke807
        26
    duke807  
       2022-04-18 10:52:48 +08:00 via Android
    @xtreme1 你 rb tree 一定有什解,存 1000 node ,只需要 1000 node 的存用,head 本身存用忽略不
    3dwelcome
        27
    3dwelcome  
    OP
       2022-04-18 10:55:31 +08:00
    @duke807 “有元素的引用者也受到影,因引用可能失效”

    所以我数组一般都是存指针,指针不会失效。

    而且数组还有一点好处,可以排序后二分法查找,查找元素速度比 rbtree 还要快。
    darknoll
        28
    darknoll  
       2022-04-18 11:00:07 +08:00
    如果只是存取,那当然是数组快
    xuanbg
        29
    xuanbg  
       2022-04-18 11:12:29 +08:00
    增删链表优于数组,改查数组优于链表。
    villivateur
        30
    villivateur  
       2022-04-18 11:16:21 +08:00 via Android
    @BeautifulSoap 噢噢,看来是我搞错了
    duke807
        31
    duke807  
       2022-04-18 11:20:00 +08:00 via Android   3
    @3dwelcome

    之,要增操作的,用 低效且不雅,你用 vector 相於把份不雅包藏了

    另外,你只存指本身也不雅,多一次中就降低一份效率和降低代可性

    於方面,你可以看一下 linux kernel 的表方式,重是使用 container_of 宏一切得雅和高效

    就是, c 言表的,是在 node 象面放一 void * 指,指向用

    而 linux 的,是反在用 struct 的任意位置,放一 list node 成,省掉了 void * 指

    嫌 linux 代多,可以看我 mcu 的 bootloader 代,和 linux 代似,重看 device_init 面,是怎把 成 表,用做存分配:

    https://github.com/dukelec/stepper_motor_controller/blob/master/mdrv_bl/usr/app_main.c
    表本身的在目:
    https://github.com/dukelec/cdnet/tree/master/utils
    Leviathann
        32
    Leviathann  
       2022-04-18 11:44:12 +08:00
    还是根据容量和操作的频率来决定
    这类调优都应该以基准测试的结果为前提
    northernlights0
        33
    northernlights0  
       2022-04-18 13:55:40 +08:00
    比如要通过线性表实现一个队列,有大量的 push_back, pop_front 操作,就定是链表占优

    另外排序二分是快,但是数组插入你就慢了
    GrayXu
        34
    GrayXu  
       2022-04-18 14:01:19 +08:00   1
    晕了,这贴里不少人没学过数据结构的样子。。
    3dwelcome
        35
    3dwelcome  
    OP
       2022-04-18 14:12:01 +08:00
    @northernlights0 "比如要通过线性表实现一个队列,有大量的 push_back, pop_front 操作,就肯定是链表占优"

    可以分而治之的,把一个超级大链表,转换成 N 个固定大小的数组集合。只对小数组进行局部操作,感觉也不会太慢。

    理论上大家都知道链表插入快,而实际上 CPU 设计的高速缓存,天然就对数组有极强的亲和性。

    而且在大部分情况下,查询的操作频率,要远远高于插入和删除。
    zhangchongjie
        36
    zhangchongjie  
       2022-04-18 14:33:30 +08:00
    要分情况说好坏吧,凡是没绝对
    msaionyc
        37
    msaionyc  
       2022-04-18 14:45:26 +08:00 via iPhone   4
    数组和链表在内存空间利用率方面的差别楼主不考虑吗?

    另外我礼貌问下,楼主大学读的是计算机专业吗?没有其他的意思,感觉你看问题有点想当然,对数据结构的理解比较浅了。这些性能测试只能当作一种参考,真正什么场景用什么数据结构,开发人员心里要有数的,我看你上面回复的队列 /栈的问题,要分而治之用数组代替链表,我觉得这是为了不用链表而不用链表。

    另外我再说一点,你看的是 cpp 这种偏低级一点的语言了,对一些高级语言来说,数组可能没法很好地利用 cache ,这时如果是在增删条件比较多的情况下难道也要用数组替换链表吗?
    msaionyc
        38
    msaionyc  
       2022-04-18 14:49:15 +08:00 via iPhone   2
    而且,帖子里有几位说到点子上了,你全都没有回复,我觉得有点奇怪
    msaionyc
        39
    msaionyc  
       2022-04-18 14:53:34 +08:00 via iPhone   1
    我一直认为,发表这种“xxx 过时”,“a 不如 b”这种话题应该非常慎重,尤其是技术方面

    楼主不妨自己测试一下,就用 cpp ,写简单的代码测试做对比就能达到目的
    jedihy
        40
    jedihy  
       2022-04-18 15:10:36 +08:00
    标题是链表快还是数组快。benchmark 放的是 vector 快还是 list 。
    sdushn
        41
    sdushn  
       2022-04-18 15:28:59 +08:00
    我理解是各有优点,实际使用中如果数据量小,在现代 CPU 的加成下,用什么都行,但是在复杂场景下就要想办法组合使用了吧
    3dwelcome
        42
    3dwelcome  
    OP
       2022-04-18 15:35:20 +08:00
    @msaionyc "这时如果是在增删条件比较多的情况下难道也要用数组替换链表吗?"

    不回复一些楼,又不是代表我持反对意见。

    我只想讨论大概率使用场景。你如果写一个只有中间 insert 的对比场景,那肯定是链表比数组更占优势。

    链表在数据结构里地位,和数组一样重要,我没怀疑这点。发帖的主要目的,只是想说明在现代 CPU 硬件设计上,对链表不友好,对数组更友好。相同场景下,优先考虑数组,仅此而已。
    3dwelcome
        43
    3dwelcome  
    OP
       2022-04-18 15:49:04 +08:00
    @msaionyc “我一直认为,发表这种“xxx 过时”,“a 不如 b”这种话题应该非常慎重,尤其是技术方面”

    以前是面向对象编程,现在时代变了,是面向硬件编程。

    举个例子,有个叫 Odin 的新编程语言( http://odin-lang.org/),这个就是为了优化硬件,而诞生的编程语言,所谓的 Data-Oriented Language 。

    你可以照本宣科,表示大学书本数据结构里,链表 O(1)插入性能好。可真实高性能编程中,SoA ( array of structures )的地位,要远高于 AoS 。你说为什么,就是因为 SoA 设计成面向 CPU 编程,有更好缓存亲和性,自然就有更好的性能。
    msaionyc
        44
    msaionyc  
       2022-04-18 16:02:28 +08:00 via iPhone
    “ 数组和链表在内存空间利用率方面的差别楼主不考虑吗?”
    我觉得我第一句已经说出一些问题了。

    我之所以发三条是我觉得你说话很不负责任,单纯凭借这篇性能测试就提出自己的观点,就认为链表过时。

    另外我认为你评价我“照本宣科”也很不负责任,照本宣科是贬义词,你看完我上面提到的内存方面的问题了吗?
    3dwelcome
        45
    3dwelcome  
    OP
       2022-04-18 16:21:18 +08:00
    @msaionyc 内存空间利用率具体指什么?没太搞懂,按理说数组比链表要节约内存。

    链表你还要多一个 next 指针才行。
    haah
        46
    haah  
       2022-04-18 16:24:25 +08:00   1
    还是哈希快!
    msaionyc
        47
    msaionyc  
       2022-04-18 16:36:58 +08:00 via iPhone
    数组必须要提前申请内存空间,没用满时等于空了的部分是浪费了的。

    另外,插入操作可能会导致数组长度不够用,需要重新申请空间,原来的 delete 掉。

    不论是平时大部分人都在做的业务场景,还是操作系统内部对于内存的管理,对于任务的调度,数组和链表都有各自的应用场景,你居然会根据一篇性能测试说出链表过时,太匪夷所思了
    3dwelcome
        48
    3dwelcome  
    OP
       2022-04-18 16:46:59 +08:00
    @msaionyc

    “需要重新申请空间,原来的 delete 掉。”, 旧空间也可以不删除的,多个子数组拼成一个大数组就可以不用删掉了。

    “数组必须要提前申请内存空间,没用满时等于空了的部分是浪费了的。”
    这句话也不对,操作系统对于应用程序申请了,但没有实际使用的内存,是不会真实分配的。只有第一次赋值或者初始化时,才会分配物理内存。

    总的来说,还是链表占用内存更多。一个 next 指针需要 8 字节,对比一个 1M 大小的数组,链表比数组要整整多出 8M 额外内存。
    dqzcwxb
        49
    dqzcwxb  
       2022-04-18 18:07:25 +08:00
    数组查找效率高,链表增删效率高
    这不是入门知识?
    llh880808
        50
    llh880808  
       2022-04-18 18:35:42 +08:00   1
    @3dwelcome 只考虑绝对内存占用,链表是比数组多,但是

    一方面要考虑链表结点的数据部分有多少,不能认为链表结点只存储一个整形数,如果数据部分足够大,指针部分浪费的空间就微不足道了,当然绝对来看内存占用还是比数组多

    另一方面,数组占用的内存必须是地址连续的,在系统启动一定时间后,有可能没有足够大的连续内存,而链表可以有效利用零散的内存

    所以,
    链表是不是比数组占用内存多?是的
    链表是不是比数组浪费内存?不一定,我倾向于否
    pursuer
        51
    pursuer  
       2022-04-18 21:26:53 +08:00
    @3dwelcome 多个子数组拼成大数组不就是数组组成的 list 了吗。实际优化过程中也是可以采用数组组成的 list 来实现速度和存储的平衡的。
    数组插入比链表快是利用了高速缓存的特性,但有些嵌入式设备高速缓存都没有,链表的优势就体现了。
    Huozy
        52
    Huozy  
       2022-04-18 21:29:33 +08:00
    @llh880808 #50 老哥也是真的认真在回复。op 就是在杠啊“旧空间也可以不删除”,数组占用连续地址这个问题在 op 眼中是完全不考虑的。
    Mystery0
        53
    Mystery0  
       2022-04-18 22:19:28 +08:00 via Android
    多个小的数组拼成一个大的数组,那还是数据结构里面的数组吗,况且多个小数组怎么拼起来?用指针链起来?
    dcsuibian
        54
    dcsuibian  
       2022-04-18 22:24:00 +08:00   1
    数组查询 O(1),插入 O(n)。链表查询 O(n),插入 O(1)。但你误解链表的这个 O(1),这个 O(1)指的是你已经拿到了中间某个节点的指针,然后想往附近插入 /删除一个元素的情况。

    你看的那两张随机插入和随机删除,是以下情况:
    随机给一个位置 i ,然后让 list 和 vector 进行插入 /删除操作。那么这时候时间复杂度就不是 O(1)了。因为链表还要从头一直往后找,找到位置 i 的那个节点,即有 O(n)部分,但真正的插入 /删除还是 O(1)的。而 vector 找起来很快,耗时的是移动后面的元素(不考虑再分配的话)。

    你应该看 random insert 最后一张图,那张才是链表的正确应用,文章也有提到:
    if the iterator was already known (no need for linear search), it would be faster to insert into a list than into the vector.
    所以并不反直觉。

    如果你的应用场景并没有把查找的那个 O(n)去掉,那么数组更快也很正常。
    3dwelcome
        55
    3dwelcome  
    OP
       2022-04-18 22:39:44 +08:00
    @Mystery0 "况且多个小数组怎么拼起来?用指针链起来?"

    用二维指针就可以,比如 int** array 就能把数组缝合起来。或者更简单的形式,用 vector<int*>这种。

    你面向硬件编程,把大数组切分成小数组是必然的。操作系统是有 4k 之类内存区块概念的,分配的数组不能太大,不能太小,性能才最好。
    hitmanx
        56
    hitmanx  
       2022-04-18 23:02:03 +08:00
    @3dwelcome 听上去有点类似 STL 的 dequeue ,https://jingyan.baidu.com/article/358570f67b4ebcce4724fcb3.html

    这种 hybrid 的数据结构看上去很好,实际上经常是两头都不讨好。所以实际利用率很低
    3dwelcome
        57
    3dwelcome  
    OP
       2022-04-18 23:34:26 +08:00
    @hitmanx 别人不知道,我代码里很多这种“缝合数组”。

    原因就是在长度增加同时,能避免旧数组失效,这特性对我挺重要的。
    Caturra
        58
    Caturra  
       2022-04-18 23:38:41 +08:00
    高速缓存在并发的条件下不一定是“高速”的,并发写操作可能对数组这种连续分配的内存造成频繁误伤,这可能是楼主提到的服务器中算法喜欢用链表的原因(虽然我不了解是否真的倾向于用链表,以及链表和数组的区别对于重 IO 服务来说是否有一丁点影响)

    只考虑通常意义下的性能的话,一般数组实现的数据结构还是优于链表实现。以前打算法竞赛很喜欢把一堆链 /结点的平衡树改写成数组实现(因为数据大小都是已知的,数组的范围肯定能确认),跑分基本都是数组要快一截,数据量级大概是增删改查十万到百万每秒
    Caturra
        59
    Caturra  
       2022-04-18 23:44:07 +08:00
    @hitmanx STL 实现常人很难理解,不看点资料都不知道为啥要搞这么诡异的事物。印象中 deque 的奇妙设计好像是为了避免迭代器失效才这么写的,性能可能就凑合水平(可是凑合归凑合,为啥 stack 默认是由 deque 来适配?还是难理解)
    Cola98
        60
    Cola98  
       2022-04-19 08:39:15 +08:00
    帖子看下来了,如果只是回答数组和链表谁快,这没有说操作,怎么对比?如果是删除和添加一个元素,数组需要移动位置,所以它的时间复杂度高,而链表只需要修改一个节点。如果是查询,因为数组的元素都是挨着的,速度比链表快。关于 vector ,这是一个动态数组,底层还是数组。。
    lifanxi
        61
    lifanxi  
       2022-04-19 09:03:11 +08:00
    @3dwelcome
    你似乎有个误解,认为 std::vector 可以通过拼接串联小数组的方式来进行优化,减少扩容时复制数据的开销,但这是不对的,C++标准上强要求 std::vector 的内存必须是连续的。如果不连续,它就不叫“数组”,也不叫“std::vector”。

    为了保证这个连续性,所以数组或 std::vector 在插入数据或扩容时,势必需要进行数据复制,这在数据量大 /对象复杂的时候有不可忽略的性能开销。

    所以其实你贴的文章中说得很清楚,在多插入场景下,std::vecotr 只有在非常小数据量的情况下,性能才比 std::list 要好。这一点无论从理论上还是实践中都可以得到证实。

    在实际开发中,是不是因为自己的场景下数组比链表快那么一点点就一定要用数组呢?我觉得不对的。写代码时更多要考虑代码所要表达的思想,如果你代码的逻辑是一个典型的链表更优场景,就应该在代码中用链表。这样的代码长远来看才更好维护。如果确实为了极致性能想改成用数组,也需要加注释保证以后别人在数据集变大遇到性能问题时可以及时定位,甚至加 fallback 逻辑自动保证在大数据集情况下不会带来严重的性能回退。
    summerLast
        62
    summerLast  
       2022-04-19 09:30:41 +08:00
    @3dwelcome 数组是连续的,计算机架构里面有用空间换时间的多级 cache ,整块的空间更容易被 cache 提高命中率,其中的关键是 cache 和 内存的访问速度不是一个数量级,数组更容易被 cache , 如果没有 cache 或数组较大时链表插入更优秀
    summerLast
        63
    summerLast  
       2022-04-19 09:31:09 +08:00
    @summerLast 内存空间连续的
    ExplorerLog
        64
    ExplorerLog  
       2022-04-19 09:40:07 +08:00
    std::array
    std::vector
    std::list
    3dwelcome
        65
    3dwelcome  
    OP
       2022-04-19 09:52:56 +08:00
    @lifanxi “你似乎有个误解,认为 std::vector 可以通过拼接串联小数组的方式来进行优化”

    不以长短论英雄。缝合小数组(deque)也是数组里的一个分支。

    13cm 和 18cm 哪个更强?不好说的。

    链表还有一个最大的问题,是不好维护。你数组还能设个越界报错,新手链表用不好,指针就在天上乱飞。
    xtinput
        66
    xtinput  
       2022-04-19 14:32:52 +08:00   1
    内存读写速度成倍提升,CPU 运算速度增长缓慢
    数组慢的地方是增删,增删主要是内存读写
    高频增删用链表
    高频读取用数组
    stuazt
        67
    stuazt  
       2022-09-05 16:04:27 +08:00
    链表快还是数组快。。。这个学算法和数据结构的时候,不是 101 问题吗
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2655 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 44ms UTC 14:27 PVG 22:27 LAX 06:27 JFK 09:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86