基本数据结构(5) —— 有序表

本文介绍了有序线性表和排序表这两种数据结构,详细阐述了它们的基本概念、操作以及实现方式。有序线性表是可搜索容器,提供了查找、插入、删除等操作;而排序表则在插入元素后保持所有元素的有序状态,不支持前插和后插操作。文章以数组表示法为例,展示了这两种数据结构的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 有序线性表

有序线性表是最基本的可搜索容器,其提供除了可搜索容器的操作,还有自己的一些基本操作。那么什么是可搜索容器呢,可搜索容器定义如下:

class SearchableContainer : public virtual Container
{
public:
	virtual bool IsMember (Object const&) const = 0;
	virtual void Insert (Object&) = 0;
	virtual void Withdraw (Object&) = 0;
	virtual Object& Find (Object const&) const = 0;
};


 

也就是说,具有IsMember(判断是否含有某对象)、Insert(插入某对象)、Withdraw(删除某对象)和Find(查找某对象)这四个基本操作的抽象容器就是可搜索容器。

 

那么什么是有序线性表呢?定义如下:

class Position : public Iterator
{
};

class List : public virtual SearchableContainer
{
public:
    virtual Object& operator [] (unsigned int) const = 0;
    virtual Object& operator [] (Position const&) const = 0;
    virtual Position& FindPosition (Object const&) const = 0;
    virtual void Withdraw (Position const&) = 0;
};

class OrderedList : public virtual List
{
public:
    virtual void InsertAfter (Position const&, Object&) = 0;
    virtual void InsertBefore (Position const&, Object&) = 0;
};


 

我们看到有序线性表OrderedList除了具备可搜索容器的基本特性,同时还具有以下操作:

    virtual Object& operator [] :存取表中某一给定位置上的对象。

    virtual Position& FindPosition:查找对象在表中的位置。

    virtual void Withdraw (Position):删除表中某一给定位置上的对象。

    virtual void InsertAfter:把一个对象插在某一给定对象位置之后。

    virtual void InsertBefore:把一个对象插在某一给定对象位置之前。

 

有序线性表的数组表示法:

class ListAsArray : public virtual OrderedList
{
protected:
    Array<Object*> array;

    class Pos;
public:
    ListAsArray (unsigned int);
    // ...
    friend class Pos;
};
ListAsArray::ListAsArray (unsigned int size) :
    array (size)
    {}

void ListAsArray::Insert (Object& object)
{
    if (count == array.Length ())
	throw domain_error ("list is full");
    array [count] = &object;
    ++count;
}

Object& ListAsArray::operator [] (unsigned int offset) const
{
    if (offset >= count)
	throw out_of_range ("invalid offset");
    return *array [offset];
}
bool ListAsArray::IsMember (Object const& object) const
{
    for (unsigned int i = 0; i < count; ++i)
	if (array [i] == &object)
	    return true;
    return false;
}

Object& ListAsArray::Find (Object const& object) const
{
    for (unsigned int i = 0; i < count; ++i)
	if (*array [i] == object)
	    return *array [i];
    return NullObject::Instance ();
}
void ListAsArray::Withdraw (Object& object)
{
    if (count == 0)
	throw domain_error ("list is empty");
    unsigned int i = 0;
    while (i < count && array [i] != &object)
	++i;
    if (i == count)
	throw invalid_argument ("object not found");

    for ( ; i < count - 1U; ++i)
	array [i] = array [i + 1];
    --count;
}
class ListAsArray::Pos : public Position
{
protected:
    ListAsArray const& list;
    unsigned int offset;
public:
    // ...
    friend class ListAsArray;
    friend class SortedListAsArray;
};

Position& ListAsArray::FindPosition (Object const& object) const
{
    unsigned int i = 0;
    while (i < count && *array [i] != object)
	++i;
    return *new Pos (*this, i);
}

Object& ListAsArray::operator [] (Position const& arg) const
{
    Pos const& position = dynamic_cast<Pos const&> (arg);

    if (&position.list != this || position.offset >= count)
	throw invalid_argument ("invalid position");
    return *array [position.offset];
}

void ListAsArray::InsertAfter (
    Position const& arg, Object& object)
{
    Pos const& position = dynamic_cast<Pos const&> (arg);

    if (count == array.Length ())
	throw domain_error ("list is full");
    if (&position.list != this || position.offset >= count)
	throw invalid_argument ("invalid position");

    unsigned int const insertPosition = position.offset + 1;

    for (unsigned int i = count; i > insertPosition; --i)
	array [i] = array [i - 1U];
    array [insertPosition] = &object;
    ++count;
}

void ListAsArray::Withdraw (Position const& arg)
{
    Pos const& position = dynamic_cast<Pos const&> (arg);

    if (count == 0)
	throw domain_error ("list is empty");
    if (&position.list != this || position.offset >= count)
	throw invalid_argument ("invalid position");
    for (unsigned int i = position.offset; i < count-1U; ++i)
	array [i] = array [i + 1];
    --count;
}


 

有序表也可以通过链表来实现,有兴趣的可以自己去研究一下,不再赘述。

2 排序表

排序表是另一种很有用的可搜索容器,它跟有序线性表的区别在于,不提供前插和后插的操作,当插入元素后,表中所有的元素应保持有序状态,来看实现:

class SortedList : public virtual List
{
};


 

这可能是全文中最简单的一个抽象类实现了,其就是一个广义表。来看排序表的数组表示法:

class SortedListAsArray :
    public virtual SortedList, public virtual ListAsArray
{
    unsigned int FindOffset (Object const&) const;
public:
    SortedListAsArray (unsigned int);
    // ...
};

void SortedListAsArray::Insert (Object& object)
{
    if (count == array.Length ())
	throw domain_error ("list is full");
    unsigned int i = count;
    while (i > 0 && *array [i - 1U] > object)
    {
	array [i] = array [i - 1U];
	--i;
    }
    array [i] = &object;
    ++count;
}

unsigned int SortedListAsArray::FindOffset (
    Object const& object) const
{
    int left = 0;
    int right = count - 1;

    while (left <= right)
    {
	int const middle = (left + right) / 2;

	if (object > *array [middle])
	    left = middle + 1;
	else if (object < *array [middle])
	    right = middle - 1;
	else
	    return middle;
    }
    return count;
}

Object& SortedListAsArray::Find (Object const& object) const
{
    unsigned int const offset = FindOffset (object);

    if (offset < count)
	return *array [offset];
    else
	return NullObject::Instance ();
}

Position& SortedListAsArray::FindPosition (
    Object const& object) const
{
    Pos& result = *new Pos (*this);
    result.offset = FindOffset (object);
    return result;
}

void SortedListAsArray::Withdraw (Object& object)
{
    if (count == 0)
	throw domain_error ("list is empty");

    unsigned int const offset = FindOffset (object);

    if (offset == count)
	throw invalid_argument ("object not found");

    for (unsigned int i = offset; i < count - 1U; ++i)
	array [i] = array [i + 1];
    --count;
}


同样,我们也不再赘述链表的实现方案了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值