数据结构(一)线性表循环链表

(一)定义

将单链表中终端结点的指针端有空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,检测循环链表

(二)优点

无论从链表的哪一个结点出发,都可以访问到所有的结点

(三)结构

对于单循环链表:
使用尾指针,可以在O(1)时间内找到终端结点和开始结点
使用头指针,我们只能在O(1)时间内找到开始结点。
所以选择尾指针的效率更好

带有头结点

不带头结点

(四)实现循环链表(使用尾指针,不带头结点)

#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);
Status ClearList(CLinkList* rear);
Status ListEmpty(CLinkList rear);
int ListLength(CLinkList rear);

//四个元素操作,插入,删除,两种查找
Status GetElem(CLinkList rear, int i, ElemType* e);
int LocateElem(CLinkList rear, ElemType e);
Status ListInsert(CLinkList* rear, int i, ElemType e);
Status ListDelete(CLinkList* rear, int i, ElemType* e);

//指定开始位置来打印数据
void PrintListByIndex(CLinkList rear,int index);
//用来打印链表
void PrintList(CLinkList rear);
//四个基本操作,初始,清空,判断是否为空,获取长度
//初始化不带头结点的链表
Status InitList(CLinkList* L)
{
    CLinkList rear,q;    //rear是尾结点
    ElemType item;
    rear = q = NULL;

    printf("please enter the value of node(enter 0 to finish Initialize)\n");
    while (1)
    {
        scanf("%d", &item);
        fflush(stdin);
        if (item == 0)
            break;

        if ((*L)==NULL)    //进行初始化
        {
            *L = (CLinkList)malloc(sizeof(Node));
            if (!(*L))
                return ERROR;
            (*L)->data = item;
            (*L)->next = *L;    //自己指向自己
            rear = *L;    //设置尾指针位置
        }
        else     //已初始完毕,进行数据插入
        {
            //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法
            q = (CLinkList)malloc(sizeof(Node));
            q->data = item;
            q->next = rear->next;
            rear->next = q;
            rear = q;
        }
    }
    *L = rear;    //更新指针位置,使原来指针指向链表尾部
    return OK;
}


//清空链表
Status ClearList(CLinkList* rear)
{
    CLinkList q, p;
    q = (*rear)->next;    //找到第一个结点
    (*rear)->next = NULL;
    while (q)
    {
        p = q;
        q = q->next;
        free(p);
    }
    return OK;
}

//判断链表是否为空,若是头指针和尾指针一样,为空
Status ListEmpty(CLinkList rear)
{
    if (rear->next == rear)    //头指针等于尾指针
        return TRUE;
    return FALSE;
}
// 
//获取列表长度
int ListLength(CLinkList rear)
{
    int length = 1;
    CLinkList head = rear->next;
    while (head != rear)
    {
        head = head->next;
        length++;
    }
    return length;
}

//四个元素操作,插入,删除,两种查找
//按照索引查找,获取元素
Status GetElem(CLinkList rear, int i, ElemType* e)
{
    int j=1;
    CLinkList q=rear;
    if (i<1 || i>ListLength(rear))
        return ERROR;
    for (; j <= i;j++)
    {
        q = q->next;
    }
    *e = q->data;
    return OK;
}

//按照元素进行查找,获取索引
int LocateElem(CLinkList rear, ElemType e)
{
    int j;
    CLinkList q = rear;
    for (j = 1; j <= ListLength(rear);j++)
    {
        q = q->next;
        if (q->data == e)
            break;
    }
    return j;
}

//按照索引进行插入数据
Status ListInsert(CLinkList* rear, int i, ElemType e)
{
    if (*rear == NULL && i > ListLength(*rear)+1)
        return ERROR;

    int j = 1;
    CLinkList q;
    CLinkList start = (*rear);    //若是想插入到第一个位置,必须从尾指针开始
    for (; j < i; j++)
        start = start->next;    //去获取插入的前一个位置

    //开始创建一个新的节点
    q = (CLinkList)malloc(sizeof(Node));
    q->data = e;
    q->next = start->next;
    start->next = q;

    return OK;
}

//进行元素删除
Status ListDelete(CLinkList* rear, int i, ElemType* e)
{
    if ((*rear) == NULL || i>ListLength(*rear))
        return ERROR;

    int j=1;
    CLinkList q, p;
    q = *rear;

    for (; j < i; j++)
        q = q->next;    //找到前一个元素
    p = q->next;    //p是我们要删除的那个元素
    q->next = p->next;
    *e = p->data;
    free(p);
    
    return OK;
}

//从指定位置开始打印数据(这个才是体现循环链表的特性)
void PrintListByIndex(CLinkList rear, int index)
{
    CLinkList start=rear;
    for (int i = 1; i <= index;i++)
        start = start->next;
    rear = start;
    while (start->next!=rear)
    {
        printf("%d ", start->data);
        start = start->next;
    }
    printf("%d\n", start->data);
}

//用来打印链表
void PrintList(CLinkList rear)    //L是尾指针
{
    CLinkList q = rear->next;    //获取头指针
    while (q!=rear)
    {
        printf("%d ", q->data);
        q = q->next;
    }
    printf("%d\n", rear->data);
}
int main()
{
    CLinkList rear,L=NULL;
    ElemType e;
    InitList(&L);    //初始化后,L变为尾指针
    rear = L;
    PrintList(rear);
    ListInsert(&rear, 3, 999);
    PrintList(rear);
    ListDelete(&rear, 3, &e);
    printf("delete:%d\n", e);
    GetElem(rear, 4, &e);
    printf("get element:%d\n", e);
    printf("get index:%d by 33\n", LocateElem(rear, 33));
    PrintListByIndex(rear, 5);
    system("pause");
    return 0;
}
注意:测试时需要长度超过5,输入的数里面含有33,以便查找,需要插入999这个数。具体自己修改,这里不在重复了

(五)优点

1.可以从任意位置访问所有节点
2.若是想上面使用尾指针构造循环链表,那么开始结点和终端结点的时间复杂度都是O(1)

 

posted @ 2018-08-05 13:52  山上有风景  阅读(921)  评论(0编辑  收藏  举报