顺序表(SeqList)的理解以及实现

数据结构是计算机存储,组织数据的方式

  • 空间占用尽量少,以节省计算机内存。
  • 数据操作尽可能快速,涵盖数据访问、添加、删除、更新等。
  • 提供简洁的数据表示和逻辑信息,以便算法高效运行。
  • 链表相较于数组,在数据添加和删除操作上更加便捷,但牺牲了数据访问速度。
  • 图相较于链表,提供了更丰富的逻辑信息,但需要占用更大的内存空间。

可以看出:数据结构的设计是一个充满权衡的过程

其实数组就是简单的数据结构,一个数组就可以去维护同类型的n个数据,顺序表的底层就是数组,顺序表是对数组的封装


顺序表的概念与结构:

线性表:

线性表( linear list )是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
顺序表的物理结构是连续的,逻辑结构一定是连续的

顺序表的分类:

顺序表的底层是数组,那么顺序表和数组有什么区别呢?

顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查 接⼝ (升级了,也方便了对数组的功能使用)
  • 静态顺序表

静态顺序表(在栈上开空间)缺陷:空间给少了不够⽤,给多了造成空间浪费
  • 动态顺序表

 动态内存管理,开辟空间:按需申请空间(在堆上开空间)


顺序表带来的问题:

  1. 中间/头部的插⼊删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷⻉数据,释放旧空间。会有不⼩的消耗。
  3. 增容⼀般是呈2倍的增⻓,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插⼊了5个数据,后⾯没有数据插⼊了,那么就浪费了95个数据空间。 
  4. 如何保证程序结束后,历史通讯录信息不会丢失?

为了更直接的体现问题以及思考,下面基于动态顺序表来实现通讯录进而更好的解决问题:

功能要求:

  • ⾄少能够存储100个⼈的通讯信息
  • 能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
  • 增加联系⼈信息
  • 删除指定联系⼈
  • 查找制定联系⼈
  • 修改指定联系⼈
  • 显⽰联系⼈信息

历史数据不丢失,要注意指针(传址调用,而不是传值调用),引用便是很好的解决方法(4)

顺序表(例如数组)确实存在一些性能问题,特别是在进行中间或头部的插入和删除操作时,因为这些操作通常需要移动大量元素以维持顺序表的连续性,导致时间复杂度为O(N)。此外,顺序表的增容操作也可能导致性能问题,因为需要申请新空间、复制数据和释放旧空间,这个过程同样需要时间,并且可能会造成空间浪费。


         针对这些问题,可以考虑以下几种解决方案:

  1. 使用动态数组:动态数组(如C++中的`std::vector`或Java中的`ArrayList`)可以在一定程度上解决增容问题。它们会在需要时自动增加容量,并且通常会使用一种非线性增长策略(例如,每次增加50%的容量),以减少增容操作的频率和空间浪费。
  2. 使用链表:链表可以提供更灵活的插入和删除操作,因为它们不需要移动其他元素。链表的插入和删除操作的时间复杂度为O(1),前提是你已经有了要操作的节点的引用。
  3. 使用平衡树结构:如红黑树、AVL树等,它们可以在保持元素有序的同时,提供对数级别的插入、删除和查找操作。
  4. 使用哈希表:哈希表提供了平均情况下常数时间的插入、删除和查找操作,但它们不保持元素的顺序。
  5. 使用跳表:跳表是一种概率型数据结构,它可以提供快速的查找、插入和删除操作,并且保持了有序性。

    关于如何保证程序结束后,历史通讯录信息不会丢失,可以采取以下措施:

  • 持久化存储:将通讯录信息存储在文件系统或数据库中,这样即使程序关闭,信息也不会丢失。
  • 定期备份:定期将通讯录信息备份到其他存储介质,如外部硬盘或云存储服务。
  • 事务日志:使用事务日志记录所有的修改操作,以便在程序崩溃或数据损坏时可以从日志中恢复数据。
  • 数据同步:如果通讯录信息分布在多个设备或服务上,确保它们之间定期同步,以防止单点故障导致数据丢失。

选择合适的数据结构和存储策略,可以有效地解决顺序表带来的问题,并确保数据的安全性和可靠性。

通讯录的实现代码:

插入操作:

铺垫:

bool IsFull()
{
    return _size == _capacity;
}

size_t size()
{
    return _size;
}
// 开空间
void reserve(size_t n)
{
    size_t old_size = size();
    T* tmp = new T[n];
    for (size_t i = 0; i < old_size; i++)
    {
        tmp[i] = _arr[i];// 调用赋值:释放旧空间,拷贝新空间
    }
    delete[] _arr;

    _arr = tmp;
    _capacity = n;
}
  • 尾插
// 尾插一个数据
void push_back(const T& x)
{
    if (IsFull())
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _arr[_size] = x;
    _size++;
}
  • 头插
