四畏鼓、線性表(六)、 循環(huán)鏈表 約瑟夫問題

數(shù)據(jù)結(jié)構(gòu)目錄

1.概念

對于單鏈表壶谒,由于每個結(jié)點值存儲了向后的指針云矫,到了尾部標識就停止了向后鏈的操作。也就是說汗菜,按照這樣的方式让禀,只能索引后繼結(jié)點不能索引前驅(qū)結(jié)點。

這樣的話陨界,假如不從頭結(jié)點觸發(fā)巡揍,就無法訪問到全部結(jié)點。為了解決這個問題普碎,我們將單鏈表中終端結(jié)點的指針由空指針改為指向頭結(jié)點吼肥,問題就解決了录平。

定義
將單鏈表中終端結(jié)點的指針端由空指針改為指向頭結(jié)點麻车,就使整個單鏈表形成一個環(huán)缀皱,這種頭尾相接的單鏈表稱為單循環(huán)鏈表,簡稱循環(huán)鏈表动猬。

循環(huán)鏈表

注意:
并不是說循環(huán)鏈表一定要有頭結(jié)點
循環(huán)鏈表和單鏈表的主要差異就在于循環(huán)的判斷空鏈表的條件上啤斗,單 鏈表是判斷head->next是否為NULL,現(xiàn)在則是head->next是否等于head

下方示例都以尾指針rear來指代循環(huán)鏈表而不是頭指針,而且示例中的循環(huán)鏈表都沒有頭結(jié)點,也就是說赁咙,循環(huán)鏈中第一個結(jié)點钮莲,實際上是尾結(jié)點(這點需要發(fā)揮一點想象力)
由于終端結(jié)點用尾指針rear指示,則查找終端結(jié)點的復雜度為O(1)彼水,而開始結(jié)點是rear->next->next,當然也是O(1)

2. 循環(huán)鏈表的定義

typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;
typedef struct Node *CircleLinkList;

3. 循環(huán)鏈表的創(chuàng)建

void initCircleLinkList(CircleLinkList *L){
    //表示輸入的內(nèi)容
    int item;
    CircleLinkList temp;
    //target每次用來標記尾結(jié)點
    CircleLinkList target = NULL;
    printf("輸入結(jié)點的值崔拥,輸入0表示完成初始化\n");
    
    while (1) {
        scanf("%d",&item);
        fflush(stdin);
        
        if (item == 0) {
            return;
        }
        if (*L == NULL) {
            /*循環(huán)鏈表為空 則創(chuàng)建第一個結(jié)點 作為尾結(jié)點*/
            *L = (CircleLinkList)malloc(sizeof(Node));
            if (!(*L)) {
                exit(0);
            }
            (*L)->data = item;
            (*L)->next = *L;
        } else {
            //找到尾結(jié)點前面第一個結(jié)點
            for (target = *L; target->next != *L; target = target->next);
            temp = (CircleLinkList)malloc(sizeof(Node));
            if (!temp) {
                exit(0);
            }
            //在尾結(jié)點之前插入一個新的結(jié)點
            temp->data = item;
            temp->next = *L;
            target->next = temp;
        }
    }
}

4. 循環(huán)鏈表的插入

/// 插入結(jié)點
/// @param L 鏈表的尾結(jié)點
/// @param i 插入的位置
void insertCircleLinkList(CircleLinkList *L,int i){
    CircleLinkList temp,target;
    int item,j = 1;
    printf("請輸入要插入結(jié)點的值:");
    scanf("%d",&item);
    
    if (i == 1) {
        //i==1 表示要替換到之前的尾結(jié)點
        temp = (CircleLinkList)malloc(sizeof(Node));
        if (!temp) {
            exit(0);
        }
        temp->data = item;
        //循環(huán)得到尾結(jié)點前面一個結(jié)點
        for (target = *L; target->next != *L; target = target->next);
        
        temp->next = *L;
        target->next = temp;
        //將新結(jié)點放在尾結(jié)點位置
        *L = temp;
    } else {
        target = *L;
        //通過i-2次遍歷,得到i位置前面一個結(jié)點
        for (; j < (i-1); ++j) {
            target = target->next;
        }
        temp = (CircleLinkList)malloc(sizeof(Node));
        if (!temp) {
            exit(0);
        }
        temp->data = item;
        temp->next = target->next;
        target->next = temp;
    }
}

