單連結串列實現

Ajay666發表於2017-06-14

目錄

線性表是資料結構的第一章,而考研和找工作都喜歡考察這方面的內容,因此,我就用C++實現了單連結串列,以及一些常見的演算法題。

本文的具體內容包括:

  1. 單連結串列的基本操作
  2. 反向輸出單連結串列
  3. 找到兩個單連結串列的第一個公共結點
  4. 對單連結串列進行排序
  5. 將兩個有序的連結串列合在一起,使之仍然有序

單連結串列的基本操作

不多說,直接上程式碼(注意基本操作中引數L的不同):

///初始化一個單連結串列,申請頭結點
void InitList(LinkList *L){
    *L = new LNode;
    if(*L==NULL){
        cout<<"new error"<<endl;
        return;
    }
    (*L)->next = NULL;
}
///釋放頭結點,將其設為null
void DestroyList(LinkList *L){
    LinkList t ;
    while((*L)!=NULL){
        t = (*L)->next;
        delete (*L);
        (*L) = t;
    }
}
///釋放除頭結點以外的所有空間
void ClearList(LinkList L){
    LinkList t,q=L;
    L = L->next;
    while(L!=NULL){
        t = L->next;
        delete L;
        L = t;
    }
    q->next = NULL;
}
///獲取連結串列的長度
int Length(LinkList L){
    if(L == NULL)
        return -1;
     int cnt = 0;
     L = L->next;
     while(L!=NULL){
        ++cnt;
        L=L->next;
     }
     return cnt;
}
///返回連結串列L中第1個與e相等的元素的位置
int locateElem(LinkList L,int e){
    if(L==NULL)
        return -1;
    int index = 0;
    L = L->next;
    while(L!=NULL){
        if(L->data==e){
            return index;
        }
        ++index;
        L = L->next;
    }
    return -1;
}
///獲取連結串列第i個元素的值
bool getElem(LinkList L,int i,LNode &e){
    if(L==NULL or i<0)
        return false;
    int j = 0;
    L = L->next;
    while(L!=NULL){
        if(j==i){
            e.data=L->data;
            e.next=L->next;
            return true;
        }
        ++j;
        L = L->next;
    }
    return false;
}
///在L中第i個位置插入結點e
bool ListInsert(LinkList L,int i,LNode e){
    if(L==NULL)
        return false;
    LinkList p;
    p = L;
    L = L->next;
    int j = 0;
    while(p!=NULL){
        if(j==i){
            LinkList t;
            t = new LNode;
            t->data = e.data;
            t->next = e.next;
            p->next = t;
            t->next = L;
            return true;
        }
        p = L;
        L = L->next;
        ++j;
    }
    return false;
}
///刪除第i個元素,並返回該結點
bool ListDelete(LinkList L,int i,LNode &e){
    if(L==NULL)
        return false;
    LinkList p;
    int j = 0;
    p = L;
    L = L->next;
    while(L!=NULL){
        if(i==j){

            e.data = L->data;
            e.next = L->next;

            p->next = L->next;

            delete L;
            return true;
        }
        p = p->next;
        L = L->next;
        ++j;
    }
    return false;

}
///列印單連結串列L
void PrintList(LinkList L){

    if(L==NULL)
        return;
    L = L->next;
    while(L!=NULL){
        cout<<L->data<<' ';
        L = L->next;
    }
    cout<<" end"<<endl;
}

測試程式碼:

LinkList L;
    InitList(&L);
    cout<<"the address of the head node is "<<L<<endl;
    LNode e;
    e.data = 124;
    e.next = NULL;

    ListInsert(L,0,e);
    e.data = 132;
    ListInsert(L,0,e);
    e.data = 123;
    ListInsert(L,0,e);
    PrintList(L);

    cout<<"the length of L is "<<Length(L)<<endl;

    cout<<"the location of 132 is "<<locateElem(L,132)<<endl;

    if(getElem(L,1,e)){
        cout<<"the value of location 1 is "<<e.data<<endl;
    }

    if(ListDelete(L,1,e))
        cout<<"the value of deleted element is "<<e.data<<endl;
    else
        cout<<"delete failed"<<endl;

    ClearList(L);
    cout<<L<<endl;
    DestroyList(&L);
    cout<<L<<endl;

