1. 程式人生 > 實用技巧 >迴圈連結串列

迴圈連結串列

迴圈連結串列

迴圈連結串列概念

對於單鏈表以及雙向連結串列,其就像一個小巷,無論怎麼樣最終都能從一端走到另一端,然而迴圈連結串列則像一個有傳送門的小巷,因為迴圈連結串列當你以為你走到結尾的時候,其實你又回到了開頭。

迴圈連結串列和非迴圈連結串列其實建立的過程以及思路幾乎完全一樣,唯一不同的是,非迴圈連結串列的尾結點指向空(NULL),而迴圈連結串列的尾指標指向的是連結串列的開頭。通過將單鏈表的尾結點指向頭結點的連結串列稱之為迴圈單鏈表(Circular linkedlist)

如圖,為一個完整的迴圈單鏈表

迴圈連結串列結點設計(以單迴圈連結串列為例)

對於迴圈單鏈表的結點,可以完全參照於單鏈表的結點設計,如圖:

data表示資料,其可以是簡單的型別(如int,double等等),也可以是複雜的結構體(struct型別)

next表示指標,它永遠指向自身的下一個結點,對於只有一個結點的存在,這個next指標則永遠指向自身,對於一個連結串列的尾部結點,next永遠指向開頭。

其程式碼可以表示為:

typedef struct list{
    int data;
    struct list *next;
}list;
//data為儲存的資料,next指標為指向下一個結點

迴圈單鏈表初始化

如同單鏈表的建立,我們需要先建立一個頭結點並且給其開闢記憶體空間,但與單鏈表不同的是,我們需要在開闢記憶體空間成功之後將頭結點的next指向head自身,我們可以建立一個init函式來完成這件事情,為了以後的重複建立和插入,我們可以考慮在init重建立的結點next指向空,而在主函式呼叫建立之後手動講head頭結點的next指標指向自身。

這樣的操作方式可以方便過後的建立單鏈表,直接利用多次呼叫的插入函式即可完成整體建立。

其程式碼可以表示為:

//初始結點
list *initlist(){
    list *head=(list*)malloc(sizeof(list));
    if(head==NULL){
        printf("建立失敗,退出程式");
        exit(0);
    }else{
        head->next=NULL;
        return head;
    }
}

迴圈連結串列的建立操作

如圖所示:

我們可以通過逐步的插入操作,建立一個新的節點,將原有連結串列尾結點的next指標修改指向到新的結點,新的結點的next指標再重新指向頭部結點,然後逐步進行這樣的插入操作,最終完成整個單項迴圈連結串列的建立。

其程式碼可以表示為:

int insert_list(list* head){
    int data;
    cout<<"enter your data"<<endl;
    cin>>data;
    list* node =initlist();
    node->data=data;
    if (head!=NULL)
    {
        list *p = head;
        while (p->next!=head){
            p=p->next;
        }
        p->next=node;
        node->next=head;
        return 1;
    }else
    {
        cout<<"The head node has no element"<<endl;
        return 0;
    }
}

迴圈連結串列的插入操作

如圖,對於插入資料的操作,基本與單鏈表的插入操作相同,我們可以建立一個獨立的結點,通過將需要插入的結點的上一個結點的next指標指向該節點,再由需要插入的結點的next指標指向下一個結點的方式完成插入操作。

其程式碼可以表示為:

//插入元素
list *insert_list(list *head,int pos,int data){
    //三個引數分別是連結串列,位置,引數
    list *node=initlist();  //新建結點
    list *p=head;       //p表示新的連結串列
    list *t;
    t=p;
    node->data=data;
    if(head!=NULL){
        for(int i=1;i<pos;i++){
            t=t->next;  //走到需要插入的位置處
        }
        node->next=t->next;
        t->next=node;
        return p;
    }
    return p;
}

迴圈單鏈表的刪除操作

如圖所示,迴圈單鏈表的刪除操作可以參考單鏈表的刪除操作,其都是找到需要刪除的結點,將其前一個結點的next指標直接指向刪除結點的下一個結點即可,但需要注意的是尾節點和頭結點的特判,尤其是尾結點,因為刪除尾節點後,尾節點前一個結點就成了新的尾節點,這個新的尾節點需要指向的是頭結點而不是空,其重點可以記錄為【當前的前一節點.next=自身結點.next】這樣的操作可以省去頭尾結點的特判:

//刪除元素
int delete_list(list *head) {
    if(head == NULL) {
        printf("連結串列為空!\n");
        return 0;
    }
    //建立臨時結點儲存頭結點資訊(目的為了找到退出點)
    //如果不這麼建立的化需要使用一個數據進行計數標記,計數達到連結串列長度時自動退出
    //迴圈連結串列當找到最後一個元素的時候會自動指向頭元素,這是我們不想讓他發生的
    list *temp = head;          
    list *ptr = head->next;
  
    int del;
    printf("請輸入你要刪除的元素:");
    scanf("%d",&del);
  
    while(ptr != head) {
        if(ptr->data == del) {
            if(ptr->next == head) { 
                temp->next = head;
                free(ptr);
                return 1;
            }
            temp->next = ptr->next;    //核心刪除操作程式碼
            free(ptr);
            //printf("元素刪除成功!\n");
            return 1;
        }
        temp = temp->next;
        ptr = ptr->next;
    }
    printf("沒有找到要刪除的元素\n");
    return 0;
}