5.循環(huán)鏈表的刪除

void deleteCircleLinkList(CircleLinkList *L,int i){
    CircleLinkList target,temp;

    if (i == 1) {
        //刪除尾指針的結(jié)點
        //獲取到尾結(jié)點前面的一個結(jié)點
        for (target = *L; target->next != *L; target = target->next);
        //重新指向
        temp = *L;
        *L = (*L)->next;
        target->next = *L;
        //釋放結(jié)點
        free(temp);
    } else {
        //獲取到i-1位置的結(jié)點
        target = *L;
        for (int j = 1; j < i-1; j++) {
            target = target->next;
        }
        temp = target->next;
        target->next = target->next->next;
        free(temp); 
    }
}

6.循環(huán)鏈表返回結(jié)點的位置

//通過數(shù)據(jù)的值凤覆,得到結(jié)點位置
int searchCircleLinkList(CircleLinkList L,int elem){
    CircleLinkList target;
    int i = 1;
    for (target = L; target->data != elem && target->next != L; ++i) {
        target = target->next;
    }
    if (target->next == L) {
        //找了一圈還是沒找到
        return 0;
    }
    return i;
}

7链瓦、循環(huán)鏈表的特點

循環(huán)鏈表
  • 使用rear是否等于rear->next來判斷循環(huán)鏈表是否為空表

一個例子:


合并兩個鏈表

題目:實現(xiàn)將兩個線性表(a1,a2,...,an)和(b1,b2,...,bm)連接成一個線性表(a1,...an,b1,...bm)的運算
分析:
若在單鏈表或頭指針表示的單循環(huán)表上左這種鏈接操作,都需要遍歷第一個鏈表盯桦,找到結(jié)點an,然后將結(jié)點b1鏈到an的后面慈俯,其執(zhí)行時間是O(n)
若在尾指針表示的單循環(huán)鏈表上實現(xiàn),則只需要修改指針拥峦,無需遍歷贴膘,其執(zhí)行時間為O(1)

CircleLinkList connectLinkList(CircleLinkList L1,CircleLinkList L2){
    CircleLinkList head = L1->next; //保存L1的頭結(jié)點位置
    L1->next = L2->next->next; //L1的尾結(jié)點指向L2的第一個結(jié)點
    free(L2->next); //是否L2的頭結(jié)點
    L2->next = head; //讓L2的尾結(jié)點指向L1的頭結(jié)點
    
    return L2;
}

8、約瑟夫問題

據(jù)說著名猶太歷史學家 Josephus有過以下的故事:在羅馬人占領(lǐng)喬塔帕特后略号,39 個猶太人與Josephus及他的朋友躲到一個洞中刑峡,39個猶太人決定寧愿死也不要被敵人抓到,于是決定了一個自殺方式玄柠,41個人排成一個圓圈氛琢,由第1個人開始報數(shù),每報數(shù)到第3人該人就必須自殺随闪,然后再由下一個重新報數(shù)阳似,直到所有人都自殺身亡為止。然而Josephus 和他的朋友并不想遵從铐伴。首先從一個人開始撮奏,越過k-2個人(因為第一個人已經(jīng)被越過),并殺掉第k個人当宴。接著畜吊,再越過k-1個人,并殺掉第k個人户矢。這個過程沿著圓圈一直進行玲献,直到最終只剩下一個人留下,這個人就可以繼續(xù)活著。問題是捌年,給定了和瓢娜,一開始要站在什么地方才能避免被處決?Josephus要他的朋友先假裝遵從礼预,他將朋友與自己安排在第16個與第31個位置眠砾,于是逃過了這場死亡游戲。

解題思路:

  • 創(chuàng)建一個包含41個結(jié)點的循環(huán)鏈表(沒有頭結(jié)點),每個節(jié)點的數(shù)據(jù)域都存放一個序號托酸,分別為1-41
  • 模擬計數(shù)過程褒颈,每次數(shù)到3則刪除這個節(jié)點,并在下一個節(jié)點開始重新計數(shù)励堡,最后得到免死的序號與人數(shù)
