C語言進階-第18講:單鏈表的遍歷、創建、插入和刪除結點

任務和代碼

 

       遍歷鏈表

void traverse(Node *h){
    Node *p;   //p作爲用來遍歷的指針
    p=h;
    while(p!=NULL){
        printf("%-5d",p->data); //訪問結點信息
        p=p->next;              
    }
    printf("\n");
}


       順序創建動態鏈表

      (一)

/*
返回值:指向頭結點的指針
參數:鏈表長度
說明:新建鏈表就是不斷插入新的結點
*/
Node *createLinkList(int n){
    Node *h=NULL,*p,*last;  //p指向新增結點,last指向尾結點
    int d;                  //要插入結點的元素值
    int i;
    for(i=0;i<n;i++){
        scanf("%d",&d);
        p=(Node*)malloc(sizeof(Node));
        p->data=d;          //添加新增結點的信息
        p->next=NULL;
        if(h==NULL) 
            h=p;            //h爲空,p是第一個結點的地址,h指向p
        else
            last->next=p;   //新增結點的前一個結點的指針域是新增結點的地址
        last=p;             //last代表新增的最後一個結點的地址
    }
    return h;
} 

      

      (二)

S *create(int n)  
{  
    S *p,*q,*head;  
    head=p=q=malloc(sizeof(S));  //先建立頭結點(頭指針不能變,故放在循環體前)
    scanf("%d",&p->date);  
    while(--n)                   //添加後面的n-1個結點
    {  
        p=malloc(sizeof(S));  
        scanf("%d",&p->date);  
        q->link=p;  
        q=p;  
    }  
    q->link=NULL;  
    return head;  
} 
       

        倒序創建動態鏈表

Node* make_list(int n){    
    Node *p;  
    h=NULL;  
    printf("輸入若干正數(以0或一個負數結束)建立鏈表:" );  
    scanf("%d", &n);  
    while(n>0){                         //輸入若干正數建立鏈表,輸入非正數時,建立過程結束  
        p=(Node*)malloc(sizeof(Node));  //新建結點  
        p->data=n;  
        p->next=h;                      //更改新增結點的指針域使其指向上一個新增結點  
        h=p;                            //將新增結點作爲頭結點  
        scanf("%d", &n);                //輸入下一個數,準備建立下一個結點  
    }  
    return h;  
} 

       



        插入結點應用

       

/*
功能:將一組無序數建成有序表
返回值:鏈表的頭指針
參數1:鏈表的頭指針
參數2:新讀入的一個數
說明:建立有序表的過程就是不斷將新增結點插入正確的位置
*/
Node *insertNode(Node *h,int b){
    Node *q1=h,*q2,*p;     //p指向新增結點
    p=(Node*)malloc(sizeof(Node));
    p->data=b;
    if(h==NULL){          //當前鏈表爲空,p爲首結點地址
        h=p;              //頭指針指向p
        p->next=NULL;     //給p的指針域賦值
    }
    else if(p->data<h->data){ //插入結點是首結點
        h=p;              //頭指針指向新的首結點
        p->next=q1;       //p的指針域存放原首結點的地址
    }
    else{
        /*兩種情況:新增結點是最大的結點或新增結點在中間
        並找到新增結點最左邊的結點*/
        while(q1!=NULL&&p->data>=q1->data){
            /*q1從首結點開始遍歷直至鏈表末尾,p在頭結點後,當p中的數據小於q1中的數據停止尋找*/
            q2=q1;        //q2爲所找結點(目標結點的前一個結點)
            q1=q1->next;
        }
        /*將新結點p插到p2後*/
        p->next=q2->next; //p的指針域存放q2原來指針域的地址(即p1的地址)
        q2->next=p;       //q2新指針域存放p的地址
        /*注意:上述兩條語句不能顛倒,小心上下語句出現左對角一樣的情況 */
    }
     return h;
}

int main(){
    int b[]={67,25,78,99,87},i;
    Node *head=NULL;
    for(i=0;i<5;i++)
        head=insertNode(head,b[i]);
    traverse(head);
    return 0;
}

       


        刪除結點應用

       

