1. 程式人生 > >資料結構與演算法筆記(二) 線性表(陣列描述)

資料結構與演算法筆記(二) 線性表(陣列描述)

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.