單鏈表的基本操作和實現


(一)鏈表的定義和介紹在這里就不多介紹了,我們現在看文章大概最煩的就是一上來就長篇大論,這樣直接勸退了很多人,所以我們選擇從簡。

    頭節點: 是單鏈表的頭,是一個特殊的節點,只有指針域,沒有數據域。

    

    節點:由兩部分構成,第一部分是數據域,存儲的是該節點的內容,第二部分是指針域,用來存儲下一節點的地址,通過改地址可以訪問下一個節點。

      

    單鏈表:由頭節點和若干節點組成的鏈表。

    

    這是一個有三個節點的鏈表。

(二)鏈表的存儲結構

  (1)從上篇文章我們得知順序表在計算機中存儲的位置是連續的,就像宿舍樓一層的房間,都是相鄰的,但鏈表不一樣,鏈表不一定是相鄰的,只不過每一個節點都會存儲下一個節點的地址。比如說,小明有小紅的地址,小紅呢有小白的地址,小白又有小蘭的地址,小黑有小明的地址,他們五個是一個班的,所以他們之間就形成了一個關系:

  

這就是一個單鏈表的結構,單是指只有前驅節點有后繼節點的地址,只有單向性,當然也有雙向鏈表,后面遇到了我們再講。

(2)上面是我們自己抽象化的一個鏈表,在計算機中的存儲結構是下圖:    

(三)代碼分析

(1)頭文件

#include <iostream>     //基本的輸入輸出
using namespace std;   //聲明命名空間
#include<string.h>     //字符串頭文件

(2)預處理

#define OK 1
#define ERROR 0
#define ElemType int

(3)創建節點,很明顯,這里要用到結構體,結構體有兩個成員,一個是數據域,一個是指針域。

typedef struct LNode
{
    struct LNode *next;   //節點指針域
    string data;          //節點數據域
}*LinkList,LNode;          //一個是結構體指針,一個是結構體名稱

(4)初始化函數,創建一個頭節點,並將指針域置空。

ElemType Init_Linklist(LinkList &L)  //初始化鏈表
{
    L = new LNode;                  //創建一個頭節點
    L->next = NULL;                 //並將頭節點的指針域置空
    return OK;
}

(5)輸出鏈表函數,這個函數呢,是將單鏈表中的每個元素按順序輸出,為了美觀,用"->"隔開。

   實現步驟:①定義一個節點指針,指向第一個節點。(注意:這里的第一個節點是頭節點之后的第一個。)

        ②while判斷當前節點是否為空,如果不是空,就輸出該節點內容,然后指針下移,下面的if是最后一個節點不輸出"->",這樣鏈表看起來會更美觀。

void Printf_LNode(LinkList L)   //遍歷輸出鏈表內容
{
    cout<<"鏈表內容:";
    LNode *p = L->next;            //節點指針,用來遍歷節點
    while(p)
    {
        cout<<p->data;             //輸出節點的數據域
        p = p->next;
        if(p)                     //判斷下一個節點是否為空,如果是就不輸出"->"
        {
            cout<<"->";
        }
    }
    cout<<endl;

}

(6)前插法建立鏈表,即每次在第一個節點的位置插入新節點。咱們先自己想一下這個過程,首先,我們要新建一個節點吧,然后將新節點的指針域指向下一個節點,再把上一個節點的指針域指向自己,不就插入成功了嘛。看一下圖片:

    

   實現步驟:①輸出提醒,並得到你要創建節點的個數n。

        ②創建一個節點指針,便於后面改指針的操作。

        ③初始化頭節點,並將指針域置空。

        ④要創建n個節點,自然需要循環n次,首先開辟一個新節點,並讓p指向新節點,將新節點的指針指向下一個節點,再讓上一個節點指向自己,然后在輸入該節點的內容。

        ⑤輸出鏈表。

void Create_LinkList_h(LinkList &L)    //前插法創建單鏈表
{
    int n;
    cout<<"請輸入你要創建的鏈表的結點個數:";
    cin>>n;
    LNode *p;
    L = new LNode;        //初始化頭節點
    L->next = NULL;
    cout<<"將要采用頭插法創建單鏈表,請逆序輸入"<<n<<"個節點內容:";
    for(int i = 0;i < n;i++)    //循環創建節點
    {
        p = new LNode;
        p->next = L->next;
        L->next = p;
        cin>>p->data;
    }
    Printf_LNode(L);          //輸出鏈表內容
}

(7)后插法建立鏈表,尾插法是將每個元素插在最后,故稱為后插法。與前插法大致相同,不過比前插法多一個節點指針,首先新建一個節點,然后自己的指針域為空,因為自己已經是最后一個節點了,然后讓上一個節點指向自己,然后插入成功。

    實現步驟:①首先得到你要輸入的節點個數n

         ②初始化頭節點

         ③定義一個節點指針r,並指向L

         ④循環n次,每一次都先申請一個節點,輸入節點內容,因為是后插,所以每一個節點的指針域都指向空,然后再讓上一個節點指向自己,然后r后移一個節點。

