【C++基礎複習01】結構體和連結串列

清少納言_發表於2017-12-16

結構體

結構體是一個可以包含不同資料型別的一個結構,使用修飾符struct進行定義。結構體可以在一個結構中宣告不同的資料型別,並且只有結構相同的結構體變數可以相互賦值。
先來看例子,如下構造一個結構體:

struct student
{
int id;
char name[9];
int Chinese,math,English;
double average;
};

以上就是由個人定義的一個結構體student,其中的屬性(id,Chinese,average)等被稱之為結構體成員,並且有自己的資料型別。需要注意的是,在{}結尾處,要加上一個分號 ; 否則會導致語法錯誤。另外,描述的結構體並不佔用記憶體空間,只有當用這種新的資料型別定義變數,陣列,申請變數或申請堆陣列時候才要分配空間。
接下來我們利用這個結構體來建立一些變數並來訪問,程式碼片段如下:

student s;        //建立一個student物件 
    student *ps=&s;     //定義一個指標變數指向s 
    student &rs=s;      //定義一個引用(不佔用空間) 
    s.id=1;         
    strcpy(s.name,"李詩珺"); //利用字串複製對s.name賦值,不能用s.name="XX" 
    ps->Chinese=95;    //用指標變數訪問目標物件成員 
    (*ps).math=90;      //等價於 ps->math=90
    s.English=92;
    rs.average=(s.Chinese+rs.English+ps->math)/3.0;
    //引用訪問“繫結”的目標物件成員 
    cout<<s.name<<"的平均成績為"<<s.average<<endl;

需要注意的是,優先順序為2的圓點運算子“.”用於結構體物件訪問其成員;箭頭運算子“->”用於結構體指標訪問目標物件成員。這樣一來,基本就掌握了結構體的基本用法了。

單向連結串列

在理解了結構體的基礎上,再來認識連結串列就比較容易了。能夠單方向聯絡的連線方式被稱為單向連結串列,構成單向連結串列的元素被稱為結點。結點應該具有承載資料及指向下一個結點的能力。在鏈式連線結構中,刪除某個結點只需要將該節點儲存的下一個結點地址“交給”上一個結點即可。由於結點個數以及各個結點在記憶體中的地址都很容易得到,所以將單向連結串列傳遞給函式時,只需要傳遞鏈首地址。
先來試著定義一個結點:

struct memory    //定義記憶體空間結點
{
 int address;      //分割槽首地址
 int space;          //空間大小
 bool status;       //狀態位
 memory *next;   //同類指標,指向下一個記憶體空間的地址
};

以上便定義了一種型的資料型別,先來建立一條空連結串列:

memory *head=NULL;

建立完畢之後,就要對這條單向連結串列進行操作了。首先得加入元素,也就是在首節點處再插入結點,插入函式如下:

void InsertBeforeHead(memory *&head,int space,bool status,int address)
{
    memory *p=new memory ;  //建立一個新結點
    p->address=address;
    p->space=space;   //賦值操作 
    p->status=status;
    p->next=head;    //新結點將原鏈首結點當成下一個結點 
    head=p;  //head記錄新結點的地址,成為新的鏈首地址
    //最先定義的head到最後會成為鏈尾 
}

在連結串列建立完成之後(一般建立空連結串列),然後利用上述函式新增一個新結點到鏈首前成為新的鏈首。當插入完成之後,就完成了一條單連結串列的建立。接下來附上一些常用的連結串列操作函式。

刪除鏈首結點的函式:

void DeleteHead(memory *&head) 
 //這裡需要使用引用,因為要直接修改head的指向
{
    memory *p=head;   //記錄當前鏈首結點地址 
    if(head!=NULL)  //判斷是否為空連結串列 
    {
        head=head->next;   //下一個結點成為新的鏈首結點,相當於刪除了當前結點 
        delete p;  //釋放原鏈首結點 
    }
}

遍歷函式的運用:

 memory *p;
 int n;  //統計結點個數 
