6-C/C++實現資料結構連結串列相關操作

outer_star發表於2018-08-12

本文程式碼分別包括:

標頭檔案、建立連結串列等

1建立連結串列

第8題驗證是否有環的建立連結串列

2遍歷連結串列函式

3刪除連結串列結點函式

4-1倒置連結串列函式-方法一改變指標指向

4-2倒置連結串列函式-方法二改變結點位置(頭插法,斷一個接一個)

5刪除倒數第n個結點-快慢指標法

6合併兩個有序連結串列

7兩數相加

8判斷連結串列中是否有環

*9求環的入口(較難,考試可能性小)

*10求環的長度

*11約瑟夫環-不包含頭結點的環刪除指定序列結點後相連

主函式


PART 1(自己寫的):

準備程式碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

typedef struct ListNode{
    int data;
    struct ListNode *next;
}ListNode;

1建立連結串列

//1建立連結串列
ListNode *CreatList(){
    ListNode *H,*M,*N;                              //定義頭結點指標H、向下連結結點指標M、新建立結點指標N
    int n,c;                                          //n為建立連結串列結點的個數,c為結點data值
    H = (ListNode *)malloc(sizeof(ListNode));       //分配一塊LinkNode型別的記憶體空間做頭結點
    H->next = NULL;                                 //頭結點指標域指向空
    M = H;                                          //M先指向頭結點

    printf("please input the number of nodes:");
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        N = (ListNode *)malloc(sizeof(ListNode));   //新建立子結點
        printf("please input %dth data:",i);
        scanf("%d",&c);                             //新建立結點data值
        N->data = c;
        M->next = N;                                //通過M指標將新結點連結進單連結串列
        M = N;                                      //M指向最後一個結點
    }
    M->next = NULL;
    printf("\n");
    return H;
}

第8題驗證是否有環的建立連結串列

//第8題驗證是否有環的建立連結串列
ListNode *CreatList2(){
    ListNode *H,*M,*N;
    int n,c;
    H = (ListNode *)malloc(sizeof(ListNode));
    H->next = NULL;
    M = H;

    printf("please input the number of nodes:");
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        N = (ListNode *)malloc(sizeof(ListNode));
        printf("please input %dth data:",i);
        scanf("%d",&c);
        N->data = c;
        M->next = N;
        M = N;
    }
    M->next = H->next->next;
    printf("\n");
    return H;
}

2遍歷連結串列函式

