zoukankan      html  css  js  c++  java
  • 数据结构(一)线性表循环链表相关补充

    (一)合并两个循环链表

        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
    ", seq);
        else
            printf("no Circle
    ");
    
        if (HasCircle02(L))
            printf("has Circle
    ");
        else
            printf("no Circle
    ");
    
        if (HasCircle03(L))
            printf("has Circle
    ");
        else
            printf("no Circle
    ");
    
    
        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("
    ");
    }

    测试结果

    (三)魔术师发牌问题

    一共13张黑牌1-13,预先排好顺序,牌面朝下,开始数数,
    数1翻开第一张牌为1取出,
    数1,2,将喊到1的牌放在末尾,喊到2的牌翻开为2取出
    喊1,2,3,将喊到1和2的牌放到末尾,将喊到3的牌翻开取出
    ......
    喊到13,直到将所有牌取出。
    正好翻开顺序为1-2-....-13
    我们需要找到他排列的预先顺序
    #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* CLinkList;
    
    //初始化链表
    Status InitList(CLinkList* L, int CardCount);
    
    //获取牌的顺序
    Status Magician(CLinkList* L, int CardCount);
    
    //用来打印链表
    void PrintList(CLinkList rear);
    
    
    int main()
    {
        CLinkList L = NULL;
        CLinkList p;
        ElemType e;
        int CardCount = 13;
    
        InitList(&L, CardCount);        //初始化13个节点,无头结点
        Magician(&L, CardCount);     //获取牌的顺序
        PrintList(L);           //开始打印
    
        system("pause");
        return 0;
    }
    
    
    //初始化带有头结点的链表,初始化牌数的结点,数据域都设为0
    Status InitList(CLinkList* L, int CardCount)
    {
        CLinkList rear, q;    //rear是尾结点
        rear = q = NULL;
    
        for (int i = 0; i < CardCount; i++)
        {
            if (*L == NULL)
            {
                *L = (CLinkList)malloc(sizeof(Node));
                if (!(*L))
                    return ERROR;
                (*L)->data = 0;
                (*L)->next = *L;    //自己指向自己
                rear = *L;    //设置尾指针位置
            }
            else
            {
                //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法
                q = (CLinkList)malloc(sizeof(Node));
                q->data = 0;
                q->next = rear->next;
                rear->next = q;
                rear = q;
            }
        }
        return OK;
    }
    
    //获取牌的顺序 Status Magician(CLinkList
    * L, int CardCount) { CLinkList start = *L; int CardNumber = 1; int i; if (CardCount < 1 || (*L) == NULL) return ERROR; while (1) { //无论是第一次还是后面,我们现在指针指向的当前牌都是数值为0的牌,我们只需要再向下找CardNumber-1张就可以找到下一张赋值的牌。 for (i = 1; i <= CardNumber-1; i++) { //一直找到下一张空闲的位置,就是完成一次for循环 start = start->next; while (start->data != 0) start = start->next; } start->data = CardNumber; CardNumber++; if (CardNumber > CardCount) //注意这个判断要在while前面,不然会造成死循环 break; //当上面一张牌被赋值后,我们就将指针指向下一张空闲的牌,即为第一张, start = start->next; while (start->data!=0) start = start->next; } return OK; } //用来打印链表 void PrintList(CLinkList L) { CLinkList q = L; //获取头指针 while (q->next != L) { printf("黑桃%d ", q->data); q = q->next; } printf("%d ", q->data); }

    (四)拉丁方阵

    nXn方阵,方阵中恰有n中不同的元素,例如1-n,现在希望每行每列中元素不重复。打印出来

    思路:一个循环链表1-n,从开始结点读一圈后,将结点移动下一个再读取一圈,直到将结点移动到最后就结束打印
    #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* CLinkList;
    
    //初始化链表
    Status InitList(CLinkList* L, int number);
    
    //用来打印链表
    void PrintList(CLinkList rear);
    
    
    int main()
    {
        CLinkList L = NULL;
        CLinkList p;
        int number = 5;
    
        InitList(&L, number);        //现在L指向第一个结点
        p = L;
        //开始打印每一行数据
        for (int i = 0; i < number;i++)
        {
            PrintList(p);
            p = p->next;
        }
        
    
        system("pause");
        return 0;
    }
    
    
    //初始化带有头结点的链表,初始化牌数的结点,数据域都设为0
    Status InitList(CLinkList* L, int number)
    {
        CLinkList rear, q;    //rear是尾结点
        rear = q = NULL;
    
        for (int i = 1; i <= number; i++)
        {
            if (*L == NULL)
            {
                *L = (CLinkList)malloc(sizeof(Node));
                if (!(*L))
                    return ERROR;
                (*L)->data = i;
                (*L)->next = *L;    //自己指向自己
                rear = *L;    //设置尾指针位置
            }
            else
            {
                //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法
                q = (CLinkList)malloc(sizeof(Node));
                q->data = i;
                q->next = rear->next;
                rear->next = q;
                rear = q;
            }
        }
        return OK;
    }
    
    //用来打印链表
    void PrintList(CLinkList L)
    {
        CLinkList q = L;    //获取头指针
        while (q->next != L)
        {
            printf("%d ", q->data);
            q = q->next;
        }
        printf("%d
    ", q->data);
    }

  • 相关阅读:
    Linux学习-网络管理
    Linux学习-文件权限
    Linux学习-用户管理常用命令
    python操作数据库(MySQL、redis、MD5加密函数)
    Jenkins待过的坑
    Robot framework+Jenkins
    jenkins + maven + SVN自动化集成部署
    接口测试学习笔记二
    接口测试学习笔记一
    【数据结构】为什么要使用一致性哈希算法?
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9427702.html
Copyright © 2011-2022 走看看