數組
數組是一種線性表資料結構。它用一組連續記憶體空間,來存儲一組具有相同資料類型資料。
1.線性表:資料存儲像一條線一樣的結構,每個線性表上的資料最多隻有前和後的兩個方向,如數組、連結清單、隊列、棧等都是這種結構,是以實作的數組的動态操作,其他結構也可輕易的類似實作。更重要的是,在這之後看源碼就可大大降低難度。(部落客自己看的是STL源碼剖析)
2.非線性表:如二叉樹、堆、圖等。
3連續記憶體空間和相同資料類型:當數組作插入、删除操作時,為了保證資料的連續性,往往需要做大量的資料搬移工作,效率很低。
動态數組功能實作
1.數組初始化
考慮到擴容時資料搬移可能會發生的記憶體洩露,部落客這裡采用兩隻手的原則,即設定一個記憶體标志位 ItemsFlag 。當 ItemsFlag = 0,using preitems;當 ItemsFlag = 1,using items。下文擴容部分有具體實作。預設數組容量10。
enum { MAX = 10 };
explicit GenericArray(int ss = MAX);
template
GenericArray::GenericArray(int ss) : capacity(ss),counts(0)
{
itemsFlag = 0;
preitems = new T[capacity];
items = nullptr;
}
2.析構函數
釋放記憶體。
template
GenericArray::~GenericArray()
{
if (preitems != nullptr)
delete[]preitems;
if (items != nullptr)
delete[]items;
}
3.檢查下标
檢查要操作的下标是否在數組容量範圍内。
template
bool GenericArray::checkIndex(int index)
{
if (index < 0 || index >= capacity)
{
int cap = capacity - 1;
cout << "Out of the range! Please ensure the index be
in 0 ~ " << cap << '\n';
return false;
}
return true;
}
4.擷取元素數目和容量、判斷數組空和滿
int count()const { return counts; }
int getCapacity()const { return capacity; }
bool isEmpty()const { return counts == 0; }
bool isFull()const { return counts >= capacity; }
5.取索引對應值、按索引修改值、列印輸出、是否包含某值
template
T GenericArray::get(int index)
{
if (!itemsFlag)
return preitems[index];
else
return items[index];
}
void GenericArray::set(int index, T elem)
{
if(checkIndex(index))
{
if (!itemsFlag)
preitems[index] = elem;
else
items[index] = elem;
return;
}
}
template
void GenericArray::printArray()const
{
for (int i = 0; i < counts; i++)
if (!itemsFlag)
cout << preitems[i] << '\t';
else
cout << items[i] << '\t';
cout << '\n';
return;
}
template
bool GenericArray::contains(T arr)
{
for (int i = counts - 1; i >= 0; i--)
if (!itemsFlag)
{
if (arr == preitems[i])
return true;
}
else
{
if (arr == items[i])
return true;
}
return false;
}
6.查找某值下标、删除某值
查找某值的下标時,要考慮到該值在數組中是否重複,是以部落客用了一個結構體 findArrIndex 來存儲該值重複的次數和對應的下标。
struct findArrIndex
{
int numIndex;
int *findIndex;
};
template
void GenericArray::find(T arr, findArrIndex *ps)
{
ps->findIndex = new int[counts];
ps->numIndex = 0;
for (int i = 0, j = 0; i < counts; i++, j++)
if (!itemsFlag)
{
if (arr == preitems[i])
{
(ps->findIndex)[j] = i;
(*ps).numIndex++;
cout << i << '\t';
}
}
else
if (arr == items[i])
{
(ps->findIndex)[j] = i;
(*ps).numIndex++;
cout << i << '\t';
}
cout << '\n';
return;
}
template
void GenericArray::removeElement(findArrIndex *ps)
{
for (int i = ps->numIndex; i > 0; i--)
remove((ps->findIndex)[i - 1]);
delete[](ps->findIndex);
}
template
void GenericArray::set(int index, T elem)
{
if(checkIndex(index))
{
if (!itemsFlag)
preitems[index] = elem;
else
items[index] = elem;
return;
}
}
7.擴容
添加資料操作時需判斷數組容量是否足夠,若不夠需考慮擴容。
template
void GenericArray::renewCapacity()
{
cout << "The array's capacity is small! Renew capacity.\n";
if (capacity < 1000)
capacity = capacity << 1;
else
capacity = capacity >> 1 + capacity;
if (!itemsFlag)
{
itemsFlag = 1;
items = new T[capacity];
for (int i = 0; i
*(items + i) = *(preitems + i);
//items[i]=proitems[i];
//cout << items << '\n';
//cout << preitems << '\n';
delete[]preitems;
preitems = nullptr;
}
else
{
itemsFlag = 0;
preitems = new T[capacity];
for (int i = 0; i
*(preitems + i) = *(items + i);
delete[]items;
items = nullptr;
}
}
8.添加資料:數組添加資料包括按索引下标插值、數組頭插值、數組尾插值。實質上後兩種都可以通過調用按索引下标插值函數實作。前文也提到過,數組添加操作中複雜的是大量的資料搬移工作:将某個元素按索引下标插入到數組第k個位置,需要将k ~ n部分的元素向後搬移一位,然後插入元素,更新元素數目。若插入到數組尾,時間複雜度O(1);插入到數組頭,時間複雜度O(n);插入的平均時間複雜度為(1+2+…+n)/n = O(n)。
另外,還有一個優化問題:若數組是無序數組,則插入時不需要搬移資料:若将某個元素插入到數組第k個位置,首先将該位置的元素移動到數組末尾,然後将待插入元素插入到第k個位置,時間複雜度O(1)。
template
void GenericArray::add(int index, T elem)
{
if (isFull())
{
cout << "Array is full!" << '\n';
renewCapacity();
}
if (checkIndex(index))
if(!itemsFlag)
{
for (int i = counts; i > index; i--)
preitems[i] = preitems[i - 1];
preitems[index] = elem;
}
else
{
for (int i = counts; i > index; i--)
items[i] = items[i - 1];
items[index] = elem;
}
counts++;
return;
}
template
void GenericArray::addFirst(T elem)
{
add(0, elem);
}
template
void GenericArray::addLast(T elem)
{
add(counts, elem);
}
9.删除資料:數組删除資料包括按索引下标删除、數組頭删除、數組尾删除。實質上後兩種都可以通過調用按索引下标删除函數實作。與前文類似,數組删除操作中複雜的也是大量的資料搬移工作:按索引下标将某個元素删除,需要将k+1 ~ n部分的元素向前搬移一位,更新元素數目。若删除數組尾,直接元素數目減一即可,時間複雜度O(1);删除數組頭,時間複雜度O(n);删除的平均時間複雜度(1+2+…+n)/n = O(n)。
另外,有一個優化問題:如果想多次删除數組中的值,可以先對要删除的值做好标記,做完标記後一次删除,這樣就大大減少了搬移的次數。
template
T GenericArray::remove(int index)
{
if (!isEmpty())
{
if (checkIndex(index))
{
if (!itemsFlag)
{
T temp = preitems[index];
for (int i = index+1; i < counts; i++)
preitems[i - 1] = preitems[i];
counts--;
return temp;
}
else
{
T temp = items[index];
for (int i = index + 1; i < counts; i++)
items[i - 1] = items[i];
counts--;
return temp;
}
}
}
else
{
cout << "Array is empty!" << '\n';
return -1;
}
}
template
T GenericArray::removeFirst()
{
return remove(0);
}
template
T GenericArray::removeLast()
{
return remove(counts - 1);
}
好啦,基本上就這麼多了。
最後總結一下,多看源碼還是很重要的。
以上就是本文的全部内容,希望對大家的學習有所幫助,也希望大家多多支援腳本之家。