//2遍歷連結串列函式
void TraverseList(ListNode *pHead){
    ListNode *p = pHead->next;
    while(p != NULL)
    {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
}

3刪除連結串列結點函式

//3刪除連結串列結點函式
void Del_Node(ListNode *pHead,int n){
    ListNode *p = pHead;                        //定義p指向頭結點
    ListNode *q = pHead->next;                  //定義q總指向p的下一個結點,q指向待刪除元素
    int i = 1;
    while((q != NULL) && (n>=1) && (i<=n-1))    //迴圈終止條件為第n-1個元素已經是最後一個元素或者n小於1或者已經查詢到索刪位置
    {
        p = p->next;
        q = q->next;
        i++;
    }
    if(q == NULL)
        p->next = NULL;
    else{
        p->next = q->next;
        free(q);
    }
}

4-1倒置連結串列函式-方法一改變指標指向

//4-1倒置連結串列函式-方法一改變指標指向
ListNode *ReverseList(ListNode *pHead){
    ListNode *p,*q,*r;                  //定義三個指標依次指向頭結點後三個結點
    p = pHead->next;
    q = p->next;
    r = q->next;

    if(p == NULL || q == NULL)            //如果不存在結點或者只有一個結點,則不發生交換
        return NULL;
    while(r != NULL)                    //如果最後一個結點指向NULL,則不再需要向後移動指標
    {
        q->next = p;
        p = q;
        q = r;
        r = r->next;
    }
    q->next = p;                        //對最後兩個結點進行倒敘
    pHead->next->next = NULL;           //將第一個結點指向NULL
    pHead->next = q;                    //用頭結點連線連結串列第最後一個結點,完成倒敘
}

4-2倒置連結串列函式-方法二改變結點位置(頭插法,斷一個接一個)

//4-2倒置連結串列函式-方法二改變結點位置(頭插法,斷一個接一個)
ListNode *ReverseList2(ListNode * pHead){
    ListNode *p,*q;
    p = pHead->next;
    q = p->next;

    while(p != NULL && q != NULL)       //如果為空連結串列或者q指向空,表示無結點調整不再執行迴圈,注意此處為!=所以要用&&
    {
        p->next = q->next;              //將q結點摘出來插在頭結點後
        q->next = pHead->next;
        pHead->next = q;
        q = p->next;                    //更新指標,使得q總指向要調整的結點,而p指標一直指向最小值
    }
    return pHead;
}

5刪除倒數第n個結點-快慢指標法

//5刪除倒數第n個結點-快慢指標法
ListNode *Del_FromEnd(ListNode *pHead,int n){
    ListNode *slow,*fast;
    slow = pHead;
    fast = pHead;

    if(n<1)                          //輸入不合法返回
    {
        printf("wrong,can't do it!\n");
        return NULL;
    }
    while(fast->next != NULL && n)   //此處限制的無法刪除頭結點
    {
        fast = fast->next;
        n--;
    }
    while(fast->next != NULL)
    {
        slow = slow->next;
        fast = fast->next;
    }
    fast = slow->next;                  //複用fast指標指向刪除的結點用於free
    slow->next = slow->next->next;
    free(fast);
    return pHead;
}

6合併兩個有序連結串列

//6合併兩個有序連結串列
ListNode *MergeOderList(ListNode *pHead1,ListNode *pHead2){
    ListNode *nHead = NULL;                                         //定義一個新的頭結點
    ListNode *pMove = NULL;                                         //定義一個新的遍歷指標
    if(pHead1->next == NULL) return pHead2;                         //如果有一個連結串列為空則返回另一個連結串列頭
    else if(pHead2->next == NULL) return pHead1;

    if((pHead1->next->data) <= (pHead2->next->data))                //將第一個結點鏈入新連結串列的頭結點
        nHead = pHead1;
    else
        nHead = pHead2;

    pHead1 = pHead1->next;                                          //從第一個結點開始
    pHead2 = pHead2->next;
    pMove = nHead;

    while((pHead1 != NULL) && (pHead2 != NULL))                     //迴圈對比資料域的值,用pMove指標連線
    {
        if((pHead1->data) <= (pHead2->data))
        {
            pMove->next = pHead1;
            pMove = pHead1;
            pHead1 = pHead1->next;
        }
        else
        {
            pMove->next = pHead2;
            pMove = pHead2;
            pHead2 = pHead2->next;
        }
    }

    if(pHead1 == NULL)                                              //最後一個結點鏈入
        pMove->next = pHead2;
    else if(pHead2 == NULL)
        pMove->next = pHead1;
    return nHead;
}

 


7兩數相加

//7兩數相加
ListNode *NumberAdd(ListNode *pHead1,ListNode *pHead2){
    ListNode *nHead = (ListNode *)malloc(sizeof(ListNode));     //建立頭結點
    nHead->next = NULL;
    ListNode *pMove = nHead;                                    //設立一個移動指標用於連結後方結點
    int symbol = 0;                                             //進位標誌
    pHead1 = pHead1->next;                                      //兩個相加連結串列從第一個結點開始
    pHead2 = pHead2->next;

    while((pHead1 != NULL) || (pHead2 != NULL))                 //迴圈終止條件
    {
        int x = (pHead1 != NULL)? pHead1->data:0;               //長度不一致時將先為NULL的置為0以相加時可以得到數值
        int y = (pHead2 != NULL)? pHead2->data:0;
        int sum = x + y + symbol;


        ListNode *p = (ListNode *)malloc(sizeof(ListNode));     //建立結點

        if(sum>=10)
        {
            p->data = sum % 10;
            symbol = 1;
        }
        else
            {
                p->data = sum;
                symbol = 0;
            }
        pMove->next = p;                                        //使用pMove指標將每次新建立的結點p鏈進連結串列
        pMove = p;
        if(pHead1 != NULL) pHead1 = pHead1->next;               //兩個連結串列向下遍歷
        if(pHead2 != NULL) pHead2 = pHead2->next;
    }
    pMove->next = NULL;

    if(symbol>0)                                                //如果最後一位相加後有進位,需要單獨再建立一個結點儲存進位值
    {
        ListNode *p = (ListNode *)malloc(sizeof(ListNode));
        p->data = symbol;
        pMove->next = p;
        p->next = NULL;
    }
    return nHead;
}

8判斷連結串列中是否有環

//8判斷連結串列中是否有環
void IfCircle(ListNode *pHead){
    ListNode *slow = pHead,*fast = pHead;       //設定快慢指標,慢指標每次走1步,快指標每次走2步
    if(pHead->next == NULL)
        printf("NO NODE!!");
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            printf("I HAVE A CIRCLE!!\n");
            break;
        }
    }
}

