C語言-線性錶鏈式存儲(循環鏈表)

理解循環鏈表

單鏈表和循環鏈表的區別在於,尾部結點的後繼指針是否指向頭部結點。相對於其他功能基本相似。

循環鏈表的插入分析

普通插入:例如pos = 2,和單鏈表是一樣的

                              

尾插法:輔助指針向後跳lenght次,指向鏈表最後元素,單鏈表支持尾插法

                              

頭插法:插入第一個元素時,當鏈表中有其他元素時插入頭部時

插入第一個元素時

pos = 0, 提供輔助指針current,當前輔助指針current指向頭部結點。

首先判斷current指針是否指向頭部結點,其次通過Get方法獲取到鏈表的最後一個結點(Null),最後讓最後一個結點的後繼指針指向插入的結點。

                              

當鏈表中有其他元素時插入頭部時

                              

插入結點的異常處理:

  • 如果是插入第一個結點需要特別注意

循環鏈表的刪除分析

普通刪除:假設刪除pos = 3/pos = length

pos = 3 

                              

pos = length 

                              

刪除頭部結點:

 

                              

刪除結點的異常處理:

  • 如果刪除頭部結點
  • 如果沒有任何結點
//CircleLinkList.h
#pragma once
typedef void CircleLinkList;
//鏈表結點(包含了後繼結點指針域)
typedef struct tag_CircleLinkListNode
{
	struct tag_CircleLinkListNode *next;
}TCircleLinkListNode;

//創建循環鏈表
CircleLinkList *CircleLinkList_Creat();
//銷燬鏈表
void CircleLinkList_Destroy(CircleLinkList*list);
//清空鏈表
void CircleLinkList_Clear(CircleLinkList *list);
//獲取鏈表長度
int CircleLinkList_GetLength(CircleLinkList *list);
//在指定位置插入結點
int CircleLinkList_inster(CircleLinkList *list, TCircleLinkListNode *node, int pos);
//獲取指定位置的結點
TCircleLinkListNode *CircleLinkList_Get(CircleLinkList *list, int pos);
//刪除指定位置的結點
TCircleLinkListNode *CircleLinkList_Delete(CircleLinkList *list, int pos);
//CircleLinkList.c
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CircleLinkList.h"

//循環鏈表結構
typedef struct tag_CircleLinkList
{
    TCircleLinkListNode header;//頭部結點
    TCircleLinkListNode *slider;//遊標
    int length;//鏈表長度
}TCircleLinkList;

