链表简介
链表是一种数据结构,由若干个节点串联而成,构成一条完整的链表。
可以把链表理解成为一列火车,每个节点,就是一节车厢。
车厢可以装货物(存储数据),每节车厢有一个挂钩(指针),连接下一节车厢。
火车头,就是链表的头节点。头节点没有数据(不装货物),实际第几个节点是从车厢开始算起。
链表还可以分为单链表、双链表、循环链表。本文先介绍一下单链表。
链表优点:
链表可以动态的存储数据,不需要一开始就把所有的空间创建出来,而且空间也不必连续。
而数组则必须一开始就确定数组的大小,创建出数组的完整空间,并且空间必须是连续的。
缺点:
不能根据索引查找,只能根据指针进行遍历。
链表的应用:
最典型的使用链表结构进行存储的数据,比如轨迹跟踪数据,需要记录每个时刻的位置、时间、角度、速度、里程等信息,并且,每个时刻的数据和它的前一时刻、后一时刻都是紧密相连的。
单链表结构
struct Node
{
int data; //数据
Node *next; //指向下一个节点的指针(存放下一个节点的地址)
};
这是链表的一个节点,火车头和所有车厢都是这样的一个节点。
头节点(火车头)不装数据,尾节点的挂钩(next指针)为NULL。
创建一个新节点
Node *p = new Node;
p->data 就是获取这个节点的数据,
p->next 既是这个节点的next成员变量,又是下一个节点
从一个节点跳到下一个节点
p = p->next;
判断是否到达尾节点
p->next != NULL
判断节点是否存在
p==NULL
注意:
p表示节点(整个车厢),p->next表示节点的成员变量next(挂钩),也表示下一个节点,这两个容易混淆。
在链表的尾节点p后面增加新节点n
n->next = NULL; //现在n是尾节点,next必须是NULL
p->next = n; //原来的尾节点p的后面是n
在任意节点p的后面插入新节点n
n->next = p->next; //原来p后面的节点,现在在n的后面了
p->next = n; //p的后面是n
删除任意节点p
删除节点后,要把p前面的节点和p后面的节点直接连接起来,因此要先找到p前面的节点。
//首先找到p前面的那个节点pre
while(pre->next != p)
{
pre = pre->next;
}
//此时,pre是p前面的那个节点
pre->next = p->next; //前一个节点的next变量,装p的下一个节点,此时p节点没有用了
free(p); //释放p
示例代码
1.创建链表时,从1个节点开始,也就是头节点(火车头)。
2.所有对链表的操作(增删改查),都从头节点开始迭代。
3.链表长度是不包含头节点的,因为头节点没有实际数据。也可以把头节点看成第0个节点。
4.单链表尾节点的next指针必须是NULL。
#include <iostream>
using namespace std;
//单链表结构
struct Node
{
int data;
Node *next;
};
//创建单链表,初始只有1个头节点
Node* createSingleList()
{
Node *head = new Node;//1个节点
head->next = NULL;
return head;
}
//在单链表尾部插入新节点
void addNode(Node* head, int data)
{
Node *p = head;//定义指针,指向头节点
Node *node = new Node;//新增节点
//为新节点赋值
node->next =NULL;//新增节点是最后一个节点
node->data =data;
//找到原链表的最后一个节点
while(p->next != NULL)
{
p = p->next;
}
//p是原链表的最后一个节点,现在它后面是新增节点
p->next = node;
}
//输出链表,head是头节点
void printList(Node* head)
{
if(head==NULL) return;
Node *p = head;//定义指针,指向头节点
while(p->next!=NULL)
{
p=p->next;//指针向后递进,不打印头节点
cout << p->data << " "; //输出数据
}
cout << endl;
}
//计算单链表长度,不含头节点
int lengthList(Node* head)
{
Node *p = head;//定义指针,指向头节点
int len = 0;
while(p->next != NULL)
{
len++;
p = p->next;
}
return len;
}
//取出单链表第i个节点(不算头节点)
Node* getNode(Node* head, int i)
{
if(head == NULL) return NULL;
int k = 0;
Node *p = head;//定义指针,指向头节点
while( (p->next!=NULL) && (k<i) )
{
p=p->next;
k++;//此时,k和第i个节点对应
}
if((k==i) && (k!=0))
return p;
else
{
cout << "node is not exist";
return NULL;
}
}
//在第i个节点后面插入新节点
void insertNode(Node *head, int i, int data)
{
int k = 0;
Node* p = head;//定义指针,指向头节点
while( (p->next!=NULL) && (k<i) ) //找到第i个节点
{
p = p->next;
k++;
}
//此时,k=i,p是第i个节点
if(p==NULL)
cout << "node is not exist" << endl;
else
{
Node *s = new Node;//新增节点
s->data = data; //赋值
s->next = p->next; //新节点的后面就是原来p节点的下一个节点
p->next = s; //现在p节点的后面就是新节点s
}
}
//在节点p后面插入新节点
void insertNode(Node *p, int data)
{
Node* s = new Node;//创建新节点
s->data = data;
s->next = p->next;//新节点的下一个就是原来p节点的下一个节点
p->next = s;//p节点的下一个是新节点
}
//删除第i个节点
void deleteNode(Node* head, int i)
{
int k = 0;
Node *pre = head;//定义指针,表示要删除节点的前一个节点,初始时指向头节点
//找到这个节点前面的节点
while( (pre->next!=NULL) && (k<i-1) )
{
pre = pre->next;
k++;//此时,k和第i-1个节点对应
}
//此时,pre是第i-1个节点
if(pre == NULL)
cout << "node is not exist";
else
{
Node *t;//临时节点,为了把要删除的节点内存释放
t = pre->next;
pre->next = pre->next->next;//pre->next里面装的是下一个的下一个节点地址
free(t);//释放内存
}
}
//删除指定节点
void deleteNode(Node *head, Node *p)
{
Node *pre = head;//要删除的节点前一个节点
while(pre->next!=p)
{
pre = pre->next;
}
//此时,pre是p的前一个
if(pre == NULL)
{
cout << "node is not exist";
}
else
{
pre->next = p->next; //pre->next里面装的是p的下一个节点
free(p); //释放p节点内存
}
}
int main()
{
//创建单链表,打印链表,链表长度,取出指定节点
Node *head = createSingleList();//创建链表
addNode(head,1); //添加节点
addNode(head,2);
addNode(head,3);
addNode(head,4);
addNode(head,5);
printList(head); //结果:1 2 3 4 5
cout << "单链表长度为:" << lengthList(head) << endl; //结果:单链表长度为:5
Node *p = getNode(head, 4);//取出第4个节点(不算头节点)
cout << "node 4 is:" << p->data << endl; //结果:node 4 is: 4
//插入节点
insertNode(p,88);//在第4个节点后面插入88
insertNode(head, 0, 10);//在第0个节点后插入10
printList(head);//结果:10 1 2 3 4 88 5
cout << "单链表长度为:" << lengthList(head) << endl;//结果:单链表长度为:7
//删除节点
p = getNode(head, 1);//取出第1个节点(就是10)
deleteNode(head,p);//删除指定节点
printList(head);//结果:1 2 3 4 88 5
deleteNode(head,5);//删除第5个节点(就是新增的88)
printList(head);//结果:1 2 3 4 5
deleteNode(head,5);//删除第5个节点(删除最后1个)
printList(head);//结果:1 2 3 4
return 0;
}