链表笔记

有头链表(注意:头结点有的地方是全局变量)

初学者学自于大话数据结构,不足及错误的地方请读者提出来,谢谢。

可加 qq1366963396交流。

一.创造链表

1.构建链表节点

struct Node

{

     int data;//数据域

     Node* next;//指针域

}

2.创建链表节点

Node* creatNode(int data)

{

    Node* new=(Node*)malloc(sizeof(Node));//申请内存

    new->data=data;

    new->next=NULL;

    return new;//new可能会冲突,换个变量也可以

}

3.整表创建(头插法)

void creatListHead(Node* head1,int n)

{

    head1->next = NULL;

    srand(time(NULL));

    for (int i = 0; i < n; i++)

    {

        Node* p = (Node*)malloc(sizeof(Node));

        p->data = rand() % 100 + 1;

        p->next = head1->next;

        head1->next = p;

    }

}

4.创建链表(尾插法)

void creatListHeatTail(Node* head2, int n)

{

    head2->next = NULL;

    Node* r = head2;//记录尾部节点

    srand(time(NULL));

    for (int i = 0; i < n; i++)

    {

        Node* p = (Node*)malloc(sizeof(Node));

        p->data=rand() % 100 + 1;

        r->next = p;

        r = p;

    }

    r->next = NULL;

}


二.单链表的读取

Node* GetItem(int i)//获取第i个节点,遍历

{

    Node* p=head.next;

    int j=1;//计数

    while(p&&j<=i)

    {

        if(j==i)

            return p;

        p=p->next;

         j++;

      }

     return NULL;

}


三,打印链表数据

void print()//遍历打印

{

    Node* p=head.next;

    while(p)

    {

        printf("%d ","p->data");

        p=p->next;

    }

}


四.插入

1.头插法

void add(Node* new)

{

    new->next=head.next;//原来头节点指向的节点现在由新节点指向

    head.next=new;头结点指向新节点

    //不可以交换,会使得head.next被new覆盖掉,等价于new->next=new

}

2.插入到任意节点前面


bool NodeInsert(int i,Node* newNode)