//創(chuàng)建一個不包含頭結(jié)點的循環(huán)鏈表
Node *create(int n){
    //head用于存儲第一個結(jié)點的地址
    Node *p = NULL,*s = NULL,*head = NULL;
    
    for (int i = 1;i <= n; i++) {
        p = (Node *)malloc(sizeof(Node));
        p->data = i;
        if (i == 1) {
            //第一個結(jié)點
            head = s = p;
        } else {
            s->next = p;
            s = p;
        }
    }
    //構(gòu)成循環(huán)
    s->next = head;
    
    return head;
}
//解決約瑟夫問題谷丸,返回免死的人數(shù)
void soulutionProblem(int *args){
    //n表示總?cè)藬?shù),m表示數(shù)到幾
    int n = 41,m = 3;
//    scanf("%d%d",&n,&m);
    Node *L = create(n);
    Node *p = L;
    
    int counter = 1;
    while (p != p->next) {
        if (counter % m == 0) {
            //數(shù)到了這個數(shù)字
            /*
             這個就是需要自殺的人 我們刪除這個節(jié)點
             然后再從下一個開始再排
             */
            Node *temp = p->next;
            p->next = temp->next;
            free(temp);
            p = p->next;
            counter = 1;
            n--;
            if (n < m) {
                //如果剩下總?cè)藬?shù)比計數(shù)的總?cè)藬?shù)還少的時候
                //剩下的這些人就可以耍賴不自殺了
                *args = n;
                while (p != p->next) {
                    printf("%d\n",p->next->data);
                    Node *temp = p->next;
                    p->next = temp->next;
                    free(temp);
                    p = p->next;
                }
                printf("%d\n",p->data);
                free(p);
                
                break;
            }
        } else {
            counter++;
            if (counter % m != 0) {
                p = p->next;
            }
        }
    }
}

9.約瑟夫問題簡易方法

推導過程可以查看約瑟夫簡便方法 或者 約瑟夫問題百度百科

大致思路就是逆推得到n人应结、m個數(shù)的問題淤井,可以由(n-1)人推導到位置,依次類推,最后得到單次的公式為(ans + m)% n,其中ams為最后出列人的序號摊趾,m表示要數(shù)的數(shù)字币狠,n表示單次總?cè)藬?shù),下面給出代碼

void simpleSolution(){
    int n, m, i, s = 0;
    n = 41;m = 3;
    scanf("%d%d", &n, &m);
    //經(jīng)過n-1次的推導砾层,單次的總?cè)藬?shù)為i
    for (i = 2; i <= n; i++)
    {
        s = (s + m) % i;
        printf("%d\n",s);
    }
    printf ("\nThe winner is %d\n", s+1);
}

這種方法的缺點是只能得到最終一個人漩绵,不能求得其它可以免于自殺的人的序號

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肛炮,隨后出現(xiàn)的幾起案子止吐,更是在濱河造成了極大的恐慌,老刑警劉巖侨糟,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碍扔,死亡現(xiàn)場離奇詭異,居然都是意外死亡秕重,警方通過查閱死者的電腦和手機不同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溶耘,“玉大人二拐,你說我怎么就攤上這事〉时” “怎么了百新?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庐扫。 經(jīng)常有香客問我饭望,道長仗哨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任铅辞,我火速辦了婚禮厌漂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巷挥。我一直安慰自己,他們只是感情好验靡,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布倍宾。 她就那樣靜靜地躺著,像睡著了一般胜嗓。 火紅的嫁衣襯著肌膚如雪高职。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天辞州,我揣著相機與錄音怔锌,去河邊找鬼。 笑死变过,一個胖子當著我的面吹牛埃元,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播媚狰,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼岛杀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了崭孤?” 一聲冷哼從身側(cè)響起类嗤,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辨宠,沒想到半個月后遗锣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡嗤形,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年精偿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赋兵。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡还最,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毡惜,到底是詐尸還是另有隱情拓轻,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布经伙,位于F島的核電站扶叉,受9級特大地震影響勿锅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枣氧,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一溢十、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧达吞,春花似錦张弛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至覆糟,卻和暖如春刻剥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滩字。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工造虏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人麦箍。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓漓藕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挟裂。 傳聞我的和親對象是個殘疾皇子撵术,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345