1. 程式人生 > >數據結構(一)線性表循環鏈表相關補充

數據結構(一)線性表循環鏈表相關補充

width hide cli 機器 都是 實時 思路 在外 for循環

(一)合並兩個循環鏈表

技術分享圖片

技術分享圖片

    p = rearA->next;    //A的頭結點,一會還要使用
    rearA->next = rearB->next->next;    //是A的尾結點指向B的第一個結點
    q = rearB->next;    //存放B的頭結點,需要釋放
    rearB->next = p;    //使B的尾結點指向A的頭結點
    free(q);    //釋放B的頭結點

(二)判斷單鏈表中是否有環

技術分享圖片

方法一:使用兩個指針,循環嵌套,A指針在外層循環,一步一步向下走,B指針在內層循環,循環到A的位置,當兩者的位置相同時判斷走的步數是否一致,不一致則代表有環。且能夠得到準確的環路節點。其中A是要將鏈表從頭走到尾,B是一直在內層進行循環,時間復雜度為O(n^2)

//兩層循環進行判斷
Status HasCircle01(List L,int *seq)
{
    List la, lb;
    int stepa, stepb;
    la = lb = L;    //la在外層循環,lb在內層循環
    stepa = stepb = 1;
    while (la)
    {
        while (lb!=la)
        {
            lb = lb->next;
            stepb++;
        }
        if (stepa != stepb)
            
break; stepa++; la = la->next; lb = L; stepb = 1; } if (la!=NULL) { *seq = stepb; return TRUE; } return FALSE; }

方法二:使用快慢指針若是有環那麽快指針會一直在環中循環,當慢指針進入環中節點後,一定會出現快指針在慢指針後面(或者相等)的情況,就可以判斷是否有環,不過這種方法不容易獲取到環路節點位置,時間復雜度按照慢指針來算,為O(n)

//快慢指針進行判斷
Status HasCircle02(List L) { List high, low; high = low = L; while (low&&high&&high->next) { if (high->next) high = high->next->next; low = low++; if (high == low) return OK; } return FALSE; }

方法三:判斷地址的大小

1.棧的地址是由高向低增長的.
2.堆得地址增長方向是由低到高向上增長的

我們創建鏈表時,一般是使用堆區進行,所以一般機器都是地址向上增長,若是有環,則地址會減小,我們可以使用一個指針,或者一個快指針,將每次的結點地址比較,這樣時間復雜度為O(n/2),若是環足夠大,我們設置的指針增長步長夠大,也會優化更多。

不過有限制,就是我們創建的鏈表需要地址增長是單向的,就是只能使用尾插法或者頭插法,不能使用中間插入或者聯合使用

//地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法
Status HasCircle03(List L)
{
    List high=L;
    int MaxAddr = 0;
    while (high&&high->next)
    {
        if (high->next)
        {
            high = high->next->next;
            if (MaxAddr < high)
                MaxAddr = high;
            else
                break;
        }
    }
    if (high&&high->next)
        return TRUE;
    return FALSE;
}
技術分享圖片
//判斷堆增長方向
int StackGrow()
{
    int *a,*b;
    int flag;
    a = (int *)malloc(sizeof(int));
    b = (int *)malloc(sizeof(int));
    if (a > b)
        flag = 0;
    else
        flag = 1;
    free(a);
    free(b);
    return flag;
}
使用一個小例子來判斷堆的增長方向

其他方法還需要再繼續回顧知識後才有思路.....

全部實現代碼

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int ElemType;
typedef int Status;

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

typedef struct Node* List;

//創建一個列表,有無環,若是有環,將單鏈表和循環鏈表合並即可
Status InitList(List* L, int flag, int* sep);
//創建一個單鏈表
Status CreateList(List* L, int n);
//創建一個循環鏈表
Status CreateCList(List* L, int n);

//開始進行判斷是否有環
//兩層循環進行判斷
Status HasCircle01(List L,int *seq);
//快慢指針進行判斷
Status HasCircle02(List L);
//地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法
Status HasCircle03(List L);


//用來打印鏈表
void PrintList(List L,int flag, int seq);