//創建循環鏈表
CircleLinkList * CircleLinkList_Creat()
{
    TCircleLinkList *list = NULL;
    list = (TCircleLinkList *)malloc(sizeof(TCircleLinkList));//給鏈表分配堆內存
    if (list == NULL)
    {
        printf("LinkList_Creat err: list = NULL");
        return NULL;
    }
    memset(list, 0, sizeof(TCircleLinkList));//初始化堆內存
    list->length = 0;
    list->header.next = NULL;
    list->slider = NULL;
    return list;
}
//銷燬鏈表
void CircleLinkList_Destroy(CircleLinkList * list)
{
    if (list == NULL)
    {
        printf("LinkList_Destroy err: list = NULL");
        return;
    }
    free(list);//釋放堆內存空間
    printf("LinkList_Destroy finished \n");
    return;
}
//清空循環鏈表
void CircleLinkList_Clear(CircleLinkList * list)
{
    TCircleLinkList *temp = NULL;
    if (list == NULL)
    {
        printf("LinkList_Clear err: list = NULL");
        return;
    }
    temp = (TCircleLinkList *)list;//類型轉換
    temp->length = 0;
    temp->header.next = NULL;
    temp->slider = NULL;
    return;
}
//獲取鏈表長度
int CircleLinkList_GetLength(CircleLinkList * list)
{
    TCircleLinkList *temp = NULL;
    if (list == NULL)
    {
        printf("LinkList_GetLength err: list = NULL");
        return -1;
    }
    temp = (TCircleLinkList *)list;//類型轉換
    return temp->length;//返回鏈表長度
}
//在指定位置插入結點
int CircleLinkList_inster(CircleLinkList * list, TCircleLinkListNode * node, int pos)
{
    TCircleLinkList *temp = NULL;
    int i = 0;
    if (list == NULL || node == NULL || pos < 0)
    {
        printf("LinkList_inster err: list = NULL || node = NULL || pos < 0");
        return -1;
    }
    temp = (TCircleLinkList *)list;//類型轉換
    TCircleLinkListNode * current = &(temp->header);//輔助指針,指向頭部結點
    for (i = 0; i < pos && node->next != NULL; i++)//通過遍歷跳轉指向要插入位置的前一個結點處
    {
        current = current->next;
    }
    node->next = current->next;//讓插入結點的後繼指針指向current的後繼結點
    current->next = node;//讓current的後繼指針指向插入的結點

    if (temp->length == 0)//初始化遊標指向
    {
        temp->slider = node;//指向鏈表的第一個結點處
    }
    temp->length++;//長度+1
    //頭插法(異常處理)
    if (current == &temp->header)//如果插入第一個結點時判斷當前輔助指針current是否指向頭部結點
    {
        TCircleLinkListNode *last = CircleLinkList_Get(temp, temp->length - 1);//獲取鏈表的尾部結點
        last->next = current->next;//讓尾部結點的後繼指針指向輔助指針current的後繼結點處,完成循環鏈表指向
    }
    return 0;
}
//獲取指定位置的結點
TCircleLinkListNode * CircleLinkList_Get(CircleLinkList * list, int pos)
{
    TCircleLinkList *temp = NULL;
    int i = 0;
    if (list == NULL  || pos < 0)
    {
        printf("LinkList_Get err: list = NULL ||  pos < 0");
        return NULL;
    }
    temp = (TCircleLinkList *)list;//類型轉換
    TCircleLinkListNode * current = &(temp->header);//提供輔助指針,並指向頭部結點
    for (i = 0; i < pos ; i++)
    {
        current = current->next;
    }
    return current->next;//返回指定位置的結點
}
//刪除指定位置的結點
TCircleLinkListNode * CircleLinkList_Delete(CircleLinkList * list, int pos)
{
    TCircleLinkList *temp = NULL;
    int i = 0;
    if (list == NULL || pos < 0)
    {
        printf("LinkList_Get err: list = NULL ||  pos < 0");
        return NULL;
    }
    temp = (TCircleLinkList *)list;//類型轉換
    TCircleLinkListNode *current = &(temp->header);//提供輔助指針current指向頭部結點
    TCircleLinkListNode *ret = NULL;//提供輔助指針用來存放臨時結點
    TCircleLinkListNode *last = NULL;//提供輔助指針用來存放尾部結點
    for (i = 0; i < pos; i++)
    {
        current = current->next;
    }
    if (current == &(temp->header))//判斷輔助指針current是否指向頭部結點
    {
        //獲取到尾部結點
        last = CircleLinkList_Get(list, CircleLinkList_GetLength(list) - 1);
    }
    ret = current->next;//輔助指針ret臨時存放要刪除的結點
    current->next = ret->next;//讓當前current的後繼指針指向要刪除的結點的後繼節點處,完成刪除
    temp->length--;//鏈表長度-1
    //如果刪除頭部結點
    if (last != NULL)
    {
        //讓頭結點的後繼指針指向要刪除的的後繼結點
        temp->header.next = ret->next;
        //讓尾部結點的後繼指針指向要刪除結點的後繼結點處
        last->next = ret->next;
    }
    //如果刪除的結點正好是遊標所指處
    if (temp->slider == ret)
    {
        //遊標下移
        temp->slider = ret->next;
    }
    //刪除結點後鏈表長度爲0
    if (temp->length == 0)
    {
        temp->header.next = NULL;
        temp->slider = NULL;
    }
    return ret;//返回被刪除的結點
}
//重置遊標
TCircleLinkListNode * CircleLinkList_Reset(CircleLinkList * list)
{
    if (list == NULL)
    {
        printf("LinkList_Reset err: list = NULL");
        return NULL;
    }
    TCircleLinkList *temp = (TCircleLinkList *)list;//類型轉換
    temp->slider = temp->header.next;//讓遊標指向第一個結點處
    return temp->slider;//返回遊標
}
//獲取遊標當前指向的結點
TCircleLinkListNode * CircleLinkList_Current(CircleLinkList * list)
{
    if (list == NULL)
    {
        printf("LinkList_Current err: list = NULL");
        return NULL;
    }
    TCircleLinkList *temp = (TCircleLinkList *)list;//類型轉換
    return temp->slider;//返回當前遊標
}
//把當前位置返回,並且遊標下移
TCircleLinkListNode * CircleLinkList_Next(CircleLinkList * list)
{
    if (list == NULL)
    {
        printf("linkList_Next err: list = NULL");
        return NULL;
    }
    TCircleLinkList *temp = (TCircleLinkList *)list;//類型轉換
    if (temp->slider == NULL)
    {
        printf("linkList_Next err: temp->slider = NULL");
        return NULL;
    }
    TCircleLinkListNode *current = temp->slider;//提供輔助指針current指向當前遊標的位置
    temp->slider = current->next;//遊標下移
    return current;
}
//刪除指定結點
TCircleLinkListNode * CircleLinkList_DeleteNode(CircleLinkList * list, TCircleLinkListNode * node)
{
    if (list == NULL || node == NULL)
    {
        printf("LinkList_DeleteNode err: list = NULL || node = NULL");
        return NULL;
    }
    int i = 0;
    TCircleLinkList *temp = (TCircleLinkList *)list;//類型轉換
    TCircleLinkListNode *current = &temp->header;//提供輔助指針current指向頭部結點
    TCircleLinkListNode *ret = NULL;//提供輔助指針ret存放臨時結點
    for (i = 0; i < temp->length; i++)//通過遍歷跳轉
    {
        if (current->next == node)//如果輔助指針current的後繼指針等於指定刪除的結點
        {
            ret = current->next;//存放要被刪除的結點
            break;
        }
        current = current->next;
    }
    //結點不爲空時,作刪除操作
    if (ret != NULL)
    {
        CircleLinkList_Delete(temp, i);
    }
    return ret;//返回被刪除的結點
}
//測試代碼
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CircleLinkList.h"

