zoukankan      html  css  js  c++  java
  • 九、顺序表和单链表的对比分析

    1、如何判断某个数据元素是否存在于线性表中?

    find()操作:

    • 可以为线性表List增加一个查找操作

    • int find(const T& e)const;

      • 参数:待查找的数据元素

      • 返回值:

        大于0:数据元素在线性表中第一次出现的位置

        -1:数据元素不存在

    针对基础数据类型,首先在顶层父类List中增加一个虚函数 virtual int find(const T& e) const = 0;,然后在各子类中实现这个函数

    // 顺序表中的实现 SeqList.h
    int find(const T& e) const  // O(n)
    {
        int ret = -1;
        for(int i = 0; i < m_length; i++)
        {
            if(m_array[i] == e)
            {
                ret = i;
                break;
            }
        }
    
        return ret;
    }
    
    // 单链表中的实现 LinkList.h
    int find(const T& e) const
    {
        int ret = -1;
        int i = 0;
        Node* node = m_header.next;
        while(node)
        {
            if(node->value == e)
            {
                ret = i;
                break;
            }
            else
            {
                node = node->next;
                i++;
            }
        }
        return ret;
    }
    

    针对自定义类类来说

    class Test
    {
        int i;
    public:
        Test(int v = 0)
        {
            i = v;
        }
    };
    int main()
    {
        Test t1(1);
        Test t2(2);
        Test t3(3);
        LinkList<Test> list; 
        
        return 0;
    }
    
    // 会报一个错,错误的地方在LinkList.h
    int find(const T& e) const
        {
    ......
            while(node)
            {
                if(node->value == e)	// 这句话报错
            }
    ......
        }
    /* 报错的原因是:
    node->value 和 e都是Test对象,并没有重载Test类的"=="操作符,在这里编译器不知道如何去比较这两个Test对象
    */
    

    解决方案1:在Test类中直接重载相等比较操作符==

    class Test
    {
        int i;
    public:
        Test(int v = 0)
        {
            i = v;
        }
        bool operator == (const Test& t)
        {
            return (i == t.i);
        }
    };	
    

    这样就可以编译通过,但是这样写存在着一个问题,我们的本意是定义一个单链表对象,保存的对象元素是Test对象,并没有进行实际的查找,也就是说还没想用==操作符来比较,我们就要去在Test类中重载==操作符,这意味着,但凡我们想要定一个这样的保存自定义类类型的单链表对象,我们就必须在自定义类类型中重载==操作符。这样find()查找函数的便利性还没有体现出来,编译错误先出现了,find操作使我们自定义类类型的时候也更加麻烦了。
    但是find操作还是应该存在的,需要解决的问题是,使find函数依然存在,但是自定义类类型的时候也不需要每次都重载,只在需要用到查找函数的类类型时再重载。

    思路:在顶层父类中实现操作符==!=的重载,定义类时,都继承于这个父类,这样类模板中的find()函数实现就可以通过编译。如果自定义类类型需要用到这个find()函数时,再重载操作符==实现相等比较逻辑。

    // Object.h
    class Object
    {
    public:
    ...
        bool operator == (const Object& obj);
        bool operator != (const Object& obj);
    ...
    };
    // Object.cpp
    bool Object::operator == (const Object& obj)
    {
        // 默认实现的方式最好就是通过比较地址
        return (this == &obj);
    }
    bool Object::operator != (const Object& obj)
    {
        return (this != &obj);
    }
    
    // 使用的时候,就需要在定义自定义类类型的时候继承Object父类
    class Test : public Object
    {
        int i;
    public:
        Test(int v = 0)
        {
            i = v;
        }
    };
    
    // 这样,这句话就不会出现编译错误了
    LinkList<Test> list;
    
    // 下面再考虑查找find函数
        Test t1(1);
        Test t2(2);
        Test t3(3);
        LinkList<Test> list;
    
        list.insert(0,t1);
        list.insert(0,t2);
        list.insert(0,t3);
        
    	list.find(t3);
    // 查找的依据应该是Test内的i的值,此时就需要在Test类中重载"=="操作符, 提供具体的相等比较逻辑
    class Test : public Object
    {
    public:
    ...
        bool operator ==(const Test& t)
        {
            return (i == t.i);
        }
    ...
    };
    

    顶层父类中重载的== != 只是提供了一个默认的相等比较符的实现方式,是为了类模板中编译通过,针对某个具体的自定义类类型的时候,如果需要用到==!=时,需要在自定义类中重载这两个操作符,因为默认的逻辑不是我们需要的相等或不等比较的实现逻辑。

    2、顺序表和单链表的对比分析

    操作 SeqList LinkList
    insert O(n) O(n)
    remove O(n) O(n)
    set O(1) O(n)
    get O(1) O(n)
    find O(n) O(n)
    length O(1) O(1)
    clear O(1) O(n)

    从时间复杂上来说,单链表和顺序表比起来并不高效,但是工程上单链表用得比顺序表更多

    顺序表的整体时间复杂度比单链表要低,那么单链表还有使用价值吗?

    实际工程开发中,时间复杂度只是效率的一个参考指标

    • 对于内置基础类型,顺序表和单链表的效率不相上下
    • 对于自定义类类型,顺序表再效率上低于单链表

    效率的深度分析

    • 插入和删除:

      • 顺序表:涉及大量数据对象的复制操作
      • 单链表:只涉及指针操作,效率与数据对象无关

      基础内置类型如int,顺序表的复制操作只涉及到4个字节,而单链表涉及的是指针操作,4字节或8字节,两者效率相差不大

      自定义类类型:非常复杂的类类型,涉及深拷贝的类类型,采用顺序表来存储,但凡到插入和删除一个对象,就要进行很多次大数据对象的复制,还是深拷贝对象的复制,这个大数据对象占用的空间可能会很大,此时顺序表的插入和删除,效率极低;如果采用单链表来存储的话,就只涉及指针操作,效率和具体的数据对象类型是无关的

    • 数据访问

      • 顺序表:随机访问,可直接定位数据对象,内部实现是用原生数组来做的,定位的时候基本不耗时,时间复杂度是常量
      • 单链表:顺序访问,必须从头访问数据对象,无法直接定位

    工程开发中的选择:

    • 顺序表:
      • 数据元素的类型想对简单,不涉及深拷贝
      • 数据元素相对稳定,访问操作远多于插入和删除操作
    • 单链表:
      • 数据元素的类型相对复杂,复制操作相对耗时
      • 数据元素不稳定,需要经常插入和删除,访问操作比较少

    3、小结

    线性表中元素的查找依赖于相等比较符==

    顺序表适用于访问需求量较大的场合(随机访问)

    单链表适用于数据元素频繁插入删除的场合(顺序访问)

    当数据类型相对简单时,顺序表和单链表的效率不相上下

  • 相关阅读:
    Construct Binary Tree from Preorder and Inorder Traversal
    Construct Binary Tree from Inorder and Postorder Traversal
    Maximum Depth of Binary Tree
    Sharepoint 2013 创建TimeJob 自动发送邮件
    IE8 不能够在Sharepoint平台上在线打开Office文档解决方案
    TFS安装与管理
    局域网通过IP查看对方计算机名,通过计算机名查看对方IP以及查看在线所有电脑IP
    JS 隐藏Sharepoint中List Item View页面的某一个字段
    SharePoint Calculated Column Formulas & Functions
    JS 两个一组数组转二维数组
  • 原文地址:https://www.cnblogs.com/chenke1731/p/9500703.html
Copyright © 2011-2022 走看看