void Create_LinkList_r(LinkList &L)   //尾插法創建鏈表
{
    int n;                              //節點個數
    cout<<"請輸入你要創建的鏈表的結點個數:";
    cin>>n;
    L = new LNode;                    //初始化頭節點
    L->next = NULL;
    LNode *r = L;
    cout<<"將要采用尾插法創建單鏈表,請輸入"<<n<<"個節點內容:";
    for(int i = 0;i < n;i++)    //循環創建節點
    {
        LNode *p;
        p = new LNode;
        cin>>p->data;
        p->next = NULL;
        r->next = p;
        r = p;
    }
    Printf_LNode(L);
}

(8)取值,通過輸入的位置, 遍歷鏈表,找到對應位置的值,然后賦值即可。

    實現步驟:①首先輸入你要取得元素的位置

         ②定義一個新的節點指針,並指向頭節點的下一個節點,同時呢也定義一個j,用來記錄節點的值。

         ③用while循環,讓指針連續下移到相應位置,同時判斷j的值是否達到n。

         ④如果已經移到空節點了,或者說j已經大於n了,就表明越界了。

         ⑤如果沒有越界,就賦值即可。

ElemType Get_ElemType(LinkList L,string &name)
{
    int n;                      //元素位置
    cout<<"請輸入你要取得元素的位置:";
    cin>>n;
    LNode *p = L->next;    //新建節點指針,並指向下一個節點
    int j = 1;
    while(p&&j<n)         //指針移到要取得位置
    {
        p = p->next;
        j++;
    }
    if(!p || j>n)           //判斷是否越界
    {
        return ERROR;
    }
    name = p->data;        //取值
}

(9)插入節點,其實也就是先開辟一個節點,然后讓新節點指向下一個節點,再讓新節點的上一個節點指向自己,就插入成功了。

    實現步驟:①首先得到你要插入的位置

         ②定義一個節點指針,指向第一個節點

         ③指針連續下移,移到n-1的節點。

         ④判斷是否越界

         ⑤新建一個節點和一個指向新節點的指針s,然后輸入插入的節點的值。

            ⑥改指針,將上一個節點的指針賦給新節點,然后讓上一個節點指向自己,就插入成功了。

 

ElemType Insert_LNode(LinkList &L)   //插入節點函數
{
    int n;      //插入得位置
    cout<<"請輸入你要插入的位置:";
    cin>>n;
    LNode *p = L->next;
    int j = 1;
    while(p&&j<(n - 1))   //新建節點指針,並指向下一個節點
    {
        p = p->next;
        j++;
    }
    if(!p || j > (n - 1))  //判斷是否越界
    {
        return ERROR;
    }
    LNode *s;         //指向新建節點的指針
    s = new LNode;
    cout<<"請輸入你要插入的元素:";
    cin>>s->data;    //輸入新節點的數據域內容
    s->next = p->next;
    p->next = s;
    Printf_LNode(L);   //輸出單鏈表

}

 (10)刪元素,其實跟之前插元素的實現差不多,都是先移到你要刪除的位置,然后把你想要刪除節點的指針域賦給前一個節點,然后釋放節點即可。

    實現步驟:①輸出一邊當前鏈表的內容,然后挑選並得到你要刪除的位置,同時定義一個節點指針q。

         ②定義一個節點指針指向第一個節點,並定義一個計數變量j。

         ③指針p連續下移到要刪除的元素的前一個位置,然后在把該節點的指針域(q = p->next),賦給節點指針q,這樣q就指向了我們要刪除的節點,然后讓q指向我們要刪除的節點的下一個節                                        點,然后釋放q節點即可。

ElemType Del_LNode(LinkList &L)  //刪除節點函數
{
    int n;
    LNode *q;       //首先定義一個節點指針
    Printf_LNode(L);
    cout<<"請輸入你要刪除的位置:";
    cin>>n;
    LNode *p = L->next;
    int j = 1;
    while(p&&(j< n - 1))  //指針下移
    {
        p = p->next;
        ++j;
    }
    if(!p || j > (n - 1))  //判斷是否越界
    {
        return ERROR;
    }
    q = p->next;
    p->next = q->next;
    delete q;
    Printf_LNode(L);
}

(11)菜單函數,只是一些普通的輸出,就不用解釋了吧

