数据结构(Data Structure)(第三集)(链表(Linked List))

之前一集所讨论的是顺序表,这一集主要讨论的是链表(Linked List)

什么是链表?

链表(Linked List) 事实上就是一系列结构体连接而成的列表

每个结构体代表一个节点, 一个结构体可能包括至少:

  1. 普通数据类型的数据变量 (例子: int data)
  2. 一个可以指向下一个结构体的指针(A pointer that points to next structure)

链表还分为两种: 单链表(Single Linked List)和双链表(Doubly Linked List)

先来看一下如何定义一个单链表的结构体

  1. 这一段其实非常简单,就是一个最基本的结构体定义,PtrToNode是一个指针被定义来之后用于建立节点,Linklist的指针会被用来指向你所建立的一整个链表。
    其实定义方法有很多种,不一定是我这样的,你也可以选择其他的定义方式。 例如: 只有一个(typedef struct Node *PtrToNode), 然后在结构体里的next改成struct Node *next
    总之结构体的Next也一定要指针,因为你需要有一个指针变量来和下一个结构体的地址连接在一起
#include <stdio.h>
#include <stdlib.h>
typedef struct Node *PtrToNode; //用来指向一个结构体的指针
typedef PtrToNode LinkList;   //用来之后指向一整个链表的指针

//定义结构体(Define a structure)
struct Node {
    char data;    //一个节点的数据域 (Store a data)
    PtrToNode next;  //指向下一个结构体(pointer to next structure)
};

2.建立完结构体后,你需要有一个函数进行空链表的建立(Empty linked list)
这一段就是建立一个什么都没有的头节点,它的next指针指向空意味着这是一个空的链表

//create a link list
LinkList Create_ll() {
    //head pointer
    LinkList L = (LinkList)malloc(sizeof(struct Node));
    L->next = NULL;    
    return L;
}

·3. 我们可以建立方面日后检查改链表是否为空的函数
显而易见,如果上传的这个节点的next指针指向NULL,说明这是一个没有数据的空链表,结果会返回整数1

//check if list is empty
int isEmpty_ll(LinkList L) {
    return (L->next == NULL);   //when it is empty, return 1
}

4.这是一个对程序的初始化函数

比如下面这段函数其实在进行对链表的输入

首先会有一个新建立的结构体指针 *p, 然后输入字符串的长度Length

getchar()的作用是你需要按一个回车才会进入下一个输入字符环节

输入字符需要以一个循环为每一个字符创造节点, 期间需要为p分配空间

输入完字符后,将该节点的指针Next指向原本头节点指针next所指向的地址,再将头节点指针next指向p,这样就可以成功插入一个节点

Hints: 记得在输入每个字符后面需要输入一个空格或者回车,不然你输入的字符只会被记录在第一个节点里,而由于节点里的数据是字符(char),所以只会当作只有一个字符,这也会影响之后的结果和链表的建立

//initialization
int Initialize_ll(LinkList L) {
    char c;
    int length, i;
    PtrToNode p;   //存放新输入字符的节点

    //enter length
    printf("enter the length: ");
    scanf_s("%d", &length);
    getchar();

    //input the elements in linked list
    printf("please enter the element of linklist: ");
    for (i = 0; i < length; i++) {
        //为指针p分配空间
        p = (PtrToNode)malloc(sizeof(struct Node)); 
        scanf_s("%c", &p->data);   //输入一个字符在该节点
        p->next = L->next;
        L->next = p;
        getchar();
    }
    return length;
}

5.以下是两个函数分别是检查一个指针的位置是否是链表的最后一个,另一个是删除操作函数
这是两个相关联的节点。
第一个: 直接检测节点的指针Next是否指向空,如果是说明这是最后一个节点

删除操作的思路:
1. 首先想删除某一个节点,你需要先知道这个节点的前驱结点(Previous node),并上传
2. 然后定一个结构体指针 (temp) 等于这个前驱结点next指针所指向的下一个节点(这里其实就是等于你想删除的节点)
3. 再将想删除的节点的指针Next指向和temp的指针next指向的同一个位置,这是再将temp的空间释放相当于也将你要删除节点的地址空间释放

//check if the list is reaching at last one
int isLast(PtrToNode p, LinkList L) {
    return (p->next == NULL);
}


//delete the one node
void Delete_ll(PtrToNode position, LinkList L) {
    PtrToNode temp;
    if (!isLast(position, L)) {
        temp = position->next;
        position->next = temp->next;
        free(temp);
    }
}

6.查找操作
思路:
需要用一个循环当链表L没有指向空时和其数据不等于你要找的字符时, 对链表进行查找。
查找方法只是将链表等于链表的指针Next所指向的下一个节点。
如果找到停止循环,并返回这个结构体指针

//find the node
LinkList find_ll(LinkList L, char c) {
    PtrToNode p;
    p = L;
    while (p != NULL && p->data != c)  //start to search the element
        p = p->next;
    return p;

}

7.插入操作
思路:
1. 同样需要建立一个结构体指针(temp), 将temp的指针next指向你要插入节点的下一个节点
2. 再将那个要插入节点的指针next指向temp就ok了
插入操作

//insertion
void Insert_ll(LinkList L, char c, PtrToNode position) {
    PtrToNode temp;
    temp = (PtrToNode)malloc(sizeof(struct Node));
    if (temp == NULL) {
        printf("Out of space\n");
    }

    temp->data = c;   //将想要插入的字符放进temp
    temp->next = position->next;   //将temp的下一个指向Position的下一个
    position->next = temp;  //Position的下一个指向temp

}

以上全部是关于单链表的初始化,以及各种操作的建立。 现在提点题外话,如何将一连串字符里重复的字符删除。
其思路:
1. 建立两个结构体指针(p,q),将其指针p和q指向整个链表头指针的下一个节点
2. 运用循环令指针p和指针q之后的每一个节点进行比较(q->next),如果相等, 将指针q上传到删除操作函数(这时指针q是q->next的前驱结点), 并将指针q的下一个节点删除
3. 最后循环结束,直接返回链表(L)

仅供参考的代码

//find repeated word
LinkList findRepeatedWord(LinkList L) {  //delete the all repeated words
    PtrToNode p, q, t;
    p = L->next;

    while (p) {
        q = p;
        while (q->next) {
            if (q->next->data == p->data) {
                Delete_ll(q, L);
            }
            else {
                q = q->next;
            }
        }
        p = p->next;
    }

    return L;
}

下面对双链表(Doubly Linked List)进行小部分的讨论

双链表的定义

事实上,双链表和单链表的别在于: 在每个节点里多了一个连接的结构体指针,一个用指向之前节点,另一个指向下一个节点。
**在数据结构学习中期,会学关于树(Tree)的知识,二叉树(Binary Tree),平衡树(AVL)……等等之类的树可能会用到双链表的结构写代码

这是对双链表结构的定义

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

typedef struct Node *node_Ptr; //用来指向一个结构体的指针
//定义结构体(Define a structure)
typedef struct Node{
    char data;    //一个节点的数据域 (Store a data)
    struct Node *prior, *next;  //指向上一个和下一个结构体(pointer to next structure)
};

这以上就是在下对链表的一些理解的描述,如果代码或在下的描述有什么问题,欢迎指出一起讨论,谢谢!!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值