{

    int j=1;

    Node* p=head.next;

    if(i==j)//若插入到头结点后

    add(newNode);//调用头插法

    else

    {

        while(p&&j<i)//找到前驱结点

        {

            if(j==i-1)

                break;

            p=p->next;

            j++;

        }

        if(!p||j>i)

        return false;

        newNode->next=p->next;//插入

        p->next=newNode;

}

优化:

void add(Node* newNode,int i)

{

    Node* p =&head;

    int j = 1;

    while (p&&j<i)

    {

        ++j;

        p = p->next;

    }

    newNode->next = p->next;

    p->next = newNode;

}

3.尾插法

void tailAdd(Node* newNode)

{

    Node* p =&head;//

    while (p->next)//判断指针是否为空,也就是判断是否是最后一个节点

    {

        p = p->next;

    }

    p->next = newNode;

    newNode->next = NULL;

}


五.删除

1.删除第i个节点

void deleteNode(int i)

{

    Node* p = &head;

    int j = 1;

    while (p&&j<i)//找到删除的前一个节点p

    {

        ++j;

        p = p->next;

    }

    p->next = p->next->next;

}

2.整表删除

void deleteList(Node* head)

{

    Node* p = head->next;

    Node* q;

    while (p)

    {

        q = p->next;//下一个节点

        free(p);//释放当前节点

        p = q;移到下一个节点

    }

}


静态链表(数组描述的链表):

让数组的元素都是由两个数据域组成,data和cur,数组的每个元素都对应一个data和一个cur。数据域data,用来存放数据元素,也就是通常我们要处理的数据;而cur相当于单链表中的next指针,存放该元素的后继在数组中的下标,把cur叫做游标

对数组第一个和最后一个元素做特殊处理,不存数据。通常把未被使用的数组元素称为备用链表。

//数组第一个元素的cur用来存放备用链表第一个节点的下标

//数组最后一个元素的cur用来存放第一个插入元素的下标,相当于头结点

初始化状态,数据域为空


struct StaticList

{

    int data;//数据

    int cur;//游标

};

StaticList staticList[MAXSIZE];

初始化状态代码

void InitList(StaticList staticList[MAXSIZE])

{

    int i;

    for (i = 0; i < MAXSIZE - 1; i++)

    staticList[i].cur = i + 1;

    staticList[MAXSIZE - 1].cur = 0;

}


例:圆圈代表数据域

下标为0的存放备用链表的第一个节点的下标,也就是未被使用的下表为4的数组元素;

下表为3的cur为0,因为下一位置数据为空

下标为5也就是最后一个元素的cur存第一个有值元素的下标,也就是1

一.静态链表插入

下表为4原来未被使用,先把新数据放在这里,其cur为5。要把新数据插在第2个节点前面,先把第2个节点之前的节点的cur赋值为4,再把插入的节点的cur赋值为2,这样就实现了不移动元素,却实现了插入数据的操作。

//若备用链表非空,则返回分配的节点下表,否则返回0

int Malloc_SLL(StaticList staticList[MAXSIZE])

{

    int i = staticList[0].cur;//当前第一个元素的cur就是要返回的第一个备用空闲的下标

    if (staticList[0].cur)

        staticList[0].cur = staticList[i].cur;//由于要拿出一个分量来使用了,所以要把它的                                                                //下一个分量用来做备用

    return i;

}

void ListInsert(StaticList staticList[MAXSIZE], int i, int e)

{

    int j, k, l;

    k = MAXSIZE - 1;//最后一个元素的下标

    j = Malloc_SLL(staticList);//获取空闲分量的下标

    if (j)

    {

        staticList[j].data = e;//将数据赋值给空闲分量

        for (l = 1; l < i; l++)//找到第i个元素之前的下标

        {

            k = staticList[k].cur;

        }

    staticList[j].cur = staticList[k].cur;//把第i个元素之前的节点的cur赋给插入节点的cur

    staticList[k].cur =j;//把新节点的下标赋给第i个元素之前的cur

    }

}

二.静态链表删除(删除后数据还在,只是用户不需要这个数据,下次malloc时该节点可以用)

//将下标为k的空闲节点回收到备用链表

void Free_SSL(StaticList staticList[MAXSIZE], int k)

{

    staticList[k].cur = staticList[0].cur;//把第一个元素cur值赋给要删除的节点的cur

    staticList[0].cur = k;//删除的节点下标赋值给第一个下标的cur,下次malloc时优先考虑该节点

}

void ListDelete(StaticList staticList[MAXSIZE],int i)

{

    int j, k;

    k = MAXSIZE - 1;

    for (j = 1; j < i; j++)

    {

        k = staticList[k].cur;

    }

    j = staticList[k].cur;

    staticList[k].cur = staticList[j].cur;

    Free_SSL(staticList, j);

}

三.求长度

int ListLength(StaticList staticList[MAXSIZE])

{

    int j = 0;

    int i = staticList[MAXSIZE - 1].cur;

    while (i)

    {

        i = staticList[i].cur;

        j++;

    }

    return j;

}

静态链表其实是为了给没有指针的高级语言设计的一种实现单链表能力的方法。尽管不一定用得上,但这样的思考方式是非常巧妙的,应该理解其思想,以备不时之需。

单向循环链表

1.创建循环链表(带头节点)

node* CreatCycleList1(node* h,node* r)

{

    node* p= h;//h为普通带头节点链表

    while (p->next)

    {

        p = p->next;

    }

    r = p;//找到尾节点

    r->next = h;//指向头结点

    return r;

}

//打印单向循环链表

void PrintCycleList(node* rear)

{

    node* p = rear->next->next;//第一个节点

    if (rear == p)

    {

        printf("该链表是空链表\n");

    }

    while (p != rear->next)//当p不指向头结点

    {

        printf("%d ", p->data);

        p = p->next;

    }

}

将单链表终端节点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为但循环链表,简称循环链表。循环链表一定要头结点。

//与单链表差异:

循环的判断结束条件:原来是p->next是否为空,现在是其是否等于头结点。

优化:

不用头指针,用指向终端节点的尾指针,此时查找开始节点和终端节点都很方便,

若尾指针记为rear,则头结点为rear->next,第一个节点为rear->next->next。

要将两个循环链表合并成一个链表,有了尾指针就非常方便

如下:

循环链表A
循环链表B


合并

void MergeCycleList(node* rearA, node* rearB)

{

    node* p = (node*)malloc(sizeof(node));

    p= rearB->next;//p保存B链表头结点

    rearB->next = rearA->next;//rearB指向A的头结点

    rearA->next=p->next;//rearA指向B的第一个节点

    free(p);//释放p

}

例:约瑟夫环(后文有双向循环链表实现方法)

思路:

1.建立无头节点的单向循环链表

2.找到起始位置

3.找到要删除的节点并删除

4.移动指针到下一次起点

//部分代码

void solve(node* q, int m)//q是起始位置

{

    node* p = q;

    node* r;//r来保存删除节点的前一节点

    while (p->next != p)//剩一个节点

    {

        for (int i = 1; i <= m - 1; i++)

        {

            r = p;//前一节点

            p = p->next;

        }

        r->next = p->next;//删除系欸但

        printf("%d ", p->data);

        free(p);

        p = r->next;//取下一报数起点

    }

    printf("%d\n", p->data);

}

双向循环链表

双向循环链表

1.创建

//创建空链表

void CreatNULLList(dulNode* h)

{

    h->next=h;

    h->prior = h;

}

//添加节点(尾插法)

void addNode(dulNode* h,dulNode* newNode)

{

    dulNode* p = h;

    while (p->next!=h)//找到尾节点

    {

        p = p->next;

    }

    p->next = newNode;//尾节点指向新节点

    newNode->prior = p;

    newNode->next = h;

    h->prior = newNode;

}

2.添加节点(任意位置)

添加新节点

//获取节点(根据序号)

dulNode* GetNode(dulNode* h, int i)//返回第i个位置的节点

{

    int j = 0;

    dulNode* p = h;

    while (j < i&&p->next!=h)

    {

        p = p->next;

        j++;

    }

    return p;

}

void InsertNode(dulNode* h, dulNode* newNode,int i)

{

    dulNode* p = GetNode(h,i-1);//获取前一节点

    newNode->prior = p;//第一步

    newNode->next = p->next;

    p->next->prior = newNode;

    p->next = newNode;//第四步

}

3.删除节点(根据位置)

删除


void DeleteNode(dulNode* h,int i)

{

    dulNode* p = GetNode(h, i);//找到要删除的节点

    p->prior->next = p->next;

    p->next->prior = p->prior;

    free(p);

}

例:用双向循环链表实现约瑟夫问题(前文有单向循环链表实现方法)

void solve(node* q, int m)//q为起始位置

{

    node* p = q;

    node* r;

    while (p->next != p)

    {

        for (int i = 1; i <= m - 1; i++)

    {

    r = p;

    p = p->next;

}

r->next = p->next;

p->next->prior = r;//删除与单项循环链表有区别

printf("%d ", p->data);

free(p);

p = r->next;

}

printf("%d", p->data);

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容