線性表__資料結構筆記
線性表是最基本、最簡單、也是最常用的一種資料結構。線性表(linear list)是資料結構的一種,線性表是n個資料元素構成的有限序列,表中元素的數量為表的長度。長度為零的表為空表。
特點:
-
集合中必存在唯一的一個“第一元素”。
-
集合中必存在唯一的一個 “最後元素” 。
-
除最後一個元素之外,均有唯一的後繼(後件)。
-
除第一個元素之外,均有唯一的前驅(前件)。
邏輯結構圖:
a1————a2————a3————a4--------------an
儲存結構:
一、順序儲存
1.順序表
使用陣列實現,一組地址連續的儲存單元,陣列大小有兩種方式指定,一是靜態分配,二是動態擴充套件。
注:線性表從1開始,而陣列從0開始。
優點:
- 有隨機訪問特性,查詢O(1)時間
- 無需額外空間,儲存密度高
缺點:
- 插入和刪除操作需移動大量元素
- 表的容量難以確定
順序表的實現
用C++的類來實現順序表,由於資料型別不確定,所以採用模板機制。
const int MAXSIZE = 1e5;
template<typename T>
class SeqList {
T data[MAXSIZE];
int length;
public:
SeqList();
SeqList(T a[], int n);
~SeqList();
int GetLength();//求線性表的長度
T Get(int index);//按索引查詢
int Locate(T x);//按值查詢
void Insert(T x, int index);//插入
T Delete(int index);//按位置刪除
bool Empty();//判斷是否為空
void printSetList();//遍歷輸出
};
無參建構函式—初始化順序表
建立一個空的順序表,將length初始化為0即可。
template<typename T>
SeqList<T>::SeqList()
{
length = 0;
}
有參建構函式—建立順序表
將資料元素傳入順序表中,資料元素個數即為順序表長度。如果順序表的儲存空間小於給定的元素個數,此時無法建立順序表。
template<typename T>
SeqList<T>::SeqList(T a[], int n)
{
if (n > MAXSIZE)throw"非法引數!";
for (int i = 0; i < n; i++)
data[i] = a[i];
length = n;
}
解構函式—銷燬順序表
順序表是靜態儲存分配,自動釋放記憶體。所以解構函式為空。
template<typename T>
SeqList<T>::~SeqList()
{
//空
}
求線性表的長度
返回length的值即可
template<typename T>
int SeqList<T>::GetLength()
{
return length;
}
按位查詢
順序表下表為i的元素儲存在陣列中下標為i-1的位置。時間複雜度為O(1)。
template<typename T>
T SeqList<T>::Get(int index)
{
if (index < 0 || index >= length)
throw"非法輸入!";
return data[index - 1];
}
按值查詢
需要對順序表中的元素進行比較,查詢成功返回序號(下標+1),否則返回0。時間複雜的為O(n)。
template<typename T>
int SeqList<T>::Locate(T x)
{
for (int i = 0; i < length; i++)
if (data[i] == x)
return i + 1;
return 0;
}
插入操作
輸人:插入位置i,待插入的元素值x
輸出:如果插人成功,返回新的順序表,否則返回插入失敗資訊
1.如果表滿了,則輸出上溢錯誤資訊,插入失敗;
2.如果元素的插人位置不合理,則輸出位置錯誤資訊,插人失敗;
3.將最後一個元素直至第1個元素分別向後移動一個位置;
4.將元素x填人位置i處;
5.表長加1;
時間複雜度為O(n)
template<typename T>
void SeqList<T>::Insert(T x, int index)
{
if (length = MAXSIZE)throw("上溢!");
if (index < 0 || index >= length)throw("非法輸入!");
for (int i = length; i >= index; i--)
data[i] = data[i - 1];
data[index - 1] = x;
length++;
}
刪除操作
從刪除位置開始,將資料元素前移一位即可,注意下標的表示。時間複雜度為O(n)
template<typename T>
T SeqList<T>::Delete(int index)`在這裡插入程式碼片`
{
if (length = 0)throw("下溢!");
if (index < 0 || index >= length)throw("刪除位置錯誤!");
T x = data[index - 1];
for (int i = index; i < length; i++)
data[i - 1] = data[i];
length--;
return x;
}
判斷順序表是否為空
判斷length的值即可
template<typename T>
bool SeqList<T>::Empty()
{
if (length > 0)
return true;
return false;
}
遍歷輸出
template<typename T>
void SeqList<T>::printSetList()
{
for (int i = 0; i < length; i++)
{
cout << data[i] << " ";
if (i % 5 == 0)
cout << endl;
}
}
二、鏈式儲存
1.單連結串列
單連結串列是用一組任意的儲存單元存放線性表的元素,這組儲存單元可以連續也可以不連續,甚至可以零散分佈在記憶體中的任意位置。
每個儲存單元在儲存資料元素的同時,還必須儲存其後繼元素所在的地址資訊,這個地址資訊稱為指標。這兩部分組成了資料元素的儲存映象,稱為結點。
每個結點的儲存地址存放在其前驅結點的next域中,而第一個元素無前驅,所以設頭指標指向第一個元素所在結點(稱為開始結點),整個單連結串列的存取必須從頭指標開始進行,因而頭指標具有標識一個單連結串列的作用。最後一個元素無後繼,故最後一個元素所在結點(稱為終端結點)的指標域為空,這個空指標稱為尾標誌。
通常在單連結串列的開始結點之前附設一個型別相同的結點,稱為頭結點。也有不帶頭結點的單連結串列,不過其操作更為麻煩,所以不經常使用,我們下面會簡單介紹。如不特殊說明,使用的連結串列都是預設帶頭結點的。
單連結串列的實現
結點的定義
data為資料區,存放資料
next為指標域,存放下一個結點的地址
template<typename T>
struct Node
{
T data;
Node<T>* next;
};
宣告:
template<typename T>
class LinkList {//有首結點
Node<T>* first;//頭指標
public:
LinkList();
LinkList(T a[], int n);//頭插法
LinkList(int n,T a[])//尾插法
T Get(int index);//按位查詢
int Locate(T x);//按值查詢
void Insert(int index, T x);//插入
T Delete(int index);//刪除
void Delete_Data(T x);//按值刪除
void PrintList();//遍歷輸出
int Length();//連結串列長度
int Empty();//判斷連結串列是否為空
~LinkList();//解構函式
};
無參構造
帶頭結點
建立一個帶首節點的空連結串列
給first指標申請一塊記憶體,將其指標域賦空值(代表無後繼結點),即空連結串列
template<typename T>
LinkList<T>::LinkList()
{
first = new Node<T>;
first->next = NULL;
}
不帶頭結點
直接把頭指標賦空值
template<typename T>
LinkList<T>::LinkList()
{
first = NULL;
}
有參構造(頭插法)
帶頭結點
頭插法就是把結點插入到首節點之後
先建立結點s儲存資料
將s的指標域指向原來的第一個結點,即first->next
現在s成為了第一個節點
將first->next指向s
template<typename T>
LinkList<T>::LinkList(T a[], int n)
{
first = new Node<T>;
first->next = NULL;
Node<T>* s = NULL;
for (int i=0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
s->next = first->next;
first->next = s;
}
}
不帶頭節點
把插入的節點作為首結點
template<typename T>
LinkList<T>::LinkList(T a[], int n)
{
first = NULL;
Node<T>* s = new Node<T>;
for (int i = 0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
s->next = first;
first = s;
}
}
有參構造(尾插法)
帶頭結點
尾插法就是在連結串列的尾部插入節點
為了操作方便,這裡需要增加一個尾指標last
定義一個結點s儲存資訊
將尾指標的指標域指向新插入的s
現在結點s才是真正的隊尾
然後再將尾指標指向s
所有元素都插入後,將尾指標的next賦空值
template<typename T>
LinkList<T>::LinkList(int n, T a[])
{
first = new Node<T>;
first->next = NULL;
Node<T>* s = NULL;
Node<T>* last = first;
for (int i = 0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
last->next = s;
last = s;
}
last->next = NULL;
}
不帶頭節點
操作相對來說較複雜一些,第一個節點和後續結點的操作不同
需要先插入第一個結點,再迴圈插入後續結點
template<typename T>
LinkList<T>::LinkList(T a[],int n)
{
Node<T>* s = new Node<T>;
s->data = a[0];
first = s;
Node<T>* last = first;
for (int i = 1; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
last->next=s;
last = s;
}
last->next = NULL;
}
按位置查詢值
這裡需要一個工作指標p和一個計數器count來計數
當count==index-1時,此時的p就是要查詢的結點
輸出p->data
template<typename T>
T LinkList<T>::Get(int index)
{
int count = 0;//計數器
Node<T>* p = first->next;//工作指標
while (p && count < index-1)
{
count++;
p = p->next;//工作指標後移
}
if (!p)throw"查詢失敗";
return p->data;
}
按值查詢位置
還是需要計數器count來計數,當p->next==x時,輸出此時的count
template<typename T>
int LinkList<T>::Locate(T x)
{
Node<T>* p = first->next;
int count = 1;
while (p)
{
if (p->data == x)return count;//查詢成功
count++;
p = p->next;
}
if (!p)throw"查詢失敗";
}
插入操作
有首節點
使用結點s來記錄資料
插入需要查詢到第i-1個結點,然後再修改指標變數的值
將s->next指向下一個結點,即第i-1個結點儲存的地址(next)
然後將第i-1個結點的next指向s
template<typename T>
void LinkList<T>::Insert(int index, T x)
{
Node<T>* p = first;
int count = 0;
while (p && count < index - 1)
{
p = p->next;
count++;
}
if (p)
{
Node<T>* s = new Node<T>;
s->data = x;
s->next = p->next;
p->next = s;
}
else
throw"插入失敗";
}
無首結點
需要判斷插入的位置,插入在第一個元素的操作不同
template<typename T>
void LinkList<T>::Insert(int index, T x)
{
Node* p=NULL;
Node* s = NULL;
int count;
if (index <= 0) throw"位置非法";
if (index == 1)
{
s = new Node<T>;
s->data = x;
s->next = first;
first = s;
return;
}
p = first;
count = 1;
while (p && count < i - 1)
{
p = p->next;
count++;
}
if (!p)throw"位置非法";
else
{
s = new Node<T>;
s->data = x;
s->next = p->next;
p->next = s;
}
}
按位置刪除
刪除操作也是需要找到第i-1個結點
然後將第i-1個結點的next指向第i+1個結點,即第i個結點的next
然後釋放第i個結點的記憶體
template<typename T>
T LinkList<T>::Delete(int index)
{
T x;
Node<T>* p = first;
Node<T>* q = NULL;
int count = 0;
while (p && count < index - 1)
{
p = p->next;
count++;
}
if (!p || !p->next)"刪除失敗";
else
{
q = p->next;
x = q->data;
p->next = q->next;
delete q;
return x;
}
}
按值刪除
先查詢到和資料相等的值所在的結點p
再修改其前驅結點p的的指標域
void LinkList::Delete_Data(int x)//按值刪除
{
Node* p = first;//前驅
Node* q = NULL;//後繼
Node* temp = NULL;
bool flag = false;
while (p->next)
{
if (p->next->data == x)
{
q = p->next;
p->next = q->next;
delete q;
flag = true;
}
p = p->next;
}
if (!flag)throw"刪除失敗";
}
求連結串列的長度
遍歷計數即可
template<typename T>
int LinkList<T>::Length()
{
Node<T>* p = first->next;
int count = 1;
while (p)
{
count++;
p = p->next;
}
return count;
}
判斷連結串列是否為空
判斷first->next是否為空
template<typename T>
int LinkList<T>::Empty()
{
if (first->next == NULL)
return 1;
return 0;
}
輸出連結串列
遍歷輸出即可
template<typename T>
void LinkList<T>::PrintList()
{
Node<T>* p = first->next;
while (p)
{
cout << p->data <<" ";
p = p -> next;
}
cout << endl;
}
解構函式
釋放記憶體
template<typename T>
LinkList<T>::~LinkList()
{
Node<T>* p = NULL;
while (first)
{
p = first->next;
delete first;
first = p;
}
}
2.迴圈連結串列
將單連結串列的頭尾結點連線起來,就是一個迴圈連結串列。沒有增加額外的開銷,為連結串列的操作增添了不少便利。
特點:從連結串列的任意一點出發,都能訪問到表中其他節點。
整體的方法和單連結串列的差異不大,只要稍作修改即可。
構造空表
首尾相連
template<typename T>
CycleLinkList<T>::CycleLinkList()
{
first = new Node<T>;
first->next = first;
}
有參構造(頭插法)
帶頭結點
和單連結串列構造的唯一差距就是將其中一條語句修改
first->next=first
template<typename T>
LinkList<T>::LinkList(T a[], int n)
{
first = new Node<T>;
first->next = first;//修改
Node<T>* s = NULL;
for (int i=0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
s->next = first->next;
first->next = s;
}
}
有參構造(尾插法)
帶頭結點
last->next=first
template<typename T>
LinkList<T>::LinkList(int n, T a[])
{
first = new Node<T>;
first->next = NULL;
Node<T>* s = NULL;
Node<T>* last = first;
for (int i = 0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
last->next = s;
last = s;
}
last->next = first;
}
將單連結串列變為迴圈連結串列
Node*p=first;
while(p->next)
{
p=p->next;
}
p->next=first;
其他的操作與單連結串列的操作基本相同們這裡不再贅述。
3.雙連結串列
在雙連結串列中,每個結點不僅記錄了資料元素,還儲存了前驅結點和後繼結點的地址資訊。能方便的找到前驅節點。
結點構成
template<typename T>
struct Node
{
T data;
Node<T>* pre;
Node<T>* next;
};
宣告
template<typename T>
class DoubleLink {
Node<T>* first;
public:
DoubleLink();
DoubleLink(T a[], int n);
T Get(int index);
int Get_data(T x);
T Delete(int index);
T Delete_data(T x);
void Insert(int index, T x);
int Length();
void Print();
~DoubleLink();
};
無參構造
template<typename T>
DoubleLink<T>::DoubleLink()
{
first = new Node<T>;
first->pre = NULL;
first->next = NULL;
}
頭插法構造
先正向修改指標,再反向修改指標,同時注意判斷p->next是否存在
template<typename T>
DoubleLink<T>::DoubleLink(T a[],int n)
{
first = new Node<T>;
first->pre = NULL;
first->next = NULL;
Node<T>* s = NULL;
for (int i = 0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
s->next = first->next;
first->next = s;
s->pre = first;
if(s->next)
s->next->pre = s;
}
}
尾插法構造
利用尾指標進行操作
template<typename T>
DoubleLink<T>::DoubleLink(T a[], int n)
{
first = new Node<T>;
first->pre = NULL;
first->next = NULL;
Node<T>* s = NULL;
Node<T>* last = first;
for (int i = 0; i < n; i++)
{
s = new Node<T>;
s->data = a[i];
last->next = s;
s->pre = last;
last = s;
}
last->next = NULL;
}
按位置查詢值
和單連結串列操作相同
template<typename T>
T LinkList<T>::Get(int index)
{
int count = 0;//計數器
Node<T>* p = first->next;//工作指標
while (p && count < index-1)
{
count++;
p = p->next;//工作指標後移
}
if (!p)throw"查詢失敗";
return p->data;
}
按值查詢位置
和單連結串列操作相同
template<typename T>
int LinkList<T>::Locate(T x)
{
Node<T>* p = first->next;
int count = 1;
while (p)
{
if (p->data == x)return count;//查詢成功
count++;
p = p->next;
}
if (!p)throw"查詢失敗";
}
插入操作
和單連結串列基本相同,就只有修改指標操作不同
template<typename T>
void DoubleLink<T>::Insert(int index, T x)
{
if (index <= 0)throw"位置非法";
Node<T>* p = first;
int count = 0;
while (p&&count<index-1)
{
p = p->next;
count++;
}
if (p)
{
Node<T>* s = new Node<T>;
s->data = x;
s->next = p->next;
p->next = s;
s->pre = p;
if (s->next)
s->next->pre = s;
}
else
throw"位置非法"
}
按位置刪除
和單連結串列稍微有些不同
不需要找前驅節點,直接找到第index個結點就可以
template<typename T>
T DoubleLink<T>::Delete(int index)
{
T x;
Node<T>* p = first->next;
int count = 0;
while (p && count < index - 1)
{
p = p->next;
count++;
}
if (!p || !p->next)"刪除失敗";
else
{
p->pre->next = p->next;
if(p->next)
p->next->pre = p->pre;
delete p;
return x;
}
}
按值刪除
思路和單連結串列基本上相同,知識細微部分不同
需要注意判斷p->next是否為空
void List::Delete(int x)
{
Node* p=NULL, * q=NULL;
p = first->next;
while (p)
{
if (p->data == x)
{
q = p;
p->pre->next = p->next;
if (p->next != NULL)
p->next->pre = p->pre;
delete q;
}
p = p->next;
}
}
遍歷輸出
和單連結串列操作相同
template<typename T>
void DoubleLink<T>::Print()
{
Node<T>* p = first->next;
while(p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
return;
}
求連結串列的長度
遍歷計數,和單連結串列相同
template<typename T>
int LinkList<T>::Length()
{
Node<T>* p = first->next;
int count = 1;
while (p)
{
count++;
p = p->next;
}
return count;
}
解構函式
和單連結串列相同
template<typename T>
DoubleLink<T>::~DoubleLink()
{
Node<T>* p = NULL;
while (first)
{
p = first->next;
delete first;
first = p;
}
}
總的來說,雙連結串列的操作的實現思路和單連結串列相同,操作上大同小異。
三、順序表和連結串列的比較
時間效能:
若線性表的操作主要是查詢,很少進行刪除、插入時,可以選擇順序表來儲存。
若進行頻繁的插入和刪除操作,適合選擇連結串列來儲存。
空間效能:
當線性表的長度變化不大,且事先容易確定其大小時,適合採用順序表來儲存,否則使用連結串列。
如果你覺得看完這篇文章有收穫,就動動小手點個贊吧!
相關文章
- 資料結構筆記——線性表(中)資料結構筆記
- 資料結構筆記——線性表(下)資料結構筆記
- [資料結構] - 線性表資料結構
- 資料結構 | 線性表資料結構
- 資料結構——線性表資料結構
- 資料結構-線性表資料結構
- 資料結構—線性表資料結構
- Java學習筆記:資料結構之線性表(雙向連結串列)Java筆記資料結構
- 資料結構 - 線性表 - 順序表資料結構
- 【資料結構之線性表總結】資料結構
- 資料結構-線性表、連結串列資料結構
- 資料結構:線性表-例題資料結構
- 考研資料結構-線性表-順序表資料結構
- 【資料結構】線性表-單連結串列資料結構
- 線性結構-線性表
- 資料結構與演算法 - 線性表資料結構演算法
- 資料結構——線性表P35.1資料結構
- 考研資料結構複習之線性表資料結構
- 資料結構線性表兩種方式分享資料結構
- 資料結構與演算法(一):線性表資料結構演算法
- 資料結構基礎學習之線性表資料結構
- 資料結構-第一篇:線性表資料結構
- 【資料結構&演算法】04-線性表資料結構演算法
- 演算法與資料結構(1)--線性表演算法資料結構
- 資料結構與演算法 | 線性表 —— 順序表資料結構演算法
- 資料結構與演算法 | 線性表 —— 連結串列資料結構演算法
- 資料結構筆記資料結構筆記
- 資料結構:特殊的線性表之 棧 & 佇列資料結構佇列
- 資料結構:線性表(Python實現基本操作)資料結構Python
- 資料結構:線性表的順序實現2.2資料結構
- 資料結構實驗三:線性表綜合實驗資料結構
- 資料結構中的線性表程式碼實現資料結構
- 實戰資料結構(7)_線性表的綜合操作資料結構
- 前端資料結構--線性結構-連結串列前端資料結構
- 資料結構與演算法-線性表-單連結串列資料結構演算法
- 資料結構——李超線段樹 學習筆記資料結構筆記
- 資料結構筆記——概述資料結構筆記
- 資料結構筆記——棧資料結構筆記