*9求環的入口(較難,考試可能性小)

//*9求環的入口(較難,考試可能性小)
ListNode *CircleEntrance(ListNode *pHead){
    ListNode *slow = pHead,*fast = pHead,*Len = pHead;       //設定快慢指標,慢指標每次走1步,快指標每次走2步,再設定一個指標用於遍歷頭結點到入口距離
    int cot = 0;                                             //cot用來統計環入口距離頭結點距離
    if(pHead->next == NULL)
        printf("NO NODE!!");

    while(fast != NULL && fast->next !=NULL)                //注意連結串列為奇數偶數時fast移動後指向的位置
    {
        slow = slow->next;
        fast = fast->next->next;
        /*分析:設頭結點到環入口距離為a,環長為r,slow和fast相遇時slow在環中走過的距離為x。可得相遇時慢指標走過的長度為a+x,
        快指標走過的距離為(a+x+r),由於fast=2倍slow,可得a=r-x,而r-x也等於slow距離環入口的長度*/
        if(slow == fast)
        {
            printf("I HAVE A CIRCLE!\n");
            while(Len != slow)              //見分析,slow與fast相遇點到環入口的距離等於頭結點到環入口的距離
            {
                Len = Len->next;
                slow = slow->next;
                cot++;
            }
            printf("The Circle Entrance is %dth Node!\n",cot);
            return Len;
        }
    }
    if(fast == NULL || fast->next == NULL)
        return NULL;
}

*10求環的長度

//*10求環的長度
int CircleLength(ListNode *pHead){
    ListNode *Head;
    ListNode *a = CircleEntrance(Head);
    ListNode *b = a->next;                  //b為環入口的下一位,使用者驗證走完一圈因為下面迴圈判斷的時候不能相等,所以這裡錯一位
    int cot = 1;                                //cot用來統計環長

    if(a == NULL)
        return 0;

    while(b != a)                           //b=a時即為環長
    {
        b=b->next;
        cot++;
    }
    printf("Circle Length is %d\n",cot);
    return cot;
}

*11約瑟夫環-不包含頭結點的環刪除指定序列結點後相連

//*11約瑟夫環-不包含頭結點的環刪除指定序列結點後相連
void JosphCircle(ListNode *pHead,int n){
    ListNode *p = pHead->next;              //p指向第一個結點
    ListNode *Del = NULL;                   //刪除結點指標
    int cot=0;                              //cot為統計環長
    while(pHead->next != NULL)
        {
            pHead = pHead->next;
            cot++;
        }
    pHead->next = p;                        //將連結串列變成環
    if(n>=cot) printf("Bad Man!!");         //刪除序列大於環長則不存在約瑟夫環
    while(p->next != p)
    {
        for(int i=1;i<=n;i++)
            p = p->next;
        Del = p->next;
        printf("DELE Node Number is %d\n",Del);
        p->next = Del->next;
        free(Del);
        /*此方法不對的原因是刪除的是不是p本身而是下一個元素?
        p->data = p->next->data;
        Del = p->next;
        p->next = Del->next;
        free(Del);*/
    }
}

主函式

int main()
{
    ListNode *chain,*chain1,*chain2;
    chain = CreatList();
    //Del_Node(chain,4);
    //ReverseList2(chain);
    //Del_FromEnd(chain,5);
    //chain1 = CreatList();
    //chain2 = CreatList();
    //chain = MergeOderList(chain1,chain2);
    //chain = NumberAdd(chain1,chain2);
    JosphCircle(chain,2);
    TraverseList(chain);
    //chain = CreatList2();
    //IfCircle(chain);
    //CircleEntrance(chain);
    //CircleLength(chain);

    return 0;
}

PART 2(西交無盡):