//头插一个数据
void push_front(const T& x)
{
    if (IsFull())
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    for (int i = size(); i  > 0; i--)
    {
        _arr[i] = _arr[i - 1];
    }
    _arr[0] = x;
    _size++;
}
  • 指定位置前插入 
//在指定位置之前插入数据
void insert(size_t pos, const T& x)
{
    assert(pos <= size() && pos >= 0); 
    if (IsFull())
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    for (int i = size(); i > pos; i--)
    {
        _arr[i] = _arr[i - 1];
    }
    _arr[pos] = x;
    _size++;
}

删除操作:

  • 尾删
//尾删
void pop_back()
{
    assert(_arr && _size > 0);
    _size--;
}
  • 头删
//头删
void pop_front()
{
    assert(_arr && _size > 0);
    for (int i = 0; i <size()-1; i++)
    {
        _arr[i] = _arr[i + 1];
    }
    _size--;
}
  • 在指定位置删除数据
//指定位置删除
void erase(size_t pos)
{
    assert(pos >= 0 && pos < size());
    for (int i = pos; i < size() - 1; i++)
    {
        _arr[i] = _arr[i + 1];
    }
    _size--;
}

顺序表实现代码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace home
{
    // 动态顺序表
    template<class T>
    class SeqList
    {
    public:
        // 全缺省构造函数
        SeqList(T* arr = nullptr, size_t size = 0, size_t capacity = 0)
            :_arr(arr)
            , _size(size)
            , _capacity(capacity)
        {}
        // 析构函数
        ~SeqList()
        {
            delete[] _arr;
            _arr = nullptr;
            _size = _capacity = 0;
        }
        const T& operator[](size_t pos) const
        {
            assert(pos < _size);//防止越界
            return _arr[pos];
        }
        bool IsFull()
        {
            return _size == _capacity;
        }
        bool empty()
        {
            return _size == 0;
        }
        size_t size()
        {
            return _size;
        }
        // 开空间
        void reserve(size_t n)
        {
            size_t old_size = size();
            T* tmp = new T[n];
            for (size_t i = 0; i < old_size; i++)
            {
                tmp[i] = _arr[i];// 调用赋值:释放旧空间,拷贝新空间
            }
            delete[] _arr;

            _arr = tmp;
            _capacity = n;
        }
        // 尾插一个数据
        void push_back(const T& x)
        {
            if (IsFull())
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }
            _arr[_size] = x;
            _size++;
        }
        //头插一个数据
        void push_front(const T& x)
        {
            if (IsFull())
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }
            for (int i = size(); i > 0; i--)
            {
                _arr[i] = _arr[i - 1];
            }
            _arr[0] = x;
            _size++;
        }
        //尾删
        void pop_back()
        {
            assert(_arr && _size > 0);
            _size--;
        }
        //头删
        void pop_front()
        {
            assert(_arr && _size > 0);
            for (int i = 0; i < size() - 1; i++)
            {
                _arr[i] = _arr[i + 1];
            }
            _size--;
        }
        //在指定位置之前插入数据
        void insert(size_t pos, const T& x)
        {
            assert(pos <= size() && pos >= 0);
            if (IsFull())
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }
            for (int i = size(); i > pos; i--)
            {
                _arr[i] = _arr[i - 1];
            }
            _arr[pos] = x;
            _size++;
        }
        //指定位置删除
        void erase(size_t pos)
        {
            assert(pos >= 0 && pos < size());
            for (int i = pos; i < size() - 1; i++)
            {
                _arr[i] = _arr[i + 1];
            }
            _size--;
        }
        //通通删掉
        void clear()
        {
            for (int i = 0; i < size(); i++)
            {
                pop_back();
            }
            _size = 0;
        }
        //查找
        int find(const T& x)
        {
            assert(_arr);
            for (int i = 0; i < size(); i++)
            {
                if (x == _arr[i])
                {
                    return i;
                }
            }
            return -1;
        }
        //打印
        void Print()
        {
            assert(_arr);
            for (int i = 0; i < size(); i++)
            {
                cout << _arr[i] << " ";
            }
            cout << endl;
        }
    private:
        T* _arr;
        size_t _size;// 顺序表当前有效的数据个数
        size_t _capacity;// 当前顺序表的容量
    };
}

通讯录项目:

#include"SeqList.h"
#include<string>
struct character_information
{
	string _name;
	string _gender;
	size_t _age;
	string _phone_code;
	string _address;
};
int main()
{
	cout << "---------通讯录----------" << endl;
	home::SeqList<struct character_information> sl;
	int n = 0;
	cin >> n;
	while (n--)
	{
		struct character_information infor;
		cout << "name:" << endl;
		cin >> infor._name;
		cout << "gender:" << endl;
		cin >> infor._gender;
		cout << "age:" << endl;
		cin >> infor._age;
		cout << "phone_code:" << endl;
		cin >> infor._phone_code;
		cout << "address:" << endl;
		cin >> infor._address;
		sl.push_back(infor);
	}
	//sl.Print();原有不可用
	//...
	return 0;
}

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值