執行結果:
這裡寫圖片描述

反向輸出單連結串列

///從尾到頭反向輸出單連結串列L
///思路:反向輸出很容易就想到棧,
///然後遞迴的本質就是棧,因此可以用
///遞迴來實現,先一層層遞迴到連結串列尾部,
///當指標為NULL時,返回,返回時輸出當前節點的值
void PrintListByReverse(LinkList L){
    if(L==NULL)
        return;
    PrintListByReverse(L->next);
    cout<<L->data<<' ';
}

測試程式碼:

LinkList L;
    InitList(&L);
    LNode e;
    e.data = 124;
    e.next = NULL;
    ListInsert(L,0,e);
    e.data = 132;
    ListInsert(L,0,e);
    e.data = 123;
    ListInsert(L,0,e);
    e.data = 88;
    ListInsert(L,0,e);
    e.data = 14;
    ListInsert(L,0,e);
    e.data = 26;
    ListInsert(L,0,e);
    cout<<"the origin linklist is:"<<endl;
    PrintList(L);
    cout<<"print list in reverse:"<<endl;
    PrintListByReverse(L->next);
    cout<<"end"<<endl;

執行結果:
這裡寫圖片描述

求兩個單連結串列的公共結點

///找到兩個連結串列的第一個公共結點
/**< 思路:若兩個單連結串列有公共結點,那麼他們的邏輯結構
肯定是Y形而不是X形(因為節點的next域只有一個),因此,
可以先計算兩個連結串列的長度,然後先讓較長的先移動d
(他們的長度差)個節點這樣他們就會同時到達公共結點,
只需比較兩個指標就可以了 */
bool FindCommonNode(LinkList La,LinkList Lb,LNode &e){
    if(La==NULL || Lb==NULL)
        return false;
    int la,lb;///La/Lb 的長度
    la = Length(La);
    lb = Length(Lb);
    int d = la-lb;
    int i = 0;
    La = La->next;
    Lb = Lb->next;
    if(d>0){
        while(La!=NULL){
            if(i==d){
                break;
            }
            ++i;
            La = La->next;
        }
    }
    else{
        d=-d;
        while(Lb!=NULL){
            if(i==d){
                break;
            }
            ++i;
            Lb = Lb->next;
        }
    }
    while(La!=NULL && Lb!=NULL){
        if(La == Lb){
            e.data = La->data;
            e.next = La->next;
            return true;
        }
        La = La->next;
        Lb = Lb->next;
    }
    return false;
}

測試程式碼:

LinkList L;
    InitList(&L);
    LNode e;
    e.data = 124;
    e.next = NULL;
    ListInsert(L,0,e);
    e.data = 132;
    ListInsert(L,0,e);



    LinkList myList;
    InitList(&myList);
    e.data = 83;
    ListInsert(myList,0,e);
    e.data = 19;
    ListInsert(myList,0,e);
    e.data = 46;
    ListInsert(myList,0,e);
    ConcatList(myList,L);
    cout<<"list 1 is:"<<endl;
    PrintList(myList);

    LinkList hisList;
    InitList(&hisList);
    e.data = 547;
    ListInsert(hisList,0,e);
    e.data = 78;
    ListInsert(hisList,0,e);
    ConcatList(hisList,L);
    cout<<"list 2 is:"<<endl;
    PrintList(hisList);

    if(FindCommonNode(myList,hisList,e))
        cout<<"the first common node is "<<e.data<<endl;

執行結果:
這裡寫圖片描述

單連結串列排序

