C++數據結構——表
1.簡介
形如A1,A2,、、、、An是一個一般的表,我們說這個表的大小為n。而且我們將大小為0的表叫做空表。
2.基本結構
首先我們來講講表的兩種基本實現方式:順序結構和鏈式結構
順序結構:順序結構使用的是數組來實現各種數據結構,它的好處是能夠快速的找到對應的元素,缺點是數組要求我們提前給出表的大小(這是非常嚴重的局限,尤其是你根本不確定表的大小的時候)並且插入和刪除操作會消耗線性時間(在數組中進行插入和刪除,后面的元素都要跟着向后/向前挪動一位,不要小看這個消耗,在你插入刪除操作頻繁時,這樣的消耗是巨大的)。
鏈式結構:鏈式結構使用的是帶有指針的結構實現,由於指針可以不連續存儲,所以可以避免順序結構的插入刪除操作所花的線性開銷,缺點是不能夠直接通過下標查找(對比數組的查找),所以我們會專門寫一個查找函數。
由於順序結構可以直接使用數組實現且基本原理與鏈式結構相同,且缺點更為明顯所以我們一般不使用順序結構實現表,並且在本作者數據結構專欄中都會使用鏈式結構(除非順序結構實現有獨特之處)。
鏈表分為:單鏈表、雙向鏈表、單雙向循環鏈表等,它們的基本結構圖如下:
單鏈表
雙向鏈表
單向循環鏈表
在鏈表的創建過程中,我們基本都會創建一個頭結點,頭結點的作用是人為的給我們的鏈表找到入口,會給你接下來的操作帶來很大的便利,推薦使用頭結點。
3.頭插法與尾插法
頭插法
尾插法
上圖中的方法就是頭插法與尾插法的過程示例,步驟分別用編號表示
①:首先將待插入的結點連接到后結點
②:前結點連接到待插入結點
③:將原本前結點與后結點的連接刪除
我們可以發現兩種插入方式的步驟都是一樣的,有區別的是插入時的位置(頭插法一直在頭結點后插入,尾插法一直在表的末尾插入)和插入后結點編號的順序(頭插法是4321,尾插法是1234),在使用時我們需要注意需求選擇不同的插入方式,比如表中我們可以選擇尾插法,這樣我們在遍歷表時我們可以得到順序的編號。
4.表的基本操作
表的基本操作有增(Insert)、刪(Delete)、查(Find)、判空(isEmpty)、判斷末尾(isLast)
4.1鏈表的類型申明
typedef struct node* Node;
struct node{
int Element;
Node next;
};
4.2判空函數
//判斷鏈表是否為空
bool isEmpty(Node head){
return head->next==NULL;//如果頭結點的下一個為空,證明為空鏈表
}
4.3判斷末尾函數
//判斷當前結點是否為末尾結點
bool isLast(Node L){
return L->next==NULL;//如果當前結點的下一個為空,那么當前結點即為末尾結點
}
你突然發現,怎么判空函數和判斷末尾的函數是一樣的?它們的形式是一樣的,但是含義卻不一樣,判空函數傳進來的結點參數必須是頭結點,只有當頭結點的下一個結點為空的時候鏈表才是空的,而判斷末尾函數所傳進來的結點參數可以是鏈表中的任意一個結點,所以通常還需要遍歷結點。
4.4插入函數
//插入一個值為x的結點,插入到參數結點的后面
void Insert(int x,Node L){
Node p=new node;
p->Element=x;
p->next=L->next;//圖中:步驟1
L->next=p;//步驟2、3,將前結點連接到待插入結點,那么前結點與后結點的連接自然就斷了
}
頭插法和尾插法在傳參時結點參數應該為頭結點,尾插法傳入末尾結點
4.5查找函數
//查找值為x的前驅結點
Node find(int x,Node L){
Node p;
p=L;
while(p->next!=NULL&&p->next->Element!=x){
p=p->next;
}
return p;
}
思考:為什么我們要找值為X的前驅結點,而不是它本身
4.6刪除函數
想要刪除一個結點,只需要將它的前驅結點和后驅結點鏈接起來,然后刪除它本身就可以了
//刪除一個值為x的結點
void Delete(int x,Node L){
if(isEmpty(L))//如果鏈表為空,則直接返回
return ;
Node p=new node;
p=findx(x,L);//找到值為X的前驅結點
if(!isLast(p)){//
Node temp=new node;
temp=p->next;//temp結點是結點2的一個復制品
p->next=temp->next;//步驟①完成時,P與2結點、2與3結點之間的鏈接自然斷裂
delete(temp);
}
}
思考:我們為什么不直接用結點2找到結點3,而是用一個它的復制品
5.完整代碼
#include<iostream>
using namespace std;
typedef struct node* Node;
struct node{
int Element;
Node next;
};
bool isEmpty(Node head){
return head->next==NULL;
}
bool isLast(Node L){
return L->next==NULL;
}
Node findx(int x,Node L){
Node p;
p=L;
while(p->next!=NULL&&p->next->Element!=x){
p=p->next;
}
return p;
}
void insert(int x,Node L){
Node p=new node;
p->Element=x;
p->next=L->next;
L->next=p;
}
void Delete(int x,Node L){
if(isEmpty(L))
return ;
Node p=new node;
Node temp=new node;
p=findx(x,L);
if(!isLast(p)){
temp=p->next;
p->next=temp->next;
delete(temp);
}
}
void print(Node L){//輸出函數
while(L->next!=NULL){
cout<<L->next->Element<<" ";
L=L->next;
}
cout<<endl;
}
int main(){
Node head=new node;
head->next=NULL;//一定要指向NULL,因為判空等函數以此作為條件,還有些含義自己可以琢磨一下
// 頭插法
// for(int i=0;i<5;i++){
// insert(i+1,head);
// }
// 尾插法
Node flag=new node;
flag=head;//flag作為末尾結點傳入
for(int i=0;i<5;i++){
insert(i+1,flag);
flag=flag->next;
}
print(head);
insert(6,head);
print(head);
Delete(3,head);
print(head);
return 0;
}
循環和雙向鏈表基於上面的代碼,自己可以嘗試一下。
第一次寫博客,有什么不足的地方歡迎大家指出