一、单链表的定义
在了解单链表之前,我们得先知道顺序表。在使用顺序表实现插入和删除的时候需要移动大量的元素,影响了运行的效率,所以我们引入单链表这种线性表的链式存储结构。单链表也是线性的存储元素,但是与顺序表不一样的是单链表是不需要使用地址连续的存储单元,他的元素只是在逻辑上相邻,并不在物理存储上相邻。
单链表的结构:
单链表由两个部分组成,一个是单链表存储的数据,一个是存放指向后继的指针。以下是代码展示:
typedef struct node
{
int data;//这是单链表存储的数据data
struct node * next;//这是单链表中存储指向后继的指针
}*Link,Node;
二、单链表实现的基本操作
1.单链表的初始化
我们通常使用一个头指针来标识一个单链表,并且在链表的第一个节点之前附加一个头节点,头节点可以不存储任何数据,头指针一般指向头节点,如果头指针指向NULL的话说明,这是一个空表,头节点指向链表的第一个节点。
头节点和头指针的区别:头指针始终指向单链表的第一个节点,而头节点是带头节点的单链表的第一个节点。
单链表的初始化就是申请一个头节点,并将头节点的指针域设置为空。
Link initList(Link head)
{
Link p = (Link)malloc(sizeof(Node));//申请一个头节点空间
head->p;//让头指针指向头节点
p->next=NULL;//让头节点的指针指向空,头节点可以不存储信息
}
2.单链表的头插法
当我们初始化单链表之后,我们就要考虑网单链表中插入元素了,这里首先介绍头插法,顾名思义,头插法的意思就是每次插入元素都从单链表的最前面插入,也就是说越先插入的元素越在后面。
如图所示,每次我们都是从单链表的最前面插入新的节点,这里我们需要在插入之前先申请一个新的节点,将这个需要插入的信息存储到这个新的节点里面,然后开始插入,这里我们需要注意一下,就是在插入的时候我们在对这些节点的指针指向的改变的顺序。我们要保证在插入的整个过程中不丢失任何节点,这里的顺序应该是:
- 将新节点的指针指向头节点的下一个节点
- 将头节点的指针指向新的节点
这里我们讨论一下为什么不能改变这个顺序?如果我们先将头节点的指针指向新的节点会发生什么,这个时候我们虽然将新的节点放在了头节点的后面,但是我们无法获得在加入之前链表中头节点后面的那个节点的地址了,这样的话我们将丢失后面所有的数据。所以插入的顺序是不能改变的!
代码实现如下:
bool insertList_head(Link head,int n)
{
Link p = (Link)malloc(sizeof(Node));//为新增的节点申请空间
p->data = n;//将需要插入的数据保存到新申请的节点
p->next = head->next;//按照头插法的插入顺序将新节点插入到链表中
head->next = p;
return true;
}
3.单链表的尾插法
单链表的尾插法与头插法对照着看,头插法是在链表的最前面插入数据,而尾插法则是在链表的最后面插入数据,尾插法跟头插法不同,他是先插入的数据在前面,后插入的数据在后面。
如图所示,在进行尾插法的时候,我们依然是先申请一个新的节点,然后将我们需要插入的数据保存到这个新的节点里面,然后我们新建一个指针,将指针移动到链表的最后一个节点,然后开始插入数据,这里我们因为是在最后面插入数据,所以比较简单,直接将最后一个节点的指针指向新建的这个节点,再将新建的这个节点的指针指向空。实现的顺序如下:
- 新建一个新的节点,将需要插入的数据保存到这个节点
- 新建一个指针并将其移动到链表的最后面
- 将最后一个节点的指针指向这个新创建的节点
- 将新创建的这个节点的指针指向空
代码实现如下:
bool insertList_end(Link head,int n)
{
Link p = (Link)malloc(sizeof(Node));//新创建一个节点保存需要插入的数据
p->data = n;
Link q = head;//新创建一个指针并将其移动到这个链表的最后一个节点
while(q->next != NULL)
{
q = q->next;
}
q->next = p;//将链表的最后一个节点的指针直接指向这个新创建的节点
p->next = NULL;//将新创建的节点的指针指向空
return true;
}
4.单链表的删除
单链表的节点删除我们可以设想,我们将需要删除的那个节点单独拿出来,并将其存储空间释放,可以理解为删除,首先我们将需要删除的节点的位置和这个链表作为参数传入函数里面。
在删除操作执行之前,我们还需要判断一下这个参数的合法性,然后开始遍历这个链表,找到我们需要删除的节点,将其删除即可。删除的顺序是:
- 先判断参数的合法性
- 找到需要删除节点的上一个节点
- 新建一个指针指向需要删除的节点,也就是上面找到的节点的下一个节点,将这个这个节点的指针直接指向下下个节点
- 直接释放需要删除的这个节点
需要注意的是一定是要释放掉这个删除的节点,而不是把他从链表中丢掉而以。
代码实现如下:
bool deleteList(Link head,int index)
{
if(head == NULL || index < 0) return false;//判断参数的合法性
Link p = head;
int count = 0;
while(count < index-1 && p->next != NULL)
{
p = p->next;
count++;
}//找到需要删除节点的上一个节点
if(count != index-1 || p == NULL)return false;
if(count == index-1)//删除释放节点
{
Link q = p->next;
p->next = p->next->next;
free(q);
return true;
}
}
5.链表的打印
此方法实现较为简单,直接遍历链表所有节点即可。
代码实现如下:
void displayList(Link head)
{
Link p = head;
// p=p->next;
// cout<<p->data;
if(p->next == NULL)
{
cout << "链表为空" << endl;
return;
}
p = p->next;
while(p != NULL)
{
cout << p->data << " ";
p = p->next;
}
cout<<endl;
}
二、实现的全部代码
#include<iostream>
using namespace std;
typedef struct node
{
int data;
struct node * next;
}*Link,Node;
Link initList()
{
Link head = (Link)malloc(sizeof(Node));
head->next = NULL;
return head;
}
bool insertList_head(Link head,int n)
{
Link p = (Link)malloc(sizeof(Node));
p->data = n;
p->next = head->next;
head->next = p;
return true;
}
bool insertList_end(Link head,int n)
{
Link p = (Link)malloc(sizeof(Node));
p->data = n;
Link q = head;
while(q->next != NULL)
{
q = q->next;
}
q->next = p;
p->next = NULL;
return true;
}
bool deleteList(Link head,int index)
{
// index++;
if(head == NULL || index < 0) return false;
Link p = head;
int count = 0;
while(count < index-1 && p->next != NULL)
{
p = p->next;
count++;
}
if(count != index-1 || p == NULL)return false;
if(count == index-1)
{
Link q = p->next;
p->next = p->next->next;
free(q);
return true;
}
}
void displayList(Link head)
{
Link p = head;
// p=p->next;
// cout<<p->data;
if(p->next == NULL)
{
cout << "链表为空" << endl;
return;
}
p = p->next;
while(p != NULL)
{
cout << p->data << " ";
p = p->next;
}
cout<<endl;
}
int main()
{
Link head = initList();
displayList(head);
insertList_head(head,1);
insertList_head(head,2);
displayList(head);
insertList_end(head,999);
displayList(head);
deleteList(head,2);
displayList(head);
return 0;
}
这是执行结果:
链表为空
2 1
2 1 999
2 999