数据结构笔记2——链表

笔记中图片均来源课堂PPT

正在学习中,如果有错误欢迎指正!


一、链表的性质

链表也是线性表的一种,它与顺序表有着相似的性质,都是以线性的结构来储存数据,但是链表与顺序表不同之处在于:链表更借助于指针来储存元素。

详细来说,链表中的每个元素(习惯性地,我们把它叫做链点),都分为元素域和指针域,元素域用来存放元素的数据(比如int型的某个数据),而指针域用来指向下一个元素的位置。

 所以,通过链表中每个链点的指针域,我们可以轻松的达到想去的任何一个元素,不必拘泥于顺序表必须一个一个按照前后顺序读取元素的泥沼中,方便了许多(好耶)

了解了组成链表的每个基本元素,我们来看构成整个链表还需要些什么。首先,我们想到的是,作为一个线性表,链表也应该有头有尾,指向头元素和尾元素指向指针是需要我们考虑的;其次,有了元素之后,还需要链表的长度,不然计算机会很迷茫(究竟需要多少个元素呢?我要一直循环下去吗?)

综上所述,链表的基本构成如下:

head:链表的头结点

tail:链表的尾结点(补充一下:其实我们定义链表的时候基本不定义尾结点,具体定义方法后面会说)

length:链表的长度

有了这些,我们就可以定义链表了。

二、链表的基本操作

1.链表的定义

用typedef先定义一个链点,然后在定义一个链表,最后将它们产生关系,结合起来,就成为了一个完整的链表。

链点:

typedef struct link{
	int data;              //data指该链点的元素域
	struct link*next;      //next指该链点的指针域
}mylink ;

链表:

typedef struct list{
	link *head;
	link *tail;     //现在基本上大多数不定义尾指针
	int length;
}mylist;

那么现在来解释下不定义尾指针也可以结束链表的方法:

其实很简单,只需使下一个元素为空元素即可,也就是说,令p->next=null,这样不仅减少一个指针的储存位置,还能更方便的使用链点的指针域,无需特殊处理。

2.链表的建立

根据前面的定义,建立链表时,我们首先需要的是:链表的长度,链表的头指针,以及将链表头指针和下一个数据连接起来的操作。

先引入一个malloc函数,这个函数的作用是分配数据所需的内存空间,并返回一个指向它的指针。听着有一些抽象,举个例子,假如我们有十个苹果,十个小朋友,那么平时我们分配空间就好像一口气给每个小朋友一个苹果,一次性发完;而malloc则是一个一个小朋友发放,如果小朋友需要,就给他苹果,这就是所谓的动态分配内存。

在动态分配内存中,每需要一个位置就要申请一次。没分配内存就写入数据一定是不可取的!

在使用malloc函数时,要加入一个头文件<stdlib.h>

创建链表的代码如下:

void createlist(mylink* p, mylist* q)
{
    int  i;
    printf("How many numbers do you want to input:\n");
    scanf("%d", &q->length);
    q->head = (struct link*)malloc(sizeof(struct link));//创建头结点
    p = (struct link*)malloc(sizeof(struct link));//申请第一个结点
    q->head->next = p;//链接头结点和首结点
    printf("Please input numbers:\n");
    for (i = 0; i < q->length; i++)
    {
        scanf("%d", &p->num);
        p->next = (struct link*)malloc(sizeof(struct link));//申请下一个节点
        p = p->next;//将该节点和下一个节点连起来
    }
}

3.链表的遍历

遍历操作比较简单。在遍历中,需要注意的有:

·首先需要重置链表,将链表回到头结点,然后进行遍历

·在利用循环遍历链表的时候,要记得每次都用指针指向下一个元素

void printlist(mylink* p,mylist*q)
{
    int i;
    printf("these numbers are:\n");
    p = q->head->next;//由于第一个循环已经将链表移到末尾,所以这里要将链表移到首结点开始打印
    for (i = 0; i < q->length; i++)
    {
        printf("%d ", p->num);
        p = p->next;//指向下一个元素
    }
}

4.链表的插入

问题描述:在位置为location的元素前插入新元素

问题分析:输入应该有链表list、link,位置location,新元素elem