迴圈單鏈表的遍歷

與普通的單鏈表和雙向連結串列的遍歷不同,迴圈連結串列需要進行結點的特判,找到尾節點的位置,由於尾節點的next指標是指向頭結點的,所以不能使用連結串列本身是否為空(NULL)的方法進行簡單的迴圈判斷,我們需要通過判斷結點的next指標是否等於頭結點的方式進行是否完成迴圈的判斷。

此外還有一種計數的方法,即建立一個計數器count=0,每一次next指標指向下一個結點時計數器加一,當count數字與連結串列的節點數相同的時候即完成迴圈,這樣做有一個問題,就是獲取到連結串列的節點數同時也需要完成一次遍歷才可以達成目標。

其程式碼可以表示為:

//遍歷元素
int display(list *head) {
    if(head != NULL) {
        list *p  = head;
        //遍歷頭節點到,最後一個數據
        while(p->next != head ) {
            printf("%d   ",p->next->data);
            p = p->next;
        }
        printf("\n");   //習慣性換行 ( o=^•ェ•)o ┏━┓
        //把最後一個節點賦新的節點過去
        return 1;
    } else {
        printf("頭結點為空!\n");
        return 0;
    }
}

迴圈單鏈表程式碼

#include<stdio.h>
#include<stdlib.h>
typedef struct list{
    int data;
    struct list *next;
}list;
//data為儲存的資料,next指標為指向下一個結點
 
//初始結點
list *initlist(){
    list *head=(list*)malloc(sizeof(list));
    if(head==NULL){
        printf("建立失敗,退出程式");
        exit(0);
    }else{
        head->next=NULL;
        return head;
    }
}
 
//建立--插入資料
int create_list(list *head){
    int data;   //插入的資料型別
    printf("請輸入要插入的元素:");
    scanf("%d",&data);
 
    list *node=initlist();
    node->data=data;
    //初始化一個新的結點,準備進行連結
 
    if(head!=NULL){
        list *p=head;
        //找到最後一個數據
        while(p->next!=head){
            p=p->next;
        }
        p->next=node;
        node->next=head;
        return 1;
    }else{
        printf("頭結點已無元素\n");
        return 0;
    }
 
}
 
//插入元素
list *insert_list(list *head,int pos,int data){
    //三個引數分別是連結串列,位置,引數
    list *node=initlist();  //新建結點
    list *p=head;       //p表示新的連結串列
    list *t;
    t=p;
    node->data=data;
    if(head!=NULL){
        for(int i=1;i<=pos;i++){
            t=t->next;
        }
        node->next=t->next;
        t->next=node;
        return p;
    }
    return p;
}
 
//刪除元素
int delete_list(list *head) {
    if(head == NULL) {
        printf("連結串列為空!\n");
        return 0;
    }
    //建立零時結點儲存頭結點資訊(目的為了找到退出點)
    //如果不這麼建立的化需要使用一個數據進行計數標記,計數達到連結串列長度時自動退出
    //迴圈連結串列當找到最後一個元素的時候會自動指向頭元素,這是我們不想讓他發生的
    list *temp = head;          
    list *ptr = head->next;
 
    int del;
    printf("請輸入你要刪除的元素:");
    scanf("%d",&del);
 
    while(ptr != head) {
        if(ptr->data == del) {
            if(ptr->next == head) { //迴圈結束的條件換成ptr->next == head
                temp->next = head;
                free(ptr);
                return 1;
            }
            temp->next = ptr->next;
            free(ptr);
            //printf("元素刪除成功!\n");
            return 1;
        }
        temp = temp->next;
        ptr = ptr->next;
    }
    printf("沒有找到要刪除的元素\n");
    return 0;
}
 
 
//遍歷元素
int display(list *head) {
    if(head != NULL) {
        list *p  = head;
        //遍歷頭節點到,最後一個數據
        while(p->next != head ) {
            printf("%d   ",p->next->data);
            p = p->next;
        }
        printf("\n");   //習慣性換行 ( o=^•ェ•)o ┏━┓
        //把最後一個節點賦新的節點過去
        return 1;
    } else {
        printf("頭結點為空!\n");
        return 0;
    }
}
 
int main(){
    //////////初始化頭結點//////////////
    list *head=initlist();
    head->next=head;
    ////////通過插入元素完善連結串列/////////
    for(int i=0;i<5;i++){   //只是演示使用,不具體提供輸入
        create_list(head);
    }
    display(head);
    ////////////插入元素////////////////
    head=insert_list(head,1,10);
    display(head);
    ////////////刪除元素////////////////
    delete_list(head);
    display(head);
    return 0;
}
 
View Code