1. 程式人生 > >c資料結構線性表之單鏈表(帶頭結點)基本操作

c資料結構線性表之單鏈表(帶頭結點)基本操作

#include<stdio.h>
#include<malloc.h>
#define MAXSIZE 10
#define ElemType char
#define OK 1
#define ERROR 0

typedef struct Node/*結點型別定義*/
{
    ElemType data;
    struct Node *next;
}Node,*LinkList;/* LinkList為結構體指標型別*/

//換行函式
void enter(int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        printf("\n");
    }
}

//初始化單鏈表
void init_list(LinkList *H)
{
    *H=(LinkList)malloc(sizeof(Node));//建立頭結點
    (*H)->next=NULL; //建立空的單鏈表
}

//計算單鏈表的長度
int list_len(LinkList H)
{
    Node *p;
    int j=0;
    p=H->next;
    while(p!=NULL)
    {
        p=p->next;
        j++;
    }
    return j;
}    

//用尾插法建立單鏈表
int create_from_tail(LinkList H)
{
    Node *r,*s;
    char c;
    int flag=1;//設定標誌位,控制建表結束
    r=H;//動態指向當前連結串列尾部,便於尾插入,初值指向頭結點
    while(flag)
    {
        c=getchar();
        if(c!='$')
        {
            s=(Node *)malloc(sizeof(Node));
            s->data=c;
            r->next=s;
            r=s;
        }
        else
        {
            flag=0;
            r->next=NULL;//將最後一個結點的next置為空,表示連結串列結束
        }
    }
    enter(1);
    return(OK);
}

//列印連結串列資料
void print_out(LinkList H)
{
    Node *p;
    p=H->next;//從第一個結點開始
    while(p!=NULL)
    {
        printf("%c ",p->data);
        p=p->next;
    }
    enter(2);
}


//按結點查詢
Node *get(LinkList H,int i)
{
    int j;
    Node *p;
    p=H;j=0;
    while(p->next!=NULL&&j<i)
    {
        p=p->next;
        j++;
    }
    if(i==j)
    {
        return p;
    }
    else
    {
        return NULL;
    }
}

//按元素值查詢
int locate(LinkList H,ElemType key)
{
    Node *p;
    int j=0;
    p=H->next;//從第一個結點查詢
    
    while(p!=NULL)
    {
        if(p->data!=key)
        {
            j++;
            p=p->next;
        }
        
        else break;    
    }
    if(p==NULL)
    {
        printf("未找到元素!");
        return(ERROR);
    }
    else
    {
        printf("此元素位於第%d個節點",j+1);//打印出元素的結點
        return(OK);
    }
}

//單鏈表插入操作
int ins_list(LinkList H,int i,ElemType e)
{
    Node *pre,*s;
    int L;
    L=list_len(H);
    if(L>=MAXSIZE)
    {
        printf("單鏈表已超過要求的長度,無法插入");
        enter(2);
        return(ERROR);
    }
    pre=get(H,i-1);//呼叫get函式,找到第i-1個結點
    if(pre==NULL)
    {
        printf("插入位置不合理");
        enter(2);
        return(ERROR);
    }
    else
    {
        s=(Node *)malloc(sizeof(Node));//新建s結點
        s->data=e;//將插入資料賦給s的資料域
        s->next=pre->next;//完成插入
        pre->next=s;
        printf("插入後的連結串列資料:");
        print_out(H);//輸出插入後的連結串列資料
        return(OK);

    }
}

//單鏈表刪除操作
int del_list(LinkList H,int i, char *e)
{
    Node *pre,*s;

    pre=get(H,i-1);//呼叫get函式,找到第i-1個結點
    if(pre==NULL)
    {
        printf("刪除位置不合理");
        enter(2);
        return(ERROR);
    }
    else
    {
        s=pre->next;
        pre->next=pre->next->next;//刪除結點
        *e=s->data;
        printf("刪除的元素是:%c",*e);
        enter(1);
        printf("刪除後的連結串列:");
        print_out(H);//輸出刪除後的結點
        free(s);//釋放被刪除的結點
        return(OK);
    }
}

//主方法
int main()
{
    LinkList H;
    int a,d,f;
    char b,c,e='m';
    init_list(&H);//初始化
    printf("用尾插法建立連結串列,請輸入連結串列資料,以$結束:\n");
    create_from_tail(H);//尾插發建表
    printf("輸出連結串列資料:\n");
    print_out(H);//輸出連結串列
    if(list_len(H)>MAXSIZE)//限制連結串列長度在10以內
    {
        printf("單鏈表長度:%d已大於要求長度",list_len(H));
        enter(2);
        return(ERROR);
    }
    printf("單鏈表長度:%d",list_len(H));
    enter(2);
    printf("請輸入需查詢的節點:");
    scanf("%d",&a);
    if(get(H,a)==NULL) //按結點查詢
    {
        printf("未找到此節點");
        enter(2);
        return(ERROR);
    }
    else printf("查詢到的元素:%c",*get(H,a));    
    enter(2);
    printf("請輸入要查詢的元素值:");
    getchar();
    scanf("%c",&b);
    locate(H,b);//按值查詢
    enter(2);
    printf("請輸入要插入的位置和元素,[hint:輸入兩個資料之間有逗號]:");
    getchar();
    scanf("%d,%c",&d,&c);
    ins_list(H,d,c);//插入
    printf("請輸入要刪除的位置:");
    getchar();
    scanf("%d",&f);
    del_list(H,f,&e);//刪除
    return(OK);
}

資料結構中,在單鏈表的開始結點之前附設一個型別相同的結點,稱之為頭結點。頭結點的資料域可以不儲存任何資訊,頭結點的指標域儲存指向開始結點的指標(即第一個元素結點的儲存位置)。

作用

1、防止單鏈表是空的而設的.當連結串列為空的時候,帶頭結點的頭指標就指向頭結點.如果當連結串列為空的時候,單鏈表沒有帶頭結點,那麼它的頭指標就為NULL.

2、是為了方便單鏈表的特殊操作,插入在表頭或者刪除第一個結點.這樣就保持了單鏈表操作的統一性!

3、單鏈表加上頭結點之後,無論單鏈表是否為空,頭指標始終指向頭結點,因此空表和非空表的處理也統一了,方便了單鏈表的操作,也減少了程式的複雜性和出現bug的機會。

4、對單鏈表的多數操作應明確對哪個結點以及該結點的前驅。不帶頭結點的連結串列對首元結點、中間結點分別處理等;而帶頭結點的連結串列因為有頭結點,首元結點、中間結點的操作相同 ,從而減少分支,使演算法變得簡單 ,流程清晰。對單鏈表進行插入、刪除操作時,如果在首元結點之前插入或刪除的是首元結點,不帶頭結點的單鏈表需改變頭指標的值,在C 演算法的函式形參表中頭指標一般使用指標的指標(在C+ +中使用引用 &);而帶頭結點的單鏈表不需改變頭指標的值,函式引數表中頭結點使用指標變數即可。