/*
功能:從有序表中刪除指定結點(只能刪除第一次出現目標元素的結點)
返回值:鏈表的頭指針
參數1:鏈表的頭指針
參數2:指定要刪除的元素
說明:找到元素所在的結點,更改其左結點的指針域並釋放其空間
*/
Node *deleteNode(Node *h,int b){
    Node *p,*q;      //p作爲遍歷的指針尋找目標結點的地址,q是p的左結點
    p=h;
    if(h==NULL)
        printf("List is null,delete fail.\n");
    else{
        /*p從首結點開始遍歷到尾結點結束,尋找刪除結點*/
        while(b!=p->data&&p->next!=NULL){
            q=p;               //q也是所找結點,目標結點的前一個結點
            p=p->next;         //p爲所找結點也是目標結點
        }
        if(b==p->data){
            /*當找到的元素就在首結點中,不經過循環體*/
            if(p==h)
                h=p->next;
            else
                q->next=p->next;
            free(p);
        }
        else
            printf("%d not found,delete fail.\n",b);
    }
    return h;
}

int main(){
    int b[]={67,25,78,99,87},i;
    Node *head=NULL;
    for(i=0;i<5;i++)
        head=insertNode(head,b[i]);
    traverse(head);
    head=deleteNode(head,78);
    traverse(head);
    head=deleteNode(head,43);
    traverse(head);
    return 0;
}

      

/*
功能:從有序表中刪除指定結點(可刪除出現目標元素的多個結點)
參數1:鏈表的頭指針
參數2:指定要刪除的元素
說明:q是遍歷指針,用來尋找目標結點,p用來連接每一個非目標結點。
當q是目標結點時,p裏的風向標指向下一個結點;當q是目標結點時,p跟着q走。
*/
Node* delete_node(Node *h,int x){  
    Node *p,*q;  
    if (h==NULL)  
        printf("空鏈表,不能刪除\n");  
    else{  
        //判斷頭結點是否含目標元素,若有一直遍歷頭指針
        while(h!=NULL&&h->data==x){  //結果有兩種:空鏈表或首結點不含目標元素
            p=h;  
            h=->next;                //遍歷h,p跟在後面
            free(p);                 //把確定下來h前的內存空間統統釋放
        }  
        if(h!=NULL){  
            p=h;                     //p從頭結點開始,代表非目標結點
            q=p->next;               //q從p之後開始找目標
            while(q!=NULL){  
                if(q->data==x){      //q是目標結點  
                    p->next=q->next; //p的風向標指往q之後的結點
                    free(q);  
                }  
                else{      
                    p=q;             //q不是目標結點,p跟着移動
                }  
                q=p->next;           //q是p的下一個結點,q遍歷每一個結點
            }  
        }  
    }  
    return h;  
}  
       



知識點總結


順序建立鏈表
核心:更改上一個新增結點的指針域使其指向新增結點


倒序建立鏈表
核心:更改新增結點的指針域使其指向上一個新增結點,並將新增結點作爲頭結點


有序插入結點:
首先判斷待插結點的位置
該結點可作爲頭結點,則更改頭指針及該結點的指針域
該結點在頭結點後,則更改插入結點的指針域及其前一個結點的指針域,遍歷得到前一個結點的地址


刪除第一個出現的目標結點:
首先遍歷結點,找到目標結點的位置(遍歷得到目標結點及其左結點)
若目標結點是頭結點,則更改頭結點的地址
否則,更改其前一個結點的指針域使其指向要刪結點的後一個結點,
最後釋放刪除結點內存

刪除出現目標元素的多個結點:
首先遍歷頭指針,釋放所有目標結點的內存空間,確定頭結點不含目標元素

遍歷頭結點之後的所有結點,使所有結點的指針域均不存放目標結點的地址



心得

       構建鏈表最重要的就是確定每一個結點的地址以及存放的下一個結點的地址,我的理解就是頭指針和各個“風向標”。而函數中定義的指針變量p,q都只是變量,可以被不斷重新賦值,但只要與head關聯,都是明確的地址。因此,當所有風向標確定下來,一條鏈表就完成了,而只要給出頭指針,該鏈表的所有信息也均可被訪問。
       在創建或形成一條新的鏈表的時候,除了用一個指針表示指向新增結點或目標結點的指針外,同時還得定義另一個指針,用來形成鏈表。

發佈了94 篇原創文章 · 獲贊 44 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章