数据结构与算法之循环链表2020.0401

486 阅读5分钟
  • 循环单链表:链表的尾结点指针域指向头结点,在循环链表中我们称头结点为首元结点,如图所示

    其中L就是首元结点,当如果只有一个结点时,首元结点即是尾结点

  • 创建循环列表

    #pragma mark - 定义结点
    typedef struct Node{
        struct Node *next;
        ElemType data;
    }Node;
    typedef struct Node *ModelList;
    
    
    #pragma mark - 创建单链表
    /*
     需要注意的是:是否是初次创建,第一次创建,则结点的next指向自身,否则指向首元结点(原来的尾结点指向新的结点)
     */
    Status CreatModelList(ModelList *list){
        int item;
        ModelList temp = NULL, target = NULL;
        printf("输入结点的值,输入0时结束\n");
        while (1) {
            scanf("%d",&item);
            if (item == 0) {
                break;
            }
            if (*list == NULL) {//第一次创建
                *list = (ModelList)malloc(sizeof(Node));
                if (list == NULL) {
                    exit(ERROR);
                }
                //结点赋值
                (*list)->data = item;
                //结点next指向自身
                (*list)->next = *list;
            }else{
                //查找到尾结点
                for (target = *list; target->next != *list; target = target->next) {
                    //for循环什么也不干,只是为了找到尾结点
                }
                temp = (ModelList)malloc(sizeof(Node));
                temp->data = item;
                //新结点指向首元结点
                temp->next = *list;
                target->next = temp;
                
            }
        }
        return OK;
    }
    #pragma mark - 从开始记录尾结点的方式创建循环单链表
    Status CreatModelList2(ModelList *list){
        int item;
        //target是尾结点
        ModelList temp = NULL, target = NULL;
        printf("输入结点的值,输入0时结束\n");
        while (1) {
            scanf("%d",&item);
            if (item == 0) {
                break;
            }
            if (*list == NULL) {//第一次创建
                *list = (ModelList)malloc(sizeof(Node));
                if (list == NULL) {
                    exit(ERROR);
                }
                //结点赋值
                (*list)->data = item;
                //结点next指向自身
                (*list)->next = *list;
                target = *list;
            }else{
                temp = (ModelList)malloc(sizeof(Node));
                if (!temp) {
                    exit(ERROR);
                }
                temp->data = item;
                //将新结点指向首元结点
                temp->next = *list;
                //将尾结点的next指向新结点
                target->next = temp;
                //将新结点赋值给尾结点,即新结点变成尾结点
                target = temp;
            }
        }
        return OK;
    }
    
    
    
    
  • 遍历循环列表

    #pragma mark - 遍历循环链表
    Status TraversalList(ModelList list){
        if (!list) {
            return ERROR;
        }
        printf("链表数据是:");
        ModelList temp;
        temp = list;
        do{
            printf("%5d",temp->data);
            temp = temp->next;
        }while (temp != list);
        printf("\n");
        return OK;
    }
    
    
    

  • 循环链表插入


    #pragma mark - 循环链表插入
    Status InsertList(ModelList *list, int place, ElemType type) {
        if (!list) {
            return ERROR;
        }
        ModelList temp,target;
        int i;
        temp = (ModelList)malloc(sizeof(Node));
        if (!temp) {
            return ERROR;
        }
        temp->data = type;
        if (place == 1) {//插入的是第一个,此时需要找到尾结点,尾结点的next->新的结点,新结点的next指向首元结点,
            for (target = *list; target->next != *list; target = target->next) {
                //找到尾结点target
            }
            //新结点指向原来的首元结点
            temp->next = *list;
            //尾结点指向新的结点
            target->next = temp;
            //temp成为新的首元结点
            *list = temp;
        }else{
            //插入其他位置先找到插入的位置,i = 1是因为不是从第一个开始插入的
            //注:这里不需要判断插入的位置是否超过单链表的长度是因为还没有找到要插入的位置place时,链表就已经查询了一边,即找到了尾结点(target->next == *list了,跳出了for循环)
            for (i = 1, target = *list; target->next != *list && i != place - 1; target = target->next,i++) {
                //找到插入的前一个结点target
            }
            //新结点指向插入位置结点的后面结点
            temp->next = target->next;
            //前一个结点指向新的结点
            target->next = temp;
        }
        return OK;
    }
    
    
    

  • 循环链表删除:需要注意区分是否删除的是首元结点

    #pragma mark - 删除循环链表结点
    Status DeleteList(ModelList *list, int place){
        if (!list) {
            return ERROR;
        }
        ModelList temp,target;
        if (place == 1) {//删除的是第一个结点
            //如果只有一个结点
            if ((*list)->next == (*list)) {
                (*list)->next = NULL;
                return OK;
            }else{
                //找到尾结点target
                for (target = *list; target->next != *list; target = target->next) {
                    
                }
                temp = *list;
                //尾结点的next指向原来首元结点的next(第二个结点)
                target->next = temp->next;
                //新的首元结点变成原来的第二个结点
                *list = target->next;
                //释放原来的首元结点
                free(temp);
            }
        }else{//删除的不是第一个结点
            int i;
            //找到删除结点的前一个结点target
            for (i = 1,target = *list; target->next != *list && i != place - 1; target = target->next,i++) {
                
            }
            //找到要删除结点
            temp = target->next;
            //前一个结点的next指向删除结点的next
            target->next = temp->next;
            free(temp);
            
        }
        return OK;
    }
    
    
    

  • 约瑟夫杀人问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。

    #pragma mark - 约瑟夫杀人问题算法
    Status JosephKill(ModelList *list, int killNum){
        if (!list) {
            return ERROR;
        }
        if (killNum == 1) {//等于1的时候没有意义,因为只要数1就死,直到剩余一个人,这样按顺序排队就好了
            printf("按顺序排好队就好了,队伍最后一个人存活\n");
            return ERROR;
        }
        if ((*list)->next == *list) {//如果只有一个结点,这个肯定不需要继续查找留下的人了
            printf("留下的是:%4d",(*list)->data);
            return OK;
        }
        /*
         1、从首元结点开始计数(从1开始),当结点的计数等于killNum时,该结点free掉,并修改前一个结点next
         2、计数到killNum则free结点的next结点从1继续计数
         3、到达尾结点,例如killNum = 3,尾结点计数为1,则回到首元结点,首元结点为2,第二个结点为3,则第二个结点free,然后第三个结点计数为1,循环下去
         4、当结点只有一个的时候退出循环
         */
        //temp为删除的结点,target为删除结点的前一个结点
        ModelList temp,target;
        int num = 1;
        target = *list;
        while (target->next != target) {
            if (num == killNum - 1) {//找到删除结点的前一个结点
                //找到删除的结点
                temp = target->next;
                //前一个结点next指向删除结点的next
                target->next = temp->next;
                printf("被杀掉的是:%4d\n",temp->data);
                free(temp);
    
           //target的next指向下一个
                target = target->next;
                //num重置成1
                num = 1;
            }else{
                target = target->next;
                num++;
            }
        }
        //target活了下来,则next指向NULL
        target->next = NULL;
        printf("最终活下来的是%4d\n",target->data);
        return OK;
    }