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

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、小结

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

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

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

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

原文地址:https://www.cnblogs.com/chenke1731/p/9500703.html

时间: 2024-10-14 08:23:24

九、顺序表和单链表的对比分析的相关文章

线性表的链式存储——顺序表和单链表的对比分析

1,线性表两种实现: 1,顺序表: 2,单链表: 2,问题: 1,如何判断某个数据元素是否存在线性表中? 1,遍历线性表: 2,封装这个遍历操作: 3,遗失的操作 - find: 1,可以为线性表(List)增加一个查找操作: 2,int find(const T& e) const; 1,参数: 1,待查找的数据元素: 2,返回值: 1,>= 0:数据元素在线性表中第一次出现的位置: 2,-1:数据元素不存在: 3,遍历中会有相等和不等操作符,当比较对象是类的时候,需要类继承自 Objec

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

问题:如何判断某个数据元素是否存在于线性表中? for(int i=0; i<list.length(); i++) { if(list.get(i) == v) { cout << "find it" <<endl; } } 遗失的操作——find-可以为线性表(List)增加一个查找操作-int find(const T& e) const; 参数: 待查找的数据元素 返回值: >=0: 数据元素在线性表中第一次出现的位置 -1:数据元素

第二十三课 顺序表和单链表的对比分析

问题: 如何判断某个数据元素是否存在于线性表中? 查找一个元素是否在线性表中,每次查找就需要使用for循环,因此,我们需要封装一个find成员函数. 在List.h中添加find函数: SeqList.h中添加find的实现: LinkList.h中添加find的实现: 测试程序如下: 1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLi

数据结构----顺序表与单链表(JAVA)

下面为学习顺序表和单链表的一些基本操作函数: 1 public class SeqList<T> extends Object { 2 protected int n; 3 protected Object[] element; 4 5 public SeqList(int length) { 6 this.element = new Object[length]; 7 this.n = 0; 8 } 9 10 public SeqList() { 11 this(64); 12 } 13 1

数据结构实验报告-实验一 顺序表、单链表基本操作的实现

实验一    顺序表.单链表基本操作的实现   l  实验目的 1.顺序表 (1)掌握线性表的基本运算. (2)掌握顺序存储的概念,学会对顺序存储数据结构进行操作. (3)加深对顺序存储数据结构的理解,逐步培养解决实际问题的编程能力. l  实验内容 1. 顺序表 1.编写线性表基本操作函数: (1)InitList(LIST *L,int ms)初始化线性表: (2)InsertList(LIST *L,int item,int rc)向线性表的指定位置插入元素: (3)DeleteList1

顺序表与单链表

初学数据结构,对顺序表与单链表的概念与操作有点模模糊糊,为此特地查阅了资料,了解了不少,以下是对他们的总结: 顺序表内存中地址连续,长度不变更,支持随机查找,可以在时间复杂度O(1)内查找元素,适用于大量访问元素而少量增添或删除的程序.链表内存地址非连续,长度可以变化,算法时间复杂度为O(n),适用于需要进行大量增添或删除元素操作,而对访问无需求的程序. 下面来看看他们的基本操作(查找,删除,插入)的区别: 顺序表的查找: int GetElem(Seqlist L,int i,DateType

顺序表与单链表的区别及优缺点

线性表之顺序表与单链表的区别及优缺点 尊重原创 -->原文链接 --> 侵权删 这里比较的是基于C语言实现的顺序表与单链表,与其他语言的实现可能会有差异,但我相信语言是相通的,它们的实现机制应该也差不多. 顺序表描述: 顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构.只要确定了起始位置,表中任一元素的地址都通过下列公式得到:LOC(ai)=LOC(a1)+(i-1)*L 1≤i≤n 其中,L是元素占用存储单元的长度. 单链表描述: 单链表

数据结构复习---顺序表和单链表

1.前言: 最近比较浮躁,想学习一门新的技术却总是浅尝辄止,遇到不懂的地方就想跳过去,时间长了,心态就有点崩了.静下心来,去喝了几碗心灵鸡汤.鸡汤博主感动到了我:"无专注,无风景.不要太贪心,一次只做一件事,而且只做最重要的事.".于是乎,我把家里翻了个底朝天,找到了我垫在床底下的<数据结构>这本书,觉得自己是时候静下心来好好复习一下基础了.今天刚看到顺序表和链表,把我的学习心得记录在这里.也希望自己能坚持,老老实实的把这本书复习完. 2.数据结构的重要性: 讲一门知识之前

用模板实现顺序表与单链表

//顺序表 #include<iostream> using namespace std; template<typename T> class SeqList { public: SeqList(); ~SeqList(); SeqList(const SeqList& s); SeqList& operator=(const SeqList &s); void _CheakCapacity(); void PushBack(const T& x)