之前一集所讨论的是顺序表,这一集主要讨论的是链表(Linked List)
什么是链表?
链表(Linked List) 事实上就是一系列结构体连接而成的列表
每个结构体代表一个节点, 一个结构体可能包括至少:
- 普通数据类型的数据变量 (例子: int data)
- 一个可以指向下一个结构体的指针(A pointer that points to next structure)
链表还分为两种: 单链表(Single Linked List)和双链表(Doubly Linked List)
先来看一下如何定义一个单链表的结构体
- 这一段其实非常简单,就是一个最基本的结构体定义,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)
};
这以上就是在下对链表的一些理解的描述,如果代码或在下的描述有什么问题,欢迎指出一起讨论,谢谢!!