1.链表基础知识
- 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据(想想数组,元素是按照顺序存储的,即存储区域连续)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。
- 链表结构分类:单向链表、双向链表、循环链表等等。有些链表头结点不存储信息,有些链表头节点存储信息。下面介绍最常用的单向链表和双向链表。
- 单向链表: 链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值(可以按照下图理解)。一般这么定义一个单向链表,next指针指向下一个节点。
/*数据*/
typedef struct {
//填入你需要的数据
int id;
} Data, *pData;
typedef struct Node {
Data data;
struct Node* next;
} LinkedList, *pLinkedList, Node, *pNode;
4. 双向链表: 每个节点有两个连接:一个指向前一个节点,(当此“连接”为第一个“连接”时,指向空值或者空列表);而另一个指向下一个节点,(当此“连接”为最后一个“连接”时,指向空值或者空列表),一般这么定义一个双向链表,prev指针指向上一个节点,next指向下一个节点。
/*数据*/
typedef struct {
//填入你需要的数据
int id;
} Data, *pData;
typedef struct Node {
Data data;
struct Node* prev;
struct Node* next;
} LinkedList, *pLinkedList, Node, *pNode;
2.链表相关操作(以头结点存储信息的单向链表为例)
- 增加一个节点: 分为在首部和尾部增加一个节点。一般代码如下:
//在头部添加节点 head是链表的头结点(头结点存储信息)data为待添加的数据
pLinkedList addNodeFirst(pLinkedList head, Data data) {
pNode newNode;
if (!(newNode = (pNode)malloc(sizeof(Node)))) {
printf("申请内存失败!\n");
return head;
}
newNode->data = data;
newNode->next = head;
head = newNode;
return head;
}
//尾部添加一个节点
pLinkedList addNodeEnd(pLinkedList head, Data data) {
pNode node;
pLinkedList h;
if(!(node=(pNode)malloc(sizeof(Node)))) {
printf("申请内存失败");
return head;
}
node->data = data;
node->next = NULL;
if(head == NULL) {
head = node;
return head;
}
/*头结点为空时注意 如果直接用下面的方法找最后一个节点时因为头结点也为空 所以head->next没有
这时候闪退 所以加了前面的判断*/
h = head;
while(h->next != NULL) {
h = h->next;
}
h->next = node;
return head;
}
- 查找数据: 简单起见,以id查找,对应代码如下。
/*找到第一个满足条件的节点(如果有多个满足条件的结点)*/
pNode findNode(pLinkedList head, int id) {
pLinkedList h = head;
while(h) {
if(h->data.id == id) {
return h;
} else {
h = h->next;
}
}
return NULL;
}
- 插入节点到指定位置: 以id为关键字为例,代码如下。
/*把data插到指定id的node后面, 可能会有多个重复的id 这里插入到第一个指定id的后面*/
pLinkedList insertNode(pLinkedList head, int id, Data data) {
pNode newNode, nFind;
if(!(newNode=(pNode)malloc(sizeof(Node)))) {
printf("申请内存失败!");
return head;
}
newNode->data = data;
nFind = findNode(head, id);
if(nFind) {
newNode->next = nFind->next;
nFind->next = newNode;
} else {
printf("没有找到满足要求的node!");
free(newNode); //没有找到指定结点记得释放内存
}
return head;
}
- 删除指定结点: 以id为key的删除操作如下。
/*删除第一个满足要求的结点(如果有多个满足要求的节点) status代表删除状态*/
pLinkedList deleteNode(pLinkedList head, int id, int *status) {
pNode temp;
pLinkedList h = head;
/*删除的是头结点 不能简单的head = NULL 这里的head指针只是原指针的拷贝*/
if(h && h->data.id == id) {
head = head->next;
free(h);
h = NULL;
*status = 1;
return head;
}
while(h) {
if(h->data.id == id) {
if(h->next == NULL) {//删除的是最后一个结点
free(h);
h = NULL;
temp->next = NULL;//把前一个结点指向NULL
*status = 1;
return head;
} else {
temp->next = h->next;
free(h);
h = NULL;
*status = 1;
return head;
}
} else {
temp = h;//保存删除节点的上一个节点 删除最后一个结点要把上一个节点的指针指向NULL
h = h->next;
}
}
*status = 0;
return head;
}
- 获取链表长度: 代码如下。
int linkedListLength(pLinkedList head) {
pLinkedList h = head;
int cnt = 0;
while(h) {
cnt++;
h = h->next;
}
return cnt;
}
- 判断链表是否为空: 代码如下。
int linkedListEmpty(pLinkedList head) {
return head==NULL;
}
3.封装代码
- linkedList.h
#ifndef __LIST_H__
#define __LIST_H__
typedef struct {
//填入你需要的数据
int id;
} Data, * pData;
typedef struct Node {
Data data;
struct Node* next;
} LinkedList, * pLinkedList, Node, *pNode;
int linkedListEmpty(pLinkedList head);
int linkedListLength(pLinkedList head);
pLinkedList addNodeFirst(pLinkedList head, Data data);
pLinkedList addNodeEnd(pLinkedList head, Data data);
pNode findNode(pLinkedList head, int id);
pLinkedList insertNode(pLinkedList head, int id, Data data);
pLinkedList deleteNode(pLinkedList head, int id, int *status);
#endif
- linkedList.c
#include "LinkedList.h"
#include <stdlib.h>
#include <stdio.h>
int linkedListEmpty(pLinkedList head) {
return head==NULL;
}
int linkedListLength(pLinkedList head) {
pLinkedList h = head;
int cnt = 0;
while(h) {
cnt++;
h = h->next;
}
return cnt;
}
pLinkedList addNodeFirst(pLinkedList head, Data data) {
pNode newNode;
if (!(newNode = (pNode)malloc(sizeof(Node)))) {
printf("申请内存失败!\n");
return head;
}
newNode->data = data;
newNode->next = head;
head = newNode;
return head;
}
pLinkedList addNodeEnd(pLinkedList head, Data data) {
pNode node;
pLinkedList h;
if(!(node=(pNode)malloc(sizeof(Node)))) {
printf("申请内存失败");
return head;
}
node->data = data;
node->next = NULL;
if(head == NULL) {
head = node;
return head;
}
h = head;
while(h->next != NULL) {
h = h->next;
}
h->next = node;
return head;
}
/*找到第一个满足条件的节点(如果有多个满足条件的结点)*/
pNode findNode(pLinkedList head, int id) {
pLinkedList h = head;
while(h) {
if(h->data.id == id) {
return h;
} else {
h = h->next;
}
}
return NULL;
}
/*把data插到指定id的node后面, 可能会有多个重复的id 这里插入到第一个指定id的后面*/
pLinkedList insertNode(pLinkedList head, int id, Data data) {
pNode newNode, nFind;
if(!(newNode=(pNode)malloc(sizeof(Node)))) {
printf("申请内存失败!");
return head;
}
newNode->data = data;
nFind = findNode(head, id);
if(nFind) {
newNode->next = nFind->next;
nFind->next = newNode;
} else {
printf("没有找到满足要求的node!");
free(newNode);
}
return head;
}
/*删除第一个满足要求的结点(如果有多个满足要求的节点) status代表删除状态*/
pLinkedList deleteNode(pLinkedList head, int id, int *status) {
pNode temp;
pLinkedList h = head;
/*删除的是头结点 不能简单的head = NULL 这里的head指针只是原指针的拷贝*/
if(h && h->data.id == id) {
head = head->next;
free(h);
h = NULL;
*status = 1;
return head;
}
while(h) {
if(h->data.id == id) {
if(h->next == NULL) {//删除的是最后一个结点
free(h);
h = NULL;
temp->next = NULL;//把前一个结点指向NULL
*status = 1;
return head;
} else {
temp->next = h->next;
free(h);
h = NULL;
*status = 1;
return head;
}
} else {
temp = h;//保存删除节点的上一个节点 删除最后一个结点要把上一个节点的指针指向NULL
h = h->next;
}
}
*status = 0;
return head;
}
4.总结
- 有错误的地方希望大家多多指正。(╥﹏╥)o
- 链表用来构建许多其它数据结构,如堆栈,队列和他们的派生,大家可以自己试试。