1.单链表的概念
1.1什么是链表
如下图,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系的存储结构就是链表。
或
核心是满足一个结点只有一个后继结点。
1.2结点的概念
如下图,单链表的每个基本单位都是这样的结构,即结点
每个结点都包括数据域和指针域,其中数据域用来存放元素的值,指针域用来指向该结点的后继结点。
例:链表{1,2,3}的结构就如下图。
所以,对于单链表,只要知道了第一个结点,就可以通过遍历访问整个链表,因此,第一个结点非常重要,一般称为头结点。
2.单链表的构造与销毁
2.1单链表结点的定义
struct Listnode{
int val;//代表数据
struct Listnode* next;//结点类型的指针,指向后继元素
};
2.2示例构造单链表
创建一个结构为{1,2,3,4,5}的单链表,函数如下
//创建一个结构为{1,2,3,4,5}的单链表
struct ListNode* initLink() {
//创建头指针
struct ListNode* head = NULL;
//创建头结点
struct ListNode* temp = new struct ListNode;
temp->val = 1;
temp->next = NULL;
//头指针指向头结点
head = temp;
//创建剩下的结点
for (int m = 2; m <= 10; m++) {
struct ListNode* a = new struct ListNode;
a->val = m;
a->next = NULL;
//每创建一个结点,就将其连上,并更新temp至最新创建的结点
temp->next = a;
temp = a;
}
//返回头指针
return head;
}
2.3单链表的销毁
单链表的销毁也是逐一销毁,用一个指针记录本次循环要销毁的结点,然后用temp指针向后移动,指向下一次要销毁的结点。//可以在销毁前输出结点的值,方便检查
//销毁链表
void destroyList(struct ListNode* head) {
struct ListNode* temp = head;
//释放链表的所有结点
while (temp) {
struct ListNode* d = temp;
//cout << temp->val<<"->";
temp = temp->next;
delete d;
}
temp = NULL;
}
3.单链表的遍历
对于单链表的遍历,就是一个一个逐一向后访问,重要的是要记得保留好头指针,不要只顾着向后遍历最后将头指针丢了。
3.1打印链表所有结点的值
//打印链表所有结点的值
void printList(struct ListNode* head) {
struct ListNode* temp = head;
while (temp) {
cout << temp->val<<"->";
temp = temp->next;
}
}
3.2获取链表的长度
//获取链表的长度
int getLength(struct ListNode* head) {
//若传入的是空链表,return 0
if (head == NULL) return 0;
int length = 0;
struct ListNode* temp = head;
while (temp) {
length++;
temp = temp->next;
}
return length;
}
4.单链表的插入
单链表的插入共分以下三种情况
4.1在单链表的表头插入
只需将新结点的后继指针指向原头结点,并将头指针更新为新结点//此处是易错点,容易将这一步漏掉
示意图如下
4.2在单链表的中间插入
在中间插入比较复杂,要注意操作的顺序,我们需要通过遍历找到目标位置的前一个位置,所以在循环遍历的时候循环判断要用current->next来判断位置。
如下图所示,要想将新结点插在15与7之间,要使current=node(15),接着先将new->next=node(15)->next,即node(7),然后将node(15)->next=new,顺序不能颠倒。
4.3在单链表的尾部插入
在单链表尾部插入只需找到原来的尾结点,然后将原尾结点->=new,就可以了。
在实际代码实现时,其实可以将中间插入和尾部插入归为一类,因为尾部插入其实就是中间插入的特殊情况。
//在单链表插入新结点
struct ListNode* insertNode(struct ListNode* head, struct ListNode* newNode, int position) {
//若要插入的是空链表,直接返回newNode
if (head == NULL) return newNode;
int size = getLength(head);
if (position<1 || position>size + 1) {
cout << "位置参数越界" << endl;
return head;
}
//在表头插入
if (position == 1) {
newNode->next = head;
head = newNode;
return head;
}
//在表中或链表尾插入
int count = 1;
struct ListNode* cur = head;
//使cur指向position-1的结点
while (count < position-1) {
count++;
cur = cur->next;
}
newNode->next = cur->next;
cur -> next = newNode;
return head;
}
5.单链表的删除
5.1删除头结点
先使用指针d保存head,接着将head=head->next,最后delete原先保存的d即可。
5.2删除尾结点
和插入结点一样,先需要找到尾结点的前一个结点,然后使用指针d保存cur->next(即要删除的尾结点),接着cur->next=NULL,最后delete原先保存的d即可。
5.3删除中间结点
一样要找的删除结点的前一个结点,用指针d保存cur->next(即要删除的结点),然后cur->next=cur->next->next,最后delete原先保存的d即可。
同样,在实际代码实现时,其实可以将删除中间和删除尾部归为一类,因为删除尾部其实就是删除中间的特殊情况。
//在单链表删除结点
struct ListNode* deleteNode(struct ListNode* head, int position) {
//若为空链表,返回NULL
if (head == NULL) return NULL;
int size = getLength(head);
if (position<1 || position>size) {
cout << "位置参数错误" << endl;
}
//删除头结点
if (position == 1) {
struct ListNode* d = head;
head = head->next;
delete d;
return head;
}
//删除表中或尾结点
int count = 1;
struct ListNode* cur = head;
//使cur指向position-1的结点
while (count < position - 1) {
count++;
cur = cur->next;
}
struct ListNode* d = cur->next;
cur->next = cur->next->next;
delete d;
return head;
}