#include<stdio.h>
#include<stdlib.h>
#define null NULL
//Definition of ListNode
typedef struct list_node
{
	int data ; //資料域,用於儲存資料
	struct list_node *next ; //指標,可以用來訪問節點資料,也可以遍歷,指向下一個節點
}ListNode;


//尾插法建立連結串列
ListNode *CreateList(int n)
{
    ListNode *head;
    ListNode *p,*pre;
    int i;
    head=(ListNode *)malloc(sizeof(ListNode));
    head->next=NULL;
    pre=head;
    for(i=1; i<=n; i++)
    {
        printf("input name of the %d data:",i);
        p=(ListNode *)malloc(sizeof(ListNode));

        scanf("%d",&p->data);
        pre->next=p;

        pre=p;
    }
    p->next = NULL;
    //p->next=head->next->next;
    return head;
}

//遍歷連結串列函式
void TraverseList(ListNode *pHead)
{
    ListNode *p = pHead->next; //將頭節點的指標給予臨時節點p
    while(p != NULL)           //節點p不為空,迴圈
    {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return ;
}


//刪除連結串列節點函式
int Del_Node(ListNode *pHead,int back)
{
    int i = 0;
    int data;
    ListNode *_node = pHead;
    ListNode *pSwap;
    if ((back < 1) && (NULL == _node->next))
    {
        printf("刪除失敗!\n");
        return 0;
    }
    while(i < back-1)
    {
        _node = _node->next;
        ++i;
    }
    pSwap = _node->next;
    data = pSwap->data;
    _node->next = _node->next->next;
    free(pSwap);
    return data;
}


//倒置連結串列3
ListNode *node2(ListNode *pHead){
    pHead = pHead->next;
    ListNode *p = NULL;
    while(pHead){
        ListNode *nx = pHead->next;
        pHead->next = p;
        p = pHead;
        pHead = nx;
    }
    return p;
}


//倒置連結串列1
ListNode *node1(ListNode *pHead){
    ListNode *p = NULL;
    ListNode *q = NULL;
    ListNode *r = NULL;
    if(pHead->next == NULL){return NULL;}
    if(pHead->next->next == NULL){return pHead;}
    else if(pHead->next->next->next == null) {
    p = pHead->next;
    q = p->next;
    q->next = p;
    pHead->next = q;
    p->next = NULL;
    }
    else if(pHead->next->next->next != null){

    p = pHead->next;
    q = p->next;
    r = q->next;
    p->next = null;
    while(r != NULL){
    q->next = p;
    p = q;
    q = r;
    r = r->next;
    }
    q->next = p;
    pHead->next = q;
    }
    return pHead;

}


//倒置連結串列3
ListNode* ReverseList(ListNode* pHead)
{
    ListNode *q=pHead->next;
    ListNode *p=NULL;
    ListNode *r=NULL;
    if(pHead->next==NULL)
        return NULL;
    while(q->next)
    {
        r=q->next;
        q->next=p;
        p=q;
        q=r;
    }
    q->next=p;
    pHead->next = q;
    return pHead;
}


//刪除倒數第N個節點
ListNode *removeNthFromEnd(ListNode *head, int n)
{
    // write your code here
    ListNode *fast = head;
    ListNode *low  = head;
    ListNode *pre = NULL;

    if(head == NULL  || n <= 0)
    {
        return NULL;   //輸入的連結串列為空,或輸入的n不合法;
    }

    for(int i = 0; i < n-1; i ++)
    {
        if(fast->next)
        {
            fast = fast->next;
        }

        else
        {
            return NULL;   //輸入的n大於連結串列的長度;
        }

    }

    while(fast->next)
    {
        fast = fast->next;
        pre  = low;      //需要儲存需要刪除的那個結點的上一個結點。
        low  = low->next;
    }

    if(low == head)   //刪除的那個結點是頭結點。
    {
        head = head->next;
    }
    else
    {
        pre->next = pre->next->next;
    }

    return head;
}


//合併兩個有序連結串列
ListNode *MergeTwoOrderedLists(ListNode *pHead1, ListNode *pHead2)
{
    ListNode *pTail = NULL;//指向新連結串列的最後一個結點 pTail->next去連線
    ListNode *newHead = NULL;//指向合併後連結串列第一個結點

    if (pHead1->next == NULL)
    {
        return pHead2;
    }
    else if(pHead2->next == NULL)
    {
        return pHead1;
    }
    else
    {
        //確定頭指標
        if ( pHead1->next->data < pHead2->next->data)
        {
            newHead = pHead1;
        }
        else
        {
            newHead = pHead2;
        }
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        pTail = newHead;    //指向第一個結點
        while ( pHead1 && pHead2)
        {
            if ( pHead1->data <= pHead2->data )
            {
                pTail->next = pHead1;
                pHead1 = pHead1->next;
            }
            else
            {
                pTail->next = pHead2;
                pHead2 = pHead2->next;
            }
            pTail = pTail->next;
        }

        if(pHead2 != NULL)
        {
            pTail->next = pHead2;
        }
        else if(pHead1 != NULL)
        {
            pTail->next = pHead1;
        }
        return newHead;
    }
}


//兩數相加
ListNode *addTwoNumbers(ListNode *pHead1, ListNode *pHead2) {
    ListNode *L,*curr;
    L = (ListNode *)malloc(sizeof(ListNode));
    L->next = NULL;
    curr = L;
    ListNode *p = pHead1->next, *q = pHead2->next;
    int carry = 0;
    while (p != null || q != null) {
        int x = (p != null) ? p->data : 0;
        int y = (q != null) ? q->data : 0;
        int sum = carry + x + y;
        carry = sum / 10;
        ListNode *r = (ListNode *)malloc(sizeof(ListNode));
        r->data = sum%10;
        curr->next = r;
        curr = curr->next;
        if (p != null) p = p->next;
        if (q != null) q = q->next;
    }
        curr->next = NULL;
    if (carry > 0) {
        ListNode *r = (ListNode *)malloc(sizeof(ListNode));
        r->data = carry;
        curr->next = r;
        curr->next->next = NULL;
    }
    return L;
}

//檢查環
int HasCircle(ListNode * pHead)
{
    ListNode * pFast = pHead; // 快指標每次前進兩步
    ListNode * pSlow = pHead; // 慢指標每次前進一步
    while(pFast != NULL && pFast->next != NULL)
    {
        pFast = pFast->next->next;
        pSlow = pSlow->next;
        if(pSlow == pFast) // 相遇,存在環
            return 1;
    }
    return 0;
}

//找出環的入口
ListNode  *searchEntranceNode(ListNode *pHead)
{
    ListNode *pSlow=pHead;//p表示從頭結點開始每次往後走一步的指標
    ListNode *pFast=pHead;//q表示從頭結點開始每次往後走兩步的指標
    while(pFast !=NULL && pFast->next !=NULL)
    {
        pSlow=pSlow->next;
        pFast=pFast->next->next;
        if(pSlow == pFast){
            break;//p與q相等,單連結串列有環
        }
    }
    if(pFast==NULL || pFast->next==NULL)
        return NULL;

    pSlow=pHead;
    while(pSlow != pFast)
    {
        pSlow=pSlow->next;
        pFast=pFast->next;
    }
    return pSlow;
}

//求環長
int circleLength(ListNode *pHead)
{
    ListNode *p=searchEntranceNode(pHead);//找到環的入口結點
    if(p==null)
        return 0;//不存在環時,返回0
    ListNode *q = p->next;
    int length=1;
    while(p != q)
    {
        length++;
        q=q->next;
    }
    return length;//返回環的長度
}

//約瑟夫環
void JosephProblem(ListNode *pHead,int m){
    ListNode *p = pHead->next;
    while(pHead->next != NULL) pHead = pHead->next;
    pHead->next = p;

    while(p->next != p){
        for(int i = 0;i<m-1;i++) p=p->next;

        //列印
        printf("%d ",p->next->data);

        ListNode *del = p->next;
        p->next = del->next;
        free(del);
        /*
        //特殊的刪除方法
        p->data = p->next->data;
        ListNode *del = p->next;
        p->next = del->next;
        free(del);
        */
    }
}

int main()
{
    ListNode *chain,*nchain,*nnchain,*nnnchain = NULL;
    int n,back2;
    chain = CreateList(5);
    JosephProblem(chain,2);
    return 0;
}

(感謝西交無盡學長提供以上題目練習)

快慢指標參考:http://www.cnblogs.com/hxsyl/p/4395794.html

相關文章