数组(array)
1 数组的出现背景
数组是为了解决连续存储一组同类型数据元素的问题。
2 数组是什么?
数组是用一段地址连续的存储单元依次存储一组相同类型数据元素的线性表结构。
3 数组的特点
1)随机存取:根据数组的下表对数据元素随意读取或修改,时间复杂度为O(1)。随机访问是数组这个数据结构最大的特点;
2)操作简单:由于数据元素的存储单元是连续的,所以对数据元素的操作简单方便。
4 线性表顺序存储的关键及实现
4.1 关键点
1)线性表长度小于等于数组长度。线性表长度是当前线性表中数据元素的个数,它随着数据元素的插入和删除变化;而数组长度是线性表初始化时设置好的,一般是不变的。
2)线性表的个数从1开始计数,而数组的下表是从0开始的;
3)线性表的插入和删除操作要搬移数据(除非是对表尾操作),时间复杂度为O(n)。插入操作时将待插入位置及以后数据元素从末尾数据元素向后进行搬移;删除操作时将待删除删除元素取出,将后续数据元素依次向前搬移。对表尾元素操作特别考虑。
4.2 实现
1 #ifndef SEQUENCELIST_H 2 #define SEQUENCELIST_H 3 4 typedef int ElemType; 5 6 class SequenceList 7 { 8 private: 9 ElemType* m_pData; 10 int m_length; //线性表长度 11 int m_maxSize; //数组长度 12 13 public: 14 SequenceList(int maxSize); //初始化线性表 15 ~SequenceList(); //销毁线性表 16 void ClearList() { m_length = 0; }; //清空线性表 17 bool GetElem(int i, ElemType* pElem) const; //读取线性表的第i个数据元素 18 bool InsertList(int i, ElemType elem); //往第i个数据元素前插入新元素 19 bool DeleteList(int i, ElemType* pElem); //删除第i个数据元素,并将值返回 20 void VisitList() const; //遍历线性表元素 21 int ListLength() const { return m_length; }; //线性表长度 22 }; 23 24 #endif
1 #include "pch.h" 2 #include "SequenceList.h" 3 #include <iostream> 4 5 SequenceList::SequenceList(int maxSize) //初始化线性表 6 { 7 m_pData = new ElemType[maxSize]; 8 m_length = 0; 9 m_maxSize = maxSize; 10 } 11 12 SequenceList::~SequenceList() //销毁线性表 13 { 14 delete[] m_pData; 15 } 16 17 bool SequenceList::GetElem(int i, ElemType* pElem) const //读取线性表的第i个数据元素 18 { 19 if (i <= 0 || i > m_length) 20 return false; 21 22 *pElem = m_pData[i - 1]; 23 return true; 24 } 25 26 bool SequenceList::InsertList(int i, ElemType elem) //往第i个数据元素前插入新元素 27 { 28 if (m_maxSize == m_length) //线性表已满 29 return false; 30 if (i < 1 || i > m_length + 1) //不在线性表范围内 31 return false; 32 33 if (i <= m_length) //不在表尾时 34 { 35 for (int n = m_length; n >= i; --n) //搬移数据元素 36 m_pData[n] = m_pData[n - 1]; 37 } 38 m_pData[i - 1] = elem; 39 ++m_length; 40 41 return true; 42 } 43 44 bool SequenceList::DeleteList(int i, ElemType* pElem) //删除第i个数据元素,并将值返回 45 { 46 if (!m_length) //如果线性表为空 47 return false; 48 if (i < 1 || i > m_length) 49 return false; 50 51 *pElem = m_pData[i - 1]; 52 if (i < m_length) //如果不是表尾元素 53 { 54 for (int n = i; n < m_length; ++n) 55 m_pData[n - 1] = m_pData[n]; 56 } 57 --m_length; 58 59 return true; 60 } 61 62 void SequenceList::VisitList() const //遍历线性表元素 63 { 64 std::cout << "The element of list: "; 65 for (int i = 0; i < m_length; ++i) 66 std::cout << m_pData[i] << ' '; 67 std::cout << std::endl; 68 }
测试代码:测试平台为Visual Studio 2017
1 #include "pch.h" 2 #include "SequenceList.h" 3 #include <iostream> 4 5 using namespace std; 6 7 int main() 8 { 9 SequenceList list(10); 10 list.InsertList(1, 1); 11 list.InsertList(2, 2); 12 list.InsertList(3, 3); 13 list.VisitList(); 14 list.InsertList(2, 10); 15 list.VisitList(); 16 ElemType elem; 17 list.DeleteList(3, &elem); 18 list.DeleteList(3, &elem); 19 cout << "链表长度为:" << list.ListLength() << endl; 20 list.VisitList(); 21 22 return 0; 23 }
测试结果:
5 插入与删除操作的改善
5.1 插入操作的改善
如果数组中的数据元素是有序的,那么在某个位置插入数据元素时,就必须搬移数据。但是如果数组中存储的数据是无序的,数组只是被当作存储数据的集合,在这种情况下,为了避免大量的数据搬移,我们可以将插入位置的数据元素放到表尾,再将待插入数据元素插入指定位置。
5.2 删除操作的改善
在一些特殊场景下,并不一定要追求数组中数据的连续性,我们可以将多次删除操作集中一起执行,删除的效率会提高很多。例,
a | b | c | d | e | f | g | h |
现在我们要删除a,b,c三个元素,为了避免后继元素被搬移三次,我们可以对要删除的元素进行标记,当数组没有剩余空间时,发起一次真正的删除。
6 与已存在实现的对比
对于数组类型,C++提供了容器类,有array和支持动态扩容的vector。对于什么时候需要我们自己实现,什么时候直接使用已有实现,有以下几点:
1)如果数据大小事先已知,对数据的操作非常简单,用不到容器类提供的大部分方法,可以直接使用数组;
2)当表示多维数组时,用数组会比较直观;
3)除非是做非常底层的开发,否则直接使用现有的容器类就足够了。
该篇博客是自己的学习博客,水平有限,如果有哪里理解不对的地方,希望大家可以指正!