void Menu()   //菜單函數
{
    cout<<"<<<<<<<<<<<<<<<菜單>>>>>>>>>>>>>>>"<<endl;
    cout<<"1.初始化單鏈表"<<endl;
    cout<<"2.頭插法創建一個單鏈表"<<endl;
    cout<<"3.尾插法創建一個單鏈表"<<endl;
    cout<<"4.取單鏈表中的某個元素"<<endl;
    cout<<"5.在單鏈表某個位置插入元素"<<endl;
    cout<<"6.在單鏈表刪除某個位置的元素"<<endl;
    cout<<"7.輸出鏈表內容"<<endl;
    cout<<"8.菜單"<<endl;
    cout<<"9.退出系統!"<<endl;
    cout<<"輸入對應選項,執行對應操作"<<endl;
}

(12)主函數:首先創建一個鏈表指針,輸出菜單,然后進入while循環,通過switch輸入不同的選項進入不同的case,然后執行不同的函數,這里為了能夠退出系統(跳出循環)使用了goto語句來結束循環。這里需要特別說明的可能就是case 4和case 5,定義了兩個標志變量,用來記錄程序退出的值,看是否出錯

int main()
{
    LinkList La;  //鏈表指針
    int select;
    Menu();
    while(1)   //循環輸入選項
    {
        cout<<"請輸入選項:";
        cin>>select;
        switch(select)   //判斷選項,並執行對應的函數
        {
        case 1:
            {
                Init_Linklist(La);
                cout<<"初始化后單鏈表為空,要想進行操作,請先創建一個單鏈表"<<endl;
                Create_LinkList_h(La);
                break;
            }
        case 2:
            {
                Create_LinkList_h(La);
                break;
            }
        case 3:
            {
                Create_LinkList_r(La);
                break;
            }
        case 4:
            {
                int flag;         //標志變量,用來記錄退出碼,用來判斷是否為正常退出
                string name;
               flag = Get_ElemType(La,name);
               if(!flag)
               {
                   cout<<"取出失敗,越界!"<<endl;
               }
                cout<<name<<endl;
                break;
            }
        case 5:
            {
                int flag;
                flag = Insert_LNode(La);    //標志變量,用來記錄退出碼,用來判斷是否為正常退出
                if(!flag)
                {
                    cout<<"插入失敗,越界!"<<endl;
                }
                break;
            }
        case 6:
            {
                Del_LNode(La);
                break;
            }
        case 7:
            {
                Printf_LNode(La);
                break;
            }
        case 8:
            {
                Menu();
                break;
            }
        case 9:
            {
                cout<<"多謝使用"<<endl;
                goto unloop;      //使用goto語句,跳轉,使程序結束
            }
        default:
        {
            cout<<"輸入有誤!"<<endl;
            break;
        }
        }
    }
    unloop:return 0;      //跳轉到程序結束語句
}

(四)完整代碼

#include <iostream>     //基本的輸入輸出
using namespace std;   //聲明命名空間
#include<string.h>     //字符串頭文件

#define OK 1
#define ERROR 0
#define ElemType int


typedef struct LNode
{
    struct LNode *next;   //節點指針域
    string data;          //節點數據域
}*LinkList,LNode;          //一個是結構體指針,一個是結構體名稱

ElemType Init_Linklist(LinkList &L)  //初始化鏈表
{
    L = new LNode;                  //創建一個頭節點
    L->next = NULL;                 //並將頭節點的指針域置空
    return OK;
}

void Printf_LNode(LinkList L)   //遍歷輸出鏈表內容
{
    cout<<"鏈表內容:";
    LNode *p = L->next;            //節點指針,用來遍歷節點
    while(p)
    {
        cout<<p->data;             //輸出節點的數據域
        p = p->next;
        if(p)                     //判斷下一個節點是否為空,如果是就不輸出"->"
        {
            cout<<"->";
        }
    }
    cout<<endl;

}

void Create_LinkList_h(LinkList &L)    //前插法創建單鏈表
{
    int n;
    cout<<"請輸入你要創建的鏈表的結點個數:";
    cin>>n;
    LNode *p;
    L = new LNode;        //初始化頭節點
    L->next = NULL;
    cout<<"將要采用頭插法創建單鏈表,請逆序輸入"<<n<<"個節點內容:";
    for(int i = 0;i < n;i++)    //循環創建節點
    {
        p = new LNode;
        p->next = L->next;
        L->next = p;
        cin>>p->data;
    }
    Printf_LNode(L);          //輸出鏈表內容
}

void Create_LinkList_r(LinkList &L)   //尾插法創建鏈表
{
    int n;                              //節點個數
    cout<<"請輸入你要創建的鏈表的結點個數:";
    cin>>n;
    L = new LNode;                    //初始化頭節點
    L->next = NULL;
    LNode *r = L;
    cout<<"將要采用尾插法創建單鏈表,請輸入"<<n<<"個節點內容:";
    for(int i = 0;i < n;i++)    //循環創建節點
    {
        LNode *p;
        p = new LNode;
        cin>>p->data;
        p->next = NULL;
        r->next = p;
        r = p;
    }
    Printf_LNode(L);
}