typedef struct tag_Teacher    
{
    TCircleLinkListNode node;
    int age;
    char *name;
}Teacher;

int main(int argc, char** argv)
{
    Teacher t1, t2, t3, t4, t5;
    t1.age = 20;
    t1.name = "zhangsan";
    t2.age = 21;
    t2.name = "lisi";
    t3.age = 22;
    t3.name = "wangwu";
    t4.age = 23;
    t4.name = "zhaoliu";
    t5.age = 24;
    t5.name = "wangqi";

    CircleLinkList *list = NULL;
    list = CircleLinkList_Creat();
    if (list == NULL)
    {
        printf("LinkList_Creat err: list = NULL");
        return -1;
    }
    int i, ret = 0;

    ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t1, CircleLinkList_GetLength(list));
    ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t2, CircleLinkList_GetLength(list));
    ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t3, CircleLinkList_GetLength(list));
    ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t4, CircleLinkList_GetLength(list));
    ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t5, CircleLinkList_GetLength(list));

    printf("LinkList length = %d\n", CircleLinkList_GetLength(list));

    for (i = 0; i < CircleLinkList_GetLength(list); i++)
    {
        Teacher *t = (Teacher *)CircleLinkList_Get(list, i);
        printf("age = %d,name = %s\n", t->age, t->name);
    }

    system("pause");
    return 0;
}

遊標功能

通過在循環鏈表中增加遊標功能可以方便解決約瑟夫問題

1-8圍成一圈,從1開始報數報道3的人出列,然後再從下一個人開始從1報數,依次類推,求出出列順序。

//在CircleLinkList.h中添加一下方法
//重置遊標,指向鏈表的第一個結點處
TCircleLinkListNode *CircleLinkList_Reset(CircleLinkList *list);
//獲取當前遊標所指的結點
TCircleLinkListNode *CircleLinkList_Current(CircleLinkList *list);
//返回當前遊標指向的結點,並遊標下移
TCircleLinkListNode *CircleLinkList_Next(CircleLinkList *list);
//刪除指定結點
TCircleLinkListNode *CircleLinkList_DeleteNode(CircleLinkList *list, TCircleLinkListNode *node);
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "LinkList.h"

typedef struct MyValue
{
    TLinkListNode node;
    int val;
}TValue;

int main(int argc, char** argv)
{
    TValue v1, v2, v3, v4, v5, v6, v7, v8;
    v1.val = 1;
    v2.val = 2;
    v3.val = 3;
    v4.val = 4;
    v5.val = 5;
    v6.val = 6;
    v7.val = 7;
    v8.val = 8;

    LinkList *list = (LinkList *)LinkList_Creat();
    if (list == NULL)
    {
        printf("LinkList_Creat err: list = NULL");
        return -1;
    }
    int i, ret = 0;
    LinkList_inster(list, (TLinkListNode *)&v1, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v2, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v3, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v4, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v5, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v6, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v7, LinkList_GetLength(list));
    LinkList_inster(list, (TLinkListNode *)&v8, LinkList_GetLength(list));

    for ( i = 0; i < LinkList_GetLength(list); i++)
    {
        TValue *val = (TValue *)linkList_Next(list);
        printf("TValue val[%d] = %d\n", i + 1, val->val);
    }
    printf("約瑟夫問題求解\n");
    LinkList_Reset(list);
    while (LinkList_GetLength(list) > 0)
    {
        TValue *val = NULL;
        for (int i = 1; i < 3; i++)
        {
            linkList_Next(list);
        }
        val = LinkList_Current(list);
        printf("TValue val = %d\n", val->val);
        LinkList_DeleteNode(list, (TLinkListNode *)val);
    }

    LinkList_Destroy(list);
	
    system("pause");
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章