for(p=head;p!=NULL;p=p->next)
{   n++;   //統計計算 
    cout<<p->space<<endl;   //遍歷輸出資料

}

釋放所有結點:

while(head!=NULL)
{
    p=head;
    head=head->next; //使head指向的下一個結點成為頭結點
    delete p; //釋放空間 
}

插入一個結點:

void Insert(int i,int e,int n,memory *&head) 
//i表示插入位置,e表示插入的資料,n表示結點個數 
{
    memory *q=new memory ;  //建立一個新結點,即為要插入的結點 
    memory *p;      //用於遍歷結點
    q->space=e;        //賦值 
    q->status=0;
    q->address=0; 
    if(i>n||i<0)  //判斷是否越界
    return ;
    if(i==1)  //即插入位置為頭結點
    {
        q->next=head; //成為鏈首
        head=q;
     } 
     for(p=head;i!=0,p!=NULL;p=p->next,i--);  //找到插入的位置 
     if(i==n)  //即插入位置為鏈尾
     {
        p->next=q; //直接在鏈尾插入
      } 
      else
      {
            q->next=p->next;   //q代替本來p的位置,指向p的下一個元素 
            p->next=q;        //p的下一個結點指向q 
      }
} 

刪除一個結點:

void DeleteHead2(int e,memory *&head) //e用來確定刪除哪個結點
{
    memory *p,*q;
    p=head; //前哨結點 
    q=head->next; //用於定位需要刪除的結點
    while(q!=NULL)
    {
        if(q->space==e) //找到該結點
        {
            p->next=q->next;  //使待刪結點脫鏈 
            delete q;
            break;
         } 
        p=p->next;  //若未找到,則往前推進一個結點 
        q=q->next;
     } 
 } 

以上是單向連結串列的一些基礎操作函式,掌握了這些就基本能夠對單向連結串列進行增刪查改的操作了。

雙向連結串列

從上文的單向連結串列中可以看出,單向連結串列只有指向下一個結點的指標,並不能訪問其前驅元素,所以在訪問單連結串列的時候,只能順序訪問,但在實際操作中,可能是一件比較麻煩的事情,所以接下來要來學習一下雙向連結串列。
說來雙向連結串列其實很簡單,無非是在之前的單向連結串列之前加入一個前驅指標pre,後繼指標next不變。這樣,無論是順序遍歷還是逆序遍歷都很方便,可以快速找到上一個結點的位置。
首先來看一下如何定義雙向連結串列的結點:

struct node   
{  
    int data;  
    node* prev;  //前驅指標
    node* next;  //後繼指標
};  

那這樣看來,雙向連結串列的頭結點前驅指標為空,尾結點的後繼指標為空,也非常好判斷。
結合單向連結串列實現的程式碼,雙向連結串列也非常容易實現,而比較容易犯錯的地方就是新增和刪除結點,其他程式碼省略了,接下來附上幾段虛擬碼,實現新增和刪除結點。

新增元素:
我們假設要插入的結點為node,插入位置的結點為current,插入位置的下一個結點為next。

current->next=node;
node->prev=current;
node->next=next;
next->prev=node;

程式碼並不難,但要理解。要求是將node插入current和next結點之間,那麼要修改的指標就是:current的後繼,next的前驅,以及node的前驅和後繼。我們要讓current的後繼指向node,讓next的前驅指向node,這樣就能找到node的位置了。接下來修改node的前驅後繼指標,分別指向current和next,就完整了。

刪除結點:
我們假設要刪除的結點為node,前驅結點為current,後繼結點為next.

current->next=next;
next->prev=current;
delete node;

刪除結點相對於插入結點更簡單,只需要修改要刪除結點的前驅結點和後繼結點的指標即可。將前驅結點的後繼指向後繼結點,然後將後繼結點的前驅指向前驅結點,最後別忘記釋放node的空間。這樣就完成了刪除結點的操作。
其他的查詢修改操作也比較容易實現,這裡筆者就不再贅述了。能夠了解上述的函式操作,那在單連結串列以及雙向連結串列的操作方面,應該都不會有問題了。

相關文章