插入一个新的元素时,首先要定位元素,其次要覆盖元素。定位元素比较简单,因为输入了location,所以直接用循环遍历就可以到达所插入元素的位置。接下来就是覆盖元素:

利用链表指针域的特性,可以容易地插入元素。先将前一个元素的next指针指向新元素,再将新元素的next指针指向后一个元素,就完成了插入的动作。

 由于新元素也应该是链点,所以这里仍然需要定义一个链点以及链表,把新元素放到链表中,再插入到原来的链表之中,否则数据类型不统一,编译器会报错。

如果这个插入的元素要插到表首,因为location=1,它的前一个元素为list->head,所以不能用上述方法,应该单独列出这种情况,利用表首的next指针将此元素插进去。

 代码如下:

void insertlist(mylink* p, mylist* q,int location,mylink *elem,mylist*elemlist)
{
    int counter = 1;
    p = q->head;//重置头指针
    elem = elemlist->head->next;//插入表的第一个链点
    if (location<0 || location>q->length)
    {
        printf("the location is wrong");//报错提示,插入位置错误
        return;
    }
    if(location==1)//考虑边界条件:表首插入
    {
        elem = q->head->next;
        q->head->next = elem;
    }

    while (counter <= location - 1 && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem->next = p->next;//elem的指针指向p->next
    p->next = elem;//把元素放入p->next中
    q->length++;//长度加一

}

注意:当循环执行到表尾时,p 的值为NULL,p->next是悬空的值, 那么对于以下代码:

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

 就是错误的,原因在于当元素是表尾插入时,p->next为null,这样写会导致系统崩溃

 对插入过程总结一下,可以更好的理解链表的特性:

①链表操作往往从表头开始,然后逐个找到所需要的节点

②链表指针小心使用,谨防覆盖丢失

③不能访问指针域为null的成员

④链表操作的有向性,以至于不能回退

5.链表的链点删除

问题描述:删除位置为location的链点 ;

问题分析:输入应有链表list、link,删除位置location

和插入元素类似,在删除元素时,首先应该找到该元素的位置(同样用循环遍历就可达到),问题在于如何删除元素。对比插入元素改变指针方向来达到目的的方法,删除元素同样可以用此方法。略加思索之后,可以想到,如果将删除位置之前元素的指针域直接指向删除位置之后元素,那么删除位置的元素就被孤立了,最后直接再释放该元素就成了!

 很好,我们已经有了思路和想法,但是在敲代码之前,这里有一点需要注意:从链表上取下来的那个倒霉链点需要用一个临时指针保存起来,然后再释放,否则可能丢失。

void deletelist(mylink* p, mylist* q, int location,mylink*elem,mylist*elemlist)
{
    int counter = 1;
    p = q->head;
    if (location<0 || location>q->length)
    {
        printf("\nthe location is wrong\n");//删除位置错误
        return;
    }
    while (counter <= location - 1 && p != NULL)//找到该元素
    {
        p = p->next;
        counter++;
    }
    elem = p->next;
    p->next = p->next->next;//删除的核心语句
    free(elem);//释放elem的空间
    q->length--;
}

【思考】如果希望删除值为x的元素,如何实现?(思路见习题部分)

二、链表的练习题

1.1首先创建一个单链表:从键盘读入五个整数,按输入顺序形成单链表。将创建好的链表元素依次输出到屏幕上。

2在已创建好的链表中插入一个元素:从键盘读入元素值和插入位置,调用插入函数完成插入操作。然后将链表元素依次输出到屏幕上。

3在已创建好的链表中删除一个元素:从键盘读入欲删除的元素位置(序号),调用删除函数完成删除操作。然后将链表元素依次输出到屏幕上。

问题分析:该问题分为四个部分,分别为创建、输入链表,插入链点,删除链点,遍历链表。以上几个部分在之前的内容都有介绍过,现在只需要把它们集合在一起,并配上一个main函数即可。不多说了,代码如下:

#include <stdio.h>
#include <stdlib.h>

typedef struct Link
{
    int num;
    struct Link* next;
}mylink;
typedef struct list {
    Link* head;
    Link* tail;
    int length;
}mylist;

void createlist(mylink* p, mylist* q)
{
    int  i;
    q->length = 5;
    q->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    p = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    q->head->next = p;//链接头结点和首结点
    printf("Please input numbers:\n");
    for (i = 0; i < q->length; i++)
    {
        scanf("%d", &p->num);
        p->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        p = p->next;//将该节点和下一个节点连起来
    }
}
void printlist(mylink* p, mylist* q)
{
    int i;
    printf("these numbers are:\n");
    p = q->head->next;//由于第一个循环已经将链表移到末尾,所以这里要将链表移到首结点开始打印
    for (i = 0; i < q->length; i++)
    {
        printf("%d ", p->num);
        p = p->next;
    }
}
void insertlist(mylink* p, mylist* q, int location, mylink* elem, mylist* elemlist)
{
    int counter = 1;
    p = q->head;
    elem = elemlist->head->next;
    if (location<0 || location>q->length)
    {
        printf("the location is wrong");
        return;
    }
    while (counter <= location - 1 && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem->next = p->next;
    p->next = elem;
    q->length++;

}
void temp(mylink* elem, mylist* elemlist)
{
    printf("please input the elem\n");
    elemlist->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    elemlist->length = 1;
    elem = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    elemlist->head->next = elem;//链接头结点和首结点
    for (int i = 0; i < elemlist->length; i++)
    {
        scanf("%d", &elem->num);
        elem->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        elem = elem->next;//将该节点和下一个节点连起来
    }
    elem = elemlist->head->next;//重置
}
void deletelist(mylink* p, mylist* q, int location, mylink* elem, mylist* elemlist)
{
    int counter = 1;
    p = q->head;
    if (location<0 || location>q->length)
    {
        printf("\nthe location is wrong\n");
        return;
    }
    while (counter <= location - 1 && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem = p->next;
    p->next = p->next->next;
    free(elem);
    q->length--;
}
int main()
{
    mylist list;
    mylink link;
    createlist(&link, &list);
    printlist(&link, &list);
    printf("\nplease input the insert location\n");
    int t = 0;
    mylink elem;
    mylist elemlist;
    scanf("%d", &t);
    temp(&elem, &elemlist);
    insertlist(&link, &list, t, &elem, &elemlist);
    printlist(&link, &list);
    printf("\nplesae input the delete location\n");
    scanf("%d", &t);
    deletelist(&link, &list, t, &elem, &elemlist);
    printlist(&link, &list);
    return 0;
}

2.对一个元素有序(升序)排列的顺序表,任意给出一个数,要求插入到顺序表的正确位置,让插入后的顺序表也是有序(升序)排列的。该题中没有提供插入元素的具体位置,要根据元素值大小寻找合适的位置。

问题分析:对于该题,要首先找到插入元素的位置,然后直接调用insert函数,就可以简单的将元素插入进去。找元素的位置时,可以利用循环遍历,if语句判断元素的值来寻找元素的位置。

#include <stdio.h>
#include <stdlib.h>
#define maxnum 20

typedef struct Link
{
    int num;
    struct Link* next;
}mylink;
typedef struct list {
    Link* head;
    Link* tail;
    int length;
}mylist;

void createlist(mylink* p, mylist* q)
{
    printf("How many numbers do you want to input:\n");
    scanf("%d", &q->length);
    int  i;
    q->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    p = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    q->head->next = p;//链接头结点和首结点
    printf("Please input numbers:\n");
    for (i = 0; i < q->length; i++)
    {
        scanf("%d", &p->num);
        p->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        p = p->next;//将该节点和下一个节点连起来

    }
}

void printlist(mylink* p, mylist* q)
{
    int i;
    printf("these numbers in the list are:\n");
    p = q->head->next;//由于第一个循环已经将链表移到末尾,所以这里要将链表移到首结点开始打印
    if (q->length < 0)
    {
        printf("no data in the list");
        return;
    }
    for (i = 0; i < q->length; i++)
    {

        printf("%d ", p->num);
        p = p->next;

    }
}
int searchlist(mylink* p, mylist* q, int num)
{
    int location, i;
    i = 1;
    p = q->head;
    while (i <= q->length && p != NULL)
    {
        if (p->num <= num && p->num > num)
        {
            location = i;
            return location + 1;
        }
        i++;
    }
    return q->length;


}
void temp(mylink* elem, mylist* elemlist, int num)
{
    elemlist->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    elemlist->length = 1;
    elem = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    elemlist->head->next = elem;//链接头结点和首结点
    for (int i = 0; i < elemlist->length; i++)
    {
        elem->num = num;
        elem->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        elem = elem->next;//将该节点和下一个节点连起来
    }
    elem = elemlist->head->next;//重置
}

void insertlist(mylink* p, mylist* q, int location, mylink* elem, mylist* elemlist)
{
    location = q->length;
    int counter = 1;
    p = q->head;
    elem = elemlist->head->next;
    if (location<0 || location>q->length)
    {
        printf("the location is wrong");
        return;
    }
    while (counter <= location && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem->next = p->next;
    p->next = elem;
    q->length++;

}


int main()
{
    mylink link;
    mylist list;
    createlist(&link, &list);//要求输入的表是升序的
    printlist(&link, &list);
    int num, location;
    printf("\nplease input the searching number\n");
    scanf("%d", &num);
    location = searchlist(&link, &list, num);
    mylink elem;
    mylist elemlist;
    temp(&elem, &elemlist, num);
    insertlist(&link, &list, location, &elem, &elemlist);
    printlist(&link, &list);
}

3.1创建一个单链表,其数据元素为整数,从键盘输入,输入0结束(注意0不放到链表内);

   2)从键盘任意输入一个整数,在单链表中查询该数,如果单链表中已经存在这个数,就调用删除函数,删除该元素所在结点,并将单链表在删除前后的数据元素依次输出到屏幕上;

  如果单链表中不存在这个数,就调用插入函数,将这个数插入到单链表尾,并将单链表在插入前后的数据元素依次输出到屏幕上。

问题分析:首先是创建链表,和前一章的顺序表类似,在创建链表的时候利用if语句判断,如果元素为0,就停止输入。根据题意,之后要查找元素 ,和第二题用类似的方法即可,遍历循环寻找元素位置,并返回位置的值,如果元素不在链表中,就返回0,再用if语句判断接下来的操作。最后的插入和删除只需套入前面的模板就可以完成。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#define maxnum 20

typedef struct Link
{
    int num;
    struct Link* next;
}mylink;
typedef struct list {
    Link* head;
    int length;
}mylist;

void createlist(mylink* p, mylist* q)
{
    q->length = 0;
    int  i;
    int elem = 0;
    q->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    p = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    q->head->next = p;//链接头结点和首结点
    printf("Please input numbers:\n");
    for (i = 0; i < maxnum; i++)
    {
        scanf("%d", &elem);
        if (elem == 0)
        {
            p->num = NULL;
            break;//问题在于我已经申请空间了,但是不想打印这个位置,这个位置就会随机数,用null解决
        }
        p->num = elem;
        p->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        p = p->next;//将该节点和下一个节点连起来
        q->length++;
       
    }
}

void printlist(mylink* p, mylist* q)
{
    int i;
    printf("these numbers in the list are:\n");
    p = q->head->next;//由于第一个循环已经将链表移到末尾,所以这里要将链表移到首结点开始打印
    if (q->length < 0)
    {
        printf("no data in the list");
        return;
    }
    for (i = 0; i < maxnum; i++)
    {
        if (p->num == 0)
        {
            break;
        }
        printf("%d ", p->num);
        p = p->next;
        
    }
}
int searchlist(mylink* p, mylist* q, int num)
{
    int location, i;
    i = 1;
    p = q->head;
    while (i <= q->length && p != NULL)
    {
        if (num == p->num)
        {
            location = i;
            return location-1;
        }
        p = p->next;
        i++;
    }
    return 0;

}
void deletelist(mylink* p, mylist* q, int location, mylink* elem, mylist* elemlist)
{
    int counter = 1;
    p = q->head;
    if (location<0 || location>q->length)
    {
        printf("\nthe location is wrong\n");
        return;
    }
    while (counter <= location - 1 && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem = p->next;
    p->next = p->next->next;
    free(elem);
    q->length--;
}
void insertlist(mylink* p, mylist* q, int location, mylink* elem, mylist* elemlist)
{
    location = q->length;
    int counter = 1;
    p = q->head;
    elem = elemlist->head->next;
    if (location<0 || location>q->length)
    {
        printf("the location is wrong");
        return;
    }
    while (counter <= location  && p != NULL)
    {
        p = p->next;
        counter++;
    }
    elem->next = p->next;
    p->next = elem;
    q->length++;

}
void temp(mylink* elem, mylist* elemlist,int num)
{
    
    elemlist->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    elemlist->length = 1;
    elem = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    elemlist->head->next = elem;//链接头结点和首结点
    elem->num = num;
    elem->next = NULL;//申请下一个节点
    elem = elemlist->head->next;//重置
}


int main()
{
    mylink link;
    mylist list;
    createlist(&link, &list);
    printlist(&link, &list);
    int num,location;
    printf("\nplease input the searching number\n");
    scanf("%d", &num);
    mylink elem;
    mylist elemlist;
    temp(&elem, &elemlist,num);
    location = searchlist(&link, &list, num);
    if (location != 0)
    {
        deletelist(&link, &list, location, &elem, &elemlist);
    }
    else
    {
        insertlist(&link, &list,location, &elem, &elemlist);
        
    }
    printlist(&link, &list);
}

4.找出链表中所有的负数并删除它们

问题分析:题中说所有的负数,在负数不止一个的情况下,需要每找到一个负数,就返回一个location值并调用一次delete函数。所以需要创建一个search函数,完成找负数->返回location值,再调用delete函数完成任务。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#define maxnum 30

typedef struct Link
{
    int num;
    struct Link* next;
}mylink;
typedef struct list {
    Link* head;
    Link* tail;
    int length;
}mylist;

void createlist(mylink* p, mylist* q)
{
    printf("How many numbers do you want to input:\n");
    scanf("%d", &q->length);
    int  i;
    q->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    p = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    q->head->next = p;//链接头结点和首结点
    printf("Please input numbers:\n");
    for (i = 0; i < q->length; i++)
    {
        scanf("%d", &p->num);
        p->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        p = p->next;//将该节点和下一个节点连起来 
    }
}

void printlist(mylink* p, mylist* q)
{
    int i;
    printf("these numbers in the list are:\n");
    p = q->head->next;//由于第一个循环已经将链表移到末尾,所以这里要将链表移到首结点开始打印
    if (q->length < 0)
    {
        printf("no data in the list");
        return;
    }
    for (i = 0; i < q->length; i++)
    {

        printf("%d ", p->num);
        p = p->next;

    }
}

void temp(mylink* elem, mylist* elemlist)
{
    elemlist->head = (struct Link*)malloc(sizeof(struct Link));//创建头结点
    elemlist->length = 1;
    elem = (struct Link*)malloc(sizeof(struct Link));//申请第一个结点
    elemlist->head->next = elem;//链接头结点和首结点
    for (int i = 0; i < elemlist->length; i++)
    {
       // elem->num = num;
        elem->next = (struct Link*)malloc(sizeof(struct Link));//申请下一个节点
        elem = elem->next;//将该节点和下一个节点连起来
    }
    elem = elemlist->head->next;//重置
}

void deletelist(mylink* p, mylist* q,mylink* elem, mylist* elemlist)
{

    int counter = 1;
    p = q->head->next;
    for (counter = 1; counter <= q->length; counter++)
    {
        if (p->next->num < 0 && p->next != NULL)
        {
            elem = p->next;
            p->next = p->next->next;
            free(elem);
            q->length--;
        }
        else
        {
            if (p->next == NULL)
                return;
            p = p->next;
        }
       
    }

}


int main()
{
    mylink link;
    mylist list;
    createlist(&link, &list);
    mylink elem;
    mylist elemlist;
    temp(&elem, &elemlist);
    deletelist(&link, &list, &elem, &elemlist);
    printf("删除负数后,表为:");
    printlist(&link, &list);
}

(这些代码中location=1的情况我好像没有写,这里要注意一下)

5.思考题

有了前面的铺垫,思考题也就变得简单了,我们只需要首先定位元素的位置,然后进行删除操作即可。定位根据循环遍历并返回location值,再调用delete函数,就可以完成任务。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值