数据结构(一)线性表循环链表相关补充

(一)合并两个循环链表

    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");
}

测试结果

原文地址:https://www.cnblogs.com/ssyfj/p/9427702.html

时间: 2024-10-25 04:27:35

数据结构(一)线性表循环链表相关补充的相关文章

数据结构:线性表之单链表

线性表(亦作顺序表)是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的.线性表有两种存储结构: ①顺序存储结构,即存储单元在一段连续的地址上存储,常见的数组就是顺序存储结构的线性表: ②链式存储结构,即存储单元在不连续的地址上存储.因为其不连续性,除了要存数据元素信息(数据域)外,还要存储它后继元素(结点)的地址(指针域,链).学习链式结构最好将结点结构牢记于心,如下图: 链表的每个结点只含有一个指

软考之路--数据结构之线性表

数据就是数值,也就是我们通过观察.实验或计算得出的结果.数据有很多种,最简单的就是数字.数据也可以是文字.图像.声音等.数据可以用于科学研究.设计.查证等.结构,组成整体的各部分的搭配和安排,两者完美结合在一起,我们这样需要重新认识她,对她重新审视与定义:数据结构是程序设计的重要理论和技术基础,她所讨论的内容和技术,对从事软件项目的开发有重要作用,通过学习数据结构,我们学会从问题出发,分析和研究计算机加工的数据的特性,以便为应用所设计的数据悬着适当的逻辑结构.存储结构及其相应的操作方法,为提高应

数据结构之线性表(顺序存储结构)

小学生放学都是要按顺序排队的,一个接一个,每个小学生的前后位置是固定的,这样便于迅速清点. 其实这就是一个线性表,从这件事里我们就可以找到很多关于线性表的特性,如 1.线性表是一个序列,它是有顺序的(排队) 2.第一个元素无前驱,最后一个无后继,其他每个元素都有一个前驱和后继(一个接一个) 3.元素是有限的(小学生的个数是有限的) 4.数据类型都相同(都是小学生在排队) 说明白线性表示什么,下面我们直接看线性表的实现 线性表的实现分顺序存储结构和链式存储结构 顺序存储结构: #define LI

自学数据结构——顺序线性表

胡乱写了一些代码 /* ============================================================================ Name : sqlist.c Author :codecup Version : Copyright : Your copyright notice Description : Hello World in C, Ansi-style ==========================================

[笔记]python数据结构之线性表:linkedlist链表,stack栈,queue队列

python数据结构之线性表 python内置了很多高级数据结构,list,dict,tuple,string,set等,在使用的时候十分舒心.但是,如果从一个初学者的角度利用python学习数据结构时,这些高级的数据结构可能给我们以迷惑. 比如,使用list实现queue的时候,入队操作append()时间复杂度可以认为是O(1),但是,出队操作pop(0)的时间复杂度就是O(n). 如果是想利用python学学数据结构的话,我觉得还是自己实现一遍基本的数据结构为好. 1.链表 在这里,我想使

自学数据结构——顺序线性表2

1 /* 2 ============================================================================ 3 Name : sqlist.c 4 Author : codecup 5 Version : 6 Copyright : Your copyright notice 7 Description : Hello World in C, Ansi-style 8 ==================================

数据结构:线性表插入一次删除一次的代码

#include <iostream> #include <cmath> #include <cstring> #include <algorithm> #include <stack> #include <queue> #include <cstdio> using namespace std; int insertsqlist(int weizhi,double charu,int *t,double b[]){   

数据结构_线性表_链式存储_双向循环链表的基本操作

//双向链表,将头结点和尾结点链接起来,就构成了双向循环链表 //双向循环链表是将头结点的前驱指针指向了尾结点,同时将尾结点的后劲指针指向了头结点. //空表,头结点的前驱和后继指针均指向了自己,这也是判断双向循环链表是否为空的条件, //双向循环链表具有对称性 //缺点,是要付出空间代价的 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. 代

Java数据结构之线性表(2)

从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的几张,我们将会分别讲解这几种数据结构,主要也是通过Java代码的方式来讲解相应的数据结构. 今天要讲解的是:Java线性结构 Java数据结构之线性结构 说到线性结构的话,我们可以根据其实现方式分为两类: 1)顺序结构的线性表 2)链式结构的线性表 3)栈和队列的线性表 对于1)和2)的讲解,请参考