資料結構與演算法筆記(二) 線性表(陣列描述)
c++常用的資料描述方法是陣列描述和鏈式描述,線性表可以用來說明這兩方法,先介紹陣列描述的線性表。後面再介紹鏈式描述的線性表。
C++ STL容器vector和list相當於線性表的陣列描述和鏈式描述。陣列描述方法將元素儲存在一個數組中,所有元素依次儲存在一片連續的儲存空間,這就是所謂的順序表
資料物件和資料結構:
資料物件是一組例項或值。 // 資料例項 理解:資料物件int, 5是資料物件 int 的例項。 還有string, digit之類的資料物件
資料物件通常會有一系列i相關的操作或者函式,把資料物件的例項轉換為該物件的另一個例項。或者轉化為其他資料物件的例項
資料結構:資料結構是一個數據物件,同時這個物件的例項以及構成例項的元素都存在著聯絡,而這些聯絡有相關的函式決定。
資料結構研究的是資料物件的描述以及相關函式的具體實現。
1.線性表資料結構:
線性表也稱有序表,它的每一個例項都是元素的有序集合。線性表例項的形式,其中,是線性表的元素,i是索引,n是線性表的長度。元素可以看成原子,他們本身的結構金額線性表無關。
線性表可以用抽象資料型別來說明(abstract data type, ADT):即說明它的例項,也說明它的操作
LinearList
{
例項:
有限個元素的集合
操作:
empty();
size();
get(index);
indexOf(x); // 返回元素的索引值
erase(index); // 刪除某個元素
insert(index, x); // 插入元素
output(); // 輸出線性表
建立();
撤銷();
}
可以用醜行類來描述上面的資料型別LineraList
template<typename T>
class linearList
{
public:
virtual ~linearList() {}; // 解構函式
virtual bool empty() const=0;
virtual int size() const=0;
virtual T& get(int index) const=0;
virtual int indexOf(T x) const=0;
virtual void erase(int index) const=0;
virtual void insert(int index, T& x) const=0;
virtual void output() const=0;
}
上面的類相當於資料結構LinearList的基類,這是一個抽象類。
陣列描述:
在陣列描述中,用陣列來儲存線性表的元素。我們需要一個對映,使得陣列的每一個元素對應線性表的一個元素。可以用公式表示為:
location(i) = i
即: 第i個線性表中的元素在陣列中的位置是i.
改變陣列的長度:
增加或者減少新的陣列長度,首先要建立一個具有新長度的陣列,把舊陣列的元素複製到新的陣列。
template<typename T>
void changeLength1D(T*& a, int oldLength, int newLength)
{
if(newLength<0)
throw illegalParameterValue("New length must >= 0");
T* temp = new T[newLength];
int number = min(oldLength, newLength); // 複製的元素個數
copy(a, a+number, temp);
delete []a ; // 釋放老陣列的記憶體空間
a = temp;
}
arrayList類:
定義一個Linearlist(抽象類)的派生類:這個派生類繼承了基類的方法的同時,也要定義一些自己特有的方法
arrayList類的基類:linearlist中定義一些純虛擬函式(注意春熙函式的定義方法),所以linearList類是一個抽象類
虛擬函式virtual functiuonName() const=0表示的是定義為純虛擬函式,這個純虛擬函式是隻讀函式
虛擬函式virtual functiuonName()=0表示的是定義為純虛擬函式,這個純虛擬函式不是隻讀函式
linearlist.h
#ifndef LINEAR_LIST_H
#define LINEAR_LIST_H
#include <iostream>
using namespace std;
template<typename T> // 定義一個抽象類
class linearList
{
public:
// 抽象類中的純虛擬函式
virtual ~linearList() {}; // 解構函式
virtual bool empty() const=0;
virtual int size() const=0;
virtual T& get(int index) const=0;
virtual int indexOf(const T& x) const=0; // 這裡定義的是虛擬函式,虛擬函式virtual functiuonName() const=0表示的是定義為純虛擬函式,這個純虛擬函式是隻讀函式
virtual void erase(int index) = 0; // 這裡定義的是虛擬函式,虛擬函式virtual functiuonName()=0表示的是定義為純虛擬函式,這個純虛擬函式不是隻讀函式
virtual void insert(int index, T x) = 0;
// virtual void output(ostream& out) const=0;
};
#endif
arrayList.h
// 定義模板類: lineaList的派生類
#ifndef ARRAY_LIST_H
#define ARRAY_LIST_H
#include "E:\back_up\code\c_plus_code\digui\external_file\linearlist.h"
#include <iostream>
using namespace std;
template<typename T>
class arrayList : public linearList<T>
{
private: // 資料域
T* element;
int arrayLength; // 一維陣列的長度
int listSize; // 線性表長度
//void checkIndex(int index) const;
public:
arrayList(); // 無參建構函式
arrayList(int capacity); //建構函式
arrayList(const arrayList& array); // 拷貝建構函式
~arrayList(); //解構函式
// ADT方法:abstract data type 抽象資料型別
bool empty() const; // 線性表是否為空
int size() const;
T& get(int index) const;
int indexOf(const T& x) const;
void erase(int index);
void insert(int index, T x);
//void output(ostream& out) const;
// 其他方法
int capacity() const;
// 過載流插入運算子
// friend ostream& operator<<(ostream& out, const arrayList<T>& array_list); // 過載流插入運算子 <<只能以友元函式的形式過載
void output() const;
};
// 模板類的實現
// 無引數建構函式
template <typename T>
arrayList<T>::arrayList()
{
arrayLength = 5; // 初始陣列的大小10
listSize = 0;
element = new T[arrayLength];
}
// 有引數的建構函式
template <typename T>
arrayList<T>::arrayList(int capacity)
{
if(capacity<1)
{
// cout << "The Initial capacity= " << capacity << " Must > 0" << endl;
//throw invalid_argument("The Initial capacity must bigger than zero");
// cout << "Parameter wrong" << endl;
}
this->arrayLength = capacity;
this->listSize = 0;
this->element = new T[arrayLength];
}
// 拷貝建構函式
template <typename T>
arrayList<T>::arrayList(const arrayList& array_list)
{
arrayLength = array_list.arrayLength;
listSize = array_list.listSize;
element = new T[arrayLength];
for(int i=0; i<listSize; i++)
{
element[i] = array_list.element[i];
}
}
// 解構函式
template<typename T>
arrayList<T>::~arrayList()
{
delete [] element;
}
// ADT方法, 抽象資料型別 arralyList基本方法實現
template<typename T>
bool arrayList<T>::empty() const
{
return listSize==0;
}
template<typename T>
int arrayList<T>::size() const // 返回線性表的長度
{
return listSize;
}
template<typename T>
T& arrayList<T>::get(int index) const
{
return element[index];
}
template<typename T>
int arrayList<T>::indexOf(const T& x) const
{
int i;
bool found_flag = false;
for(i=0; i<listSize; i++)
{
if(element[i]==x)
{
found_flag = true;
break;
}
}
return (found_flag)?i:-1;
}
template<typename T>
void arrayList<T>::erase(int index) // 刪除線性表中的某個元素
{
// 新增索引的檢查函式 checkindex中定義一個異常類
for(int i=index; i<listSize-1; i++)
{
element[i] = element[i+1];
}
listSize--;
}
template<typename T>
void arrayList<T>::insert(int index, T x) // 插入一個元素
{
//int old_listSize = listSize; // 線性表的原來長度
//listSize++; // 插入元素後線性表的長度
if(listSize>=arrayLength) // 現象表中的元素個數超出陣列的大小
{
arrayLength *= 2; // 增加陣列的大小
T* old = element;
element = new T[arrayLength]; // 新陣列
//int i;
for(int i=0; i<listSize; i++)
{
element[i] = old[i]; // 先把element中的元素複製過來
}
delete [] old; // 釋放old_ListSize的記憶體
}
// 再執行插入操作
int j;
for(j=listSize-1; j>=index; j--)
{
element[j+1] = element[j];
}
element[j] = x;
listSize++;
}
template<typename T>
int arrayList<T>::capacity() const // 返回陣列的大小
{
return arrayLength;
}
/*
template<typename T>
ostream& operator<<(ostream& out, const arrayList<T>& array_list)
{
for(int i=0; i<listSize; i++)
{
out << element[i] << " ";
if(i%10==0)
out << endl;
}
out << endl;
}
*/
template<typename T>
void arrayList<T>::output() const
{
for(int i=0; i<listSize; i++)
{
cout << element[i] << " ";
if((i+1)%10==0)
cout << endl;
}
cout << endl;
}
#endif
main.cpp
#include <iostream>
#include <string>
#include <time.h>
#include "E:\back_up\code\c_plus_code\digui\external_file\linearlist.h"
#include "E:\back_up\code\c_plus_code\digui\external_file\arraylist.h"
using namespace std;
// 實現友元函式
int main(int argc, char *argv[])
{
arrayList<double> array(3);
for(int i=0; i<10; i++)
{
array.insert(i, i);
}
//array.insert(0, a);
//array.insert(1, b);
array.output();
array.insert(9, 9.9);
array.output();
return 0;
}
上述實現線性表的方法還有一些問題,insert()函式不對,還沒有實現流插入運算子<<的過載
待解決: 模板類的友元函式的定義和實現問題:
再動態的增加陣列的長度的時候,每次為什麼不是+1,+2,而是加倍:
無論陣列每次增加多少,都不影響每一次最壞的插入操作時間 ,但是影響連續插入時的漸進時間複雜度,假設從長度為1的表開始插入資料,每次都插入到表尾,所以不需要移動表裡的元素,時間複雜度是。
假設執行次插入操作,則n次插入的時間為T:
其中A是執行插入操作的時間複雜度:
陣列增加長度的操作,程式碼如下
//int old_listSize = listSize; // 線性表的原來長度
//listSize++; // 插入元素後線性表的長度
if(listSize>=arrayLength) // 現象表中的元素個數超出陣列的大小
{
arrayLength *= 2; // 增加陣列的大小
T* old = element;
element = new T[arrayLength]; // 新陣列
//int i;
for(int i=0; i<listSize; i++)
{
element[i] = old[i]; // 先把element中的元素複製過來
}
delete [] old; // 釋放old_ListSize的記憶體
}
對於B來說,如果陣列長度按照+1,則陣列改變長度的時間是:
則:
如果陣列的長度每次增加兩倍:
n次插入操作,,其中k就是執行陣列擴容的次數,每次擴容的時間複雜度2的k次方,也即陣列擴容前個元素進行復制
所以k次插入陣列擴容的時間複雜度是B,則
所以有:
這就是陣列長度每次都增加兩倍的原因:
-------------------------------------------------------分割線---------------------------------------------------------------
新增新的方法,對arrayList進行修改
1.當線性表中的元素個數小於陣列長度的1/4時,陣列長度減半
2.