int main()
{
    List L = NULL;
    int seq = 0;    //分割點
    int flag = 1;

    printf("create cList?(1/0):");
    scanf("%d", &flag);

    if (!InitList(&L, flag, &seq))    //現在L指向第一個結點
        return 0;
    PrintList(L,flag,seq);

    if (HasCircle01(L, &seq))
        printf("has Circle:%d\n", seq);
    else
        printf("no Circle\n");

    if (HasCircle02(L))
        printf("has Circle\n");
    else
        printf("no Circle\n");

    if (HasCircle03(L))
        printf("has Circle\n");
    else
        printf("no Circle\n");


    system("pause");
    return 0;
}


//創建一個列表,有無環,若是有環,將單鏈表和循環鏈表合並即可,這裏單鏈表和循環鏈表都沒有頭結點
Status InitList(List* L, int flag,int* sep)
{
    int n;
    List SL,CL;
    List q;
    
    srand(time(0));
    
    printf("please enter the length of list:");
    scanf("%d", &n);
    *sep = n;
    if (!CreateList(&SL, n))    //鏈表創建失敗,直接退出
        return ERROR;
    if (flag)    //創建一個有環鏈表
    {
        printf("please enter the length of Clist:");
        scanf("%d", &n);
        if (!CreateCList(&CL, n)) //CL是循環鏈表頭指針
            return ERROR;
        q = SL;
        for (n = 1; n < *sep; n++)
            q = q->next;    //直接指向單鏈表的末尾,下面開始合並
        q->next = CL;
    }
    *L = SL;
    return OK;
}
//創建一個單鏈表
Status CreateList(List* L, int n)
{
    int i;
    List q,p;
    if (n < 1)
        return ERROR;

    *L = (List)malloc(sizeof(Node));
    (*L)->data = rand() % 100;
    q = *L;

    for (i = 1; i < n; i++)
    {
        p = (List)malloc(sizeof(Node));
        p->data = rand() % 100;
        q->next = p;
        q = p;
    }
    q->next = NULL;
    return OK;
}

//創建一個循環鏈表
Status CreateCList(List* L, int n)
{
    List q,p;    
    ElemType item;

    if (n < 1)
        return ERROR;

    *L = (List)malloc(sizeof(Node));
    if (*L == NULL)
        return ERROR;

    (*L)->data = rand() % 100;
    p = *L;
    p->next = p;    //形成回環

    for (int i = 1; i < n;i++)
    {
        //生成新的節點,根據尾指針添加節點,並實時更新尾指針。註意這裏數據插入是尾插法
        q = (List)malloc(sizeof(Node));
        q->data = rand()%100;
        q->next = p->next;
        p->next = q;
        p = q;
    }
    return OK;
}


//兩層循環進行判斷
Status HasCircle01(List L,int *seq)
{
    List la, lb;
    int stepa, stepb;
    la = lb = L;    //la在外層循環,lb在內層循環
    stepa = stepb = 1;
    while (la)
    {
        while (lb!=la)
        {
            lb = lb->next;
            stepb++;
        }
        if (stepa != stepb)
            break;
        stepa++;
        la = la->next;
        lb = L;
        stepb = 1;
    }
    if (la!=NULL)
    {
        *seq = stepb;
        return TRUE;
    }
    return FALSE;
}

//快慢指針進行判斷
Status HasCircle02(List L)
{
    List high, low;
    high = low = L;
    while (low&&high&&high->next)
    {
        if (high->next)
            high = high->next->next;
        low = low++;
        if (high == low)
            return OK;
    }
    return FALSE;
}

//地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法
Status HasCircle03(List L)
{
    List high=L;
    int MaxAddr = 0;
    while (high&&high->next)
    {
        if (high->next)
        {
            high = high->next->next;
            if (MaxAddr < high)
                MaxAddr = high;
            else
                break;
        }
    }
    if (high&&high->next)
        return TRUE;
    return FALSE;
}


//用來打印鏈表
void PrintList(List L, int flag, int seq)
{
    List CHead;
    List q = L;    //獲取頭指針
    int i;

    if (!flag)
    {
        while (q)
        {
            printf("%d ", q->data);
            q = q->next;
        }
    }
    else
    {
        for (i = 1; i <= seq; i++)
        {
            printf("%d ", q->data);
            q = q->next;
        }
        //for循環退出就進入了循環鏈表範圍內
        printf("-|- ");
        CHead = q;
        while (q->next != CHead)
        {
            printf("%d ", q->data);
            q = q->next;
        }
        printf("%d", q->data);
    }
    printf("\n");
}

測試結果

技術分享圖片技術分享圖片技術分享圖片

數據結構(一)線性表循環鏈表相關補充