4.单链表

1.链表的定义

  • 线性表的链式存储结构被称作链表

    • 链表中的结点在存储器上的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
  • 线性表的链式表示又称为非顺序映像链式映像

  • 链表用一组物理位置任意的存储单元来存放线性表的数据元素。

  • 这组存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。

  • 链表中元素的逻辑次序和物理次序不一定相同

在这里插入图片描述

各结点由两个域组成:(数据+指针)

数据域:存储元素数值数据(当前结点的数值信息)

指针域:存储直接后继结点的存储位置(即下一个结点的位置信息)

与链式存储有关的术语

  1. 结点:数据元素的存储映像。由数据域和指针域两部分组成。

  2. 链表:n个结点由指针链组成一个链表。它是线性表的链式存储映像,称为线性表的链式存储结构。

  3. 单链表、双链表、循环链表:

    • 结点只有一个指针域的链表,称为单链表线性链表
    • 结点有两个指针域的链表,称为双链表
    • 首尾相连的链表称为循环链表(即最后一个元素的指针域不为NULL,而是存储了首元结点的指针)
  4. 头指针、头结点和首元结点:

在这里插入图片描述

头指针:是指向链表中第一个结点的指针

首元结点:是指链表中存储第一个数据元素a1的结点

头结点:是在链表的首元结点之前附设的一个结点

单链表可以分为不带头结点和带头结点两种形式

不带头结点:头指针直接存放第一个元素的地址

带头结点:头指针指向头结点,头结点的指针域再来存储首元结点

如何表示空表?

无头结点时,头指针为空时表示空表

有头结点时,当头结点的指针域为空时表示空表

头结点的数据域内装的是什么?

头结点的数据域可以为空,也可存放线性表长度等附加信息,但此节点不能计入链表长度值。

链表(链式存储结构)的特点

  1. 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。

  2. 访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点(这种存取元素的方法被称为顺序存取法),所以寻找第一个结点和最后一个结点所花费的时间不等。

2.单链表定义

带头结点的单链表

在这里插入图片描述

  • 单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名,若头指针名是L,则把链表称为表L

单链表的存储结构:

在这里插入图片描述

P为指向整型变量a的一个指针变量

如果需要定义一个指针变量P,则需要根据P指向的数据类型来定义它

int *p;	//*p为指针变量,它的数据类型必须与指向的变量数据类型相同
p = &a;	//将整型变量a的地址赋值给指针变量p,此时指针指向a,&为取地址运算符

定义单链表存储结构代码:

在这里插入图片描述

定义链表L:LinkList L;

定义结点指针p:LNode *p;或者LinkList p;

为了统一链表的操作,通常这样定义

在这里插入图片描述

单链表的初始化(带头结点的单链表):

  • 即构造一个如图的空表

在这里插入图片描述

算法步骤:

  1. 生成新结点作头结点,用头指针L指向头结点。
  2. 将头结点的指针域置空。

算法描述:

Status InitList_L(LinkList &L){
   		//初始化链表L;LinkList为指向有两个成员(data  *next)的结点的指针;&L为引用型变量
  	L = new LNode;	//new一个结点LNode,将结点地址赋值给L
    L->next = NULL;	//将L的next域(指针域)置空
    return OK;	//返回OK,分配成功
}

3.单链表中常用的简单算法

空表:链表中无元素,称为空链表(头指针和头结点依然在)

  • 判断链表是否为空

算法思路:判断头结点指针域是否为空

int ListEmpty(LinkList L){
   	//若L为空表,则返回1,否则返回0
    if(L->next)	//判断next(指针域)是否存在,存在则不为空
        return 0;
    else
        return 1;
}
  • 单链表的销毁(链表销毁后不存在)

算法思路:从头结点开始,依次释放所有节点

在这里插入图片描述

思路解析:先将L的值赋值给P,然后让当前L结点指针域的值(此值为下一结点的地址)赋值给L,删除p指向的结点,再让指针p指向新的结点,再重复之前的操作。当删除到最后一个结点时,L获取到的指针域的值为空,然后把空赋值给p,此时已经没有结点了,循环结束。

Status DestoryList_L(LinkList &L){
   	//销毁单链表L
    Lnode *p;	//声明指针P指向要销毁的结点
    while(L){
   	//当L存在时继续运行循环
        p=L;	//从头结点开始销毁
        L=L->next;
        delete p;
    }
    return OK;
}
  • 清空链表

清空后链表仍然存在,但链表中无元素,成为空链表(头指针和头结点仍然在)

算法思路:依次释放所有结点,并将头结点指针域设置为空

在这里插入图片描述

思路解析:现将头结点next域中首元结点的地址获取,再将它赋值给指针变量p,此时p指向首元结点,创建一个指针变量q,将p指向的结点的next域赋值给指针变量q,再删除p指向的结点;删除后再把q赋值给p,再让q的next域赋值给q,反复执行到循环结束,最后将头结点next域设置为NULL即可。

Status ClearList(LinkList &L){
   	//将L重置为空表
    Lnode *p,*q;	//声明指针P指向要销毁的结点,q指向下一个结点
    p=L->next;	//从首元结点开始清空
    while(p){
   	//当p存在时继续运行循环
        q=p->next;
        delete p;
        p=q;
    }
    L->next=NULL;	//将头结点指针域置空
    return OK;
}

4.单链表插入和删除

4.1插入

按位序插入

ListInsert(&L,i,e):插入操作。在表L中的第i个位置插入指定元素e。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 单链表的存储类型描述:单链表由若干个结点组成,每个结点包括两个域:数据域和指针域,数据域存放结点的数据,指针域存放下一个结点的地址信息。 2. 单链表的初始化、插入、删除等基本运的实现(C++代码): ```c++ #include <iostream> using namespace std; // 定义链表结点的结构体 struct ListNode { int val; ListNode* next; // 构造函数 ListNode(int x) : val(x), next(NULL) {} }; // 初始化链表,返回头结点指针 ListNode* initList() { ListNode* head = new ListNode(0); // 头结点的数据域置0 head->next = NULL; // 头结点的指针域置NULL return head; } // 在链表指定位置插入结点 void insertNode(ListNode* head, int pos, int val) { ListNode* p = head; int i = 1; // 将指针移动到待插入位置的前一个结点 while (p != NULL && i < pos) { p = p->next; i++; } if (p == NULL || i > pos) { // pos位置不存在 cout << "insert failed" << endl; return; } ListNode* newnode = new ListNode(val); // 创建新结点 newnode->next = p->next; p->next = newnode; // 将新结点插入到pos位置 } // 删除链表指定位置的结点 void deleteNode(ListNode* head, int pos) { ListNode* p = head; int i = 1; // 将指针移动到待删除位置的前一个结点 while (p != NULL && i < pos) { p = p->next; i++; } if (p == NULL || p->next == NULL) { // pos位置不存在 cout << "delete failed" << endl; return; } ListNode* delnode = p->next; p->next = delnode->next; // 将pos位置的结点删除 delete delnode; // 释放delnode的内存空间 } // 重载输出运符,输出链表 ostream& operator<<(ostream& os, ListNode* head) { ListNode* p = head->next; // 跳过头结点 while (p != NULL) { os << p->val << " "; p = p->next; } os << endl; return os; } // 重载输入运符,输入链表 istream& operator>>(istream& is, ListNode* head) { int n, x; cout << "请输入链表长度:"; is >> n; ListNode* p = head; // 插入位置从头结点开始 cout << "请输入链表元素:"; for (int i = 0; i < n; i++) { is >> x; ListNode* newnode = new ListNode(x); p->next = newnode; p = newnode; // 将新结点插入链表尾部 } return is; } // 逆置单链表 void reverseList(ListNode* head) { ListNode* p = head->next, *q; head->next = NULL; // 头结点指针域置NULL while (p != NULL) { q = p->next; // 暂存p的下一个结点 p->next = head->next; // 将p结点插入到头结点之后 head->next = p; p = q; // p指向原来的下一个结点 } } // 归并两个有序链表 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode* mergeHead = new ListNode(0); // 归并后的链表的头结点 ListNode* p = mergeHead; while (l1 != NULL && l2 != NULL) { if (l1->val <= l2->val) { p->next = l1; l1 = l1->next; } else { p->next = l2; l2 = l2->next; } p = p->next; // 将p指针移动到新结点 } p->next = l1 != NULL ? l1 : l2; // 将剩余结点接到结点末尾 return mergeHead->next; // 返回归并后的链表 } // 对单链表进行归并排序 ListNode* mergeSort(ListNode* head) { if (head == NULL || head->next == NULL) { // 如果链表为空或只有一个结点,直接返回 return head; } ListNode* slow = head, *fast = head->next; while (fast != NULL && fast->next != NULL) { slow = slow->next; // 慢指针指向链表的中间结点 fast = fast->next->next; // 快指针指向链表的末尾 } ListNode* mid = slow->next; // 将链表划分为两个部分 slow->next = NULL; ListNode* left = mergeSort(head); // 递归对左半部分进行排序 ListNode* right = mergeSort(mid); // 递归对右半部分进行排序 return mergeTwoLists(left, right); // 归并两个有序链表 } int main() { ListNode* head = initList(); cin >> head; cout << head; insertNode(head, 3, 4); cout << head; deleteNode(head, 2); cout << head; reverseList(head); cout << head; ListNode* sortedList = mergeSort(head); cout << sortedList; return 0; } ``` 3. 单链表的输入、输出运的实现已经在上面的代码中完成了,采用了重载输入输出流的方式。 4. 单链表的逆置、归并、排序等复杂运的实现已经在上面的代码中完成了。 5. 对单链表的各种运进行测试的主程序如下: ```c++ int main() { ListNode* head = initList(); cin >> head; // 输入链表 cout << "原始链表:" << head; // 输出链表 insertNode(head, 3, 4); // 在第3个位置插入4 cout << "插入结点后的链表:" << head; deleteNode(head, 2); // 删除第2个位置的结点 cout << "删除结点后的链表:" << head; reverseList(head); // 逆置链表 cout << "逆置后的链表:" << head; ListNode* sortedList = mergeSort(head); // 归并排序 cout << "归并排序后的链表:" << sortedList; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值