///對單連結串列進行排序
/**< 可使用插入排序,每次從後面選擇一個結點,
在前面有序的序列選擇一個合適的位置插入 */
void SortList(LinkList L){
    if(L==NULL)
        return;
    LinkList q,r,s,x;
    r = L;
    x = L;
    L=L->next;
    bool flag;
    while(L!=NULL){
        ///在前面找到合適的位置插入
        s = r;
        q = r->next;
        flag = false;
        while(q!=NULL && q!=L){
            ///找到第一個比當前結點大的結點
            if(q->data > L->data){
                flag = true;
                x->next = L->next;
                L->next = q;
                s->next = L;
                //PrintList(r);
                break;
            }
            s = s->next;
            q = q->next;
        }
        if(flag!= true)
            x=x->next;
        L=x->next;
    }

}

測試程式碼:

LinkList L;
    InitList(&L);
    LNode e;
    e.data = 124;
    e.next = NULL;
    ListInsert(L,0,e);
    e.data = 132;
    ListInsert(L,0,e);

    LinkList myList;
    InitList(&myList);
    e.data = 83;
    ListInsert(myList,0,e);
    e.data = 19;
    ListInsert(myList,0,e);
    e.data = 46;
    ListInsert(myList,0,e);
    ConcatList(myList,L);
    cout<<"before sort:"<<endl;
    PrintList(myList);
    SortList(myList);
    cout<<"after sort:"<<endl;
    PrintList(myList);

執行結果:
這裡寫圖片描述

合併有序單連結串列

///合併有序單連結串列La、Lb,放到La裡面去,並且合併後的連結串列仍然有序
void MergeList(LinkList La,LinkList Lb){
    LinkList pa = La->next,pb = Lb->next,t;
    LNode *r;
    La->next  = NULL; ///La 作為結果連結串列的指標
    t = La;
    while(pa != NULL && pb != NULL){
        if(pa->data > pb->data){
            r = pb->next; ///r 暫存pb的後繼結點
            ///在La插入
            pb->next = La->next;
            La->next = pb;
            ///恢復pb為下一個待比較結點
            pb = r;
            La = La->next;
        }
        else{
            r = pa->next; ///r 暫存pb的後繼結點
            ///在La插入
            pa->next = La->next;
            La->next = pa;
            ///恢復pb為下一個待比較結點
            pa = r;
            La = La->next;
        }
        //PrintList(t);

    }
    if(pa!=NULL)
        La->next = pa;
    if(pb!=NULL)
        La->next = pb;
    delete Lb;
}

測試程式碼:

 LNode e;
    LinkList List1;
    InitList(&List1);
    e.data = 24;
    ListInsert(List1,0,e);
    e.data = 68;
    ListInsert(List1,0,e);
    e.data = 35;
    ListInsert(List1,0,e);
    e.data = 123;
    ListInsert(List1,0,e);
    e.data = 77;
    ListInsert(List1,0,e);
    e.data = 15;
    ListInsert(List1,0,e);
    SortList(List1);


    LinkList List2;
    InitList(&List2);
    e.data = 23;
    ListInsert(List2,0,e);
    e.data = 46;
    ListInsert(List2,0,e);
    e.data = 98;
    ListInsert(List2,0,e);
    e.data = 143;
    ListInsert(List2,0,e);
    e.data = 18;
    ListInsert(List2,0,e);
    e.data = 85;
    ListInsert(List2,0,e);
    SortList(List2);
    cout<<"list 1 is :"<<endl;
    PrintList(List1);
    cout<<"list 2 is :"<<endl;
    PrintList(List2);
    MergeList(List1,List2);
    cout<<"after merge:"<<endl;
    PrintList(List1);

執行結果:
這裡寫圖片描述

總結

雖然說單連結串列邏輯比較簡單,但是涉及指標操作,很容易出錯,我建議在紙上畫圖來編寫程式碼,這樣思路比較清晰。

相關文章