ElemType Get_ElemType(LinkList L,string &name)
{
    int n;                      //元素位置
    cout<<"請輸入你要取得元素的位置:";
    cin>>n;
    LNode *p = L->next;    //新建節點指針,並指向下一個節點
    int j = 1;
    while(p&&j<n)         //指針移到要取得位置
    {
        p = p->next;
        j++;
    }
    if(!p || j>n)           //判斷是否越界
    {
        return ERROR;
    }
    name = p->data;        //取值
}

ElemType Insert_LNode(LinkList &L)   //插入節點函數
{
    int n;      //插入得位置
    cout<<"請輸入你要插入的位置:";
    cin>>n;
    LNode *p = L->next;
    int j = 1;
    while(p&&j<(n - 1))   //新建節點指針,並指向下一個節點
    {
        p = p->next;
        j++;
    }
    if(!p || j > (n - 1))  //判斷是否越界
    {
        return ERROR;
    }
    LNode *s;         //指向新建節點的指針
    s = new LNode;
    cout<<"請輸入你要插入的元素:";
    cin>>s->data;    //輸入新節點的數據域內容
    s->next = p->next;
    p->next = s;
    Printf_LNode(L);   //輸出單鏈表

}

ElemType Del_LNode(LinkList &L)  //刪除節點函數
{
    int n;
    LNode *q;       //首先定義一個節點指針
    Printf_LNode(L);
    cout<<"請輸入你要刪除的位置:";
    cin>>n;
    LNode *p = L->next;
    int j = 1;
    while(p&&(j< n - 1))  //指針下移
    {
        p = p->next;
        ++j;
    }
    if(!p || j > (n - 1))  //判斷是否越界
    {
        return ERROR;
    }
    q = p->next;
    p->next = q->next;
    delete q;
    Printf_LNode(L);
}
void Menu()   //菜單函數
{
    cout<<"<<<<<<<<<<<<<<<菜單>>>>>>>>>>>>>>>"<<endl;
    cout<<"1.初始化單鏈表"<<endl;
    cout<<"2.頭插法創建一個單鏈表"<<endl;
    cout<<"3.尾插法創建一個單鏈表"<<endl;
    cout<<"4.取單鏈表中的某個元素"<<endl;
    cout<<"5.在單鏈表某個位置插入元素"<<endl;
    cout<<"6.在單鏈表刪除某個位置的元素"<<endl;
    cout<<"7.輸出鏈表內容"<<endl;
    cout<<"8.菜單"<<endl;
    cout<<"9.退出系統!"<<endl;
    cout<<"輸入對應選項,執行對應操作"<<endl;
}

int main()
{
    LinkList La;  //鏈表指針
    int select;
    Menu();
    while(1)   //循環輸入選項
    {
        cout<<"請輸入選項:";
        cin>>select;
        switch(select)   //判斷選項,並執行對應的函數
        {
        case 1:
            {
                Init_Linklist(La);
                cout<<"初始化后單鏈表為空,要想進行操作,請先創建一個單鏈表"<<endl;
                Create_LinkList_h(La);
                break;
            }
        case 2:
            {
                Create_LinkList_h(La);
                break;
            }
        case 3:
            {
                Create_LinkList_r(La);
                break;
            }
        case 4:
            {
                int flag;         //標志變量,用來記錄退出碼,用來判斷是否為正常退出
                string name;
               flag = Get_ElemType(La,name);
               if(!flag)
               {
                   cout<<"取出失敗,越界!"<<endl;
               }
                cout<<name<<endl;
                break;
            }
        case 5:
            {
                int flag;
                flag = Insert_LNode(La);    //標志變量,用來記錄退出碼,用來判斷是否為正常退出
                if(!flag)
                {
                    cout<<"插入失敗,越界!"<<endl;
                }
                break;
            }
        case 6:
            {
                Del_LNode(La);
                break;
            }
        case 7:
            {
                Printf_LNode(La);
                break;
            }
        case 8:
            {
                Menu();
                break;
            }
        case 9:
            {
                cout<<"多謝使用"<<endl;
                goto unloop;      //使用goto語句,跳轉,使程序結束
            }
        default:
        {
            cout<<"輸入有誤!"<<endl;
            break;
        }
        }
    }
    unloop:return 0;      //跳轉到程序結束語句
}

 

(五)運行結果

 程序在健壯性方面還有很多不足,各位大神如果有什么好的想法或者發現我的某些錯誤,歡迎指正!最后希望大家給我一個支持,謝謝!

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM