数据结构——考研笔记(三)线性表之单链表

文章目录

      • [2.3 单链表](#2.3 单链表)
        • [2.3.1 知识总览](#2.3.1 知识总览)
        • [2.3.2 什么是单链表](#2.3.2 什么是单链表)
        • [2.3.3 不带头结点的单链表](#2.3.3 不带头结点的单链表)
        • [2.3.4 带头结点的单链表](#2.3.4 带头结点的单链表)
        • [2.3.5 不带头结点 VS 带头结点](#2.3.5 不带头结点 VS 带头结点)
        • [2.3.6 知识回顾与重要考点](#2.3.6 知识回顾与重要考点)
        • [2.3.7 单链表的插入和删除](#2.3.7 单链表的插入和删除)
          • [2.3.7.1 按位序插入(带头结点)](#2.3.7.1 按位序插入(带头结点))
          • [2.3.7.2 按位序插入(不带头结点)](#2.3.7.2 按位序插入(不带头结点))
          • [2.3.7.3 指定结点的后插操作](#2.3.7.3 指定结点的后插操作)
          • [2.3.7.4 指定结点的前插操作](#2.3.7.4 指定结点的前插操作)
          • [2.3.7.5 按位序删除(带头结点)](#2.3.7.5 按位序删除(带头结点))
          • [2.3.7.6 知识回顾与重要考点](#2.3.7.6 知识回顾与重要考点)
        • [2.3.8 单链表的查找](#2.3.8 单链表的查找)
          • [2.3.8.1 按位查找](#2.3.8.1 按位查找)
          • [2.3.8.2 按值查找](#2.3.8.2 按值查找)
          • [2.3.8.3 知识回顾与重要考点](#2.3.8.3 知识回顾与重要考点)
        • [2.3.9 单链表的建立](#2.3.9 单链表的建立)
          • [2.3.9.1 尾插法](#2.3.9.1 尾插法)
          • [2.3.9.2 头插法](#2.3.9.2 头插法)

2.3 单链表

2.3.1 知识总览
2.3.2 什么是单链表
  • 顺序表

    • 优点:可随机存取,存储密度高
    • 缺点:要求大片连续空间,改变容量不方便
  • 单链表

    • 优点:不要求大片连续空间,改变容量方便
    • 缺点:不可随机存取,要消耗一定空间存放指针
  • 用代码实现单链表

cpp 复制代码
typedef struct LNode{			//定义单链表结点类型
    ElemType data;		//每个节点存放一个数据元素
    struct LNode* next;  //指针指向下一个节点
}LNode,*LinkList;
2.3.3 不带头结点的单链表
cpp 复制代码
#include <stdlib.h>

typedef struct LNode {	//定义单链表结点类型
	int data;			//每个节点存放一个数据元素
	struct LNode* next; //指针指向下一个节点
}LNode,*LinkList;

//初始化一个空的单链表
bool InitList(LinkList& L) {
	L = NULL;	//空表,暂时还没有任何结点
	return true;
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if (L == NULL)
		return true;
	else
		return false;
}

void test() {
	LinkList L;	//声明一个指向单链表的指针
	//初始化一个空表
	InitList(L);
	//......后续代码......
}
2.3.4 带头结点的单链表
cpp 复制代码
#include <stdlib.h>

typedef struct LNode {	//定义单链表结点类型
	int data;			//每个节点存放一个数据元素
	struct LNode* next; //指针指向下一个节点
}LNode, * LinkList;

//初始化一个空的单链表
bool InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));	//分配一个头节点
	if (L == NULL)			//内存不足,分配失败
		return false;
	L->next = NULL;			//头结点之后暂时还没有节点
	return true;
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if (L->next == NULL)
		return true;
	else
		return false;
}

void test() {
	LinkList L;	//声明一个指向单链表的指针
	//初始化一个空表
	InitList(L);
	//......后续代码......
}
2.3.5 不带头结点 VS 带头结点
  • 不带头结点:写代码更麻烦对第一个数据结点和后续数据结点的处理需要用不同的代码逻辑对空表和非空表的处理需要用到不同的代码逻辑
  • 带头结点:写代码更方便。
2.3.6 知识回顾与重要考点
2.3.7 单链表的插入和删除
2.3.7.1 按位序插入(带头结点)
  • ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
  • 代码展示
cpp 复制代码
#include <stdlib.h>

typedef struct LNode {	//定义单链表结点类型
	int data;			//每个节点存放一个数据元素
	struct LNode* next; //指针指向下一个节点
}LNode, * LinkList;

//初始化一个空的单链表
bool InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));	//分配一个头节点
	if (L == NULL)			//内存不足,分配失败
		return false;
	L->next = NULL;			//头结点之后暂时还没有节点
	return true;
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if (L == NULL)
		return true;
	else
		return false;
}

//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList& L, int i, int e) {
	if (i < 1)
		return false;
	LNode* p;	//指针p指向当前扫描到的结点
	int j = 0;	//当前p指向的是第几个结点
	p = L;		//L指向头结点,头结点是第0个结点(不存数据)
	while (p != NULL && j < i - 1) {	//循环找到第i-1个结点
		p = p->next;
		j++;
	}
	if (p == NULL)	//i值不合法
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;	//将结点s连到p之后
	return true;	//插入成功
}

void main() {
	LinkList L;	//声明一个指向单链表的指针
	//初始化一个空表
	InitList(L);
	//......后续代码......
}
2.3.7.2 按位序插入(不带头结点)
  • 代码展示
cpp 复制代码
#include <stdlib.h>

typedef struct LNode {	//定义单链表结点类型
	int data;			//每个节点存放一个数据元素
	struct LNode* next; //指针指向下一个节点
}LNode, * LinkList;

//初始化一个空的单链表
bool InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));	//分配一个头节点
	if (L == NULL)			//内存不足,分配失败
		return false;
	L->next = NULL;			//头结点之后暂时还没有节点
	return true;
}

//判断单链表是否为空
bool Empty(LinkList L) {
	if (L == NULL)
		return true;
	else
		return false;
}

//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList& L, int i, int e) {
	if (i < 1)
		return false;
	if (i = 1) {	//插入第1个结点的操作与其它结点的操作不同
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = e;
		s->next = L;
		L = s;		//头指针指向新节点
		return true;
	}
	LNode* p;	//指针p指向当前扫描到的结点
	int j = 1;	//当前p指向的是第几个结点
	p = L;		//L指向头结点,头结点是第0个结点(不存数据)
	while (p != NULL && j < i - 1) {	//循环找到第i-1个结点
		p = p->next;
		j++;
	}
	if (p == NULL)	//i值不合法
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;	//将结点s连到p之后
	return true;	//插入成功
}

void main() {
	LinkList L;	//声明一个指向单链表的指针
	//初始化一个空表
	InitList(L);
	//......后续代码......
}
2.3.7.3 指定结点的后插操作
cpp 复制代码
//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode* p, int e) {
	if (p == NULL)
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if (s == NULL)	//内存分配失败
		return false;
	s->data = e;	//用结点s保存数据元素e
	s->next = p->next;
	p->next = s;	//将结点s连到p之后
	return true;
}
2.3.7.4 指定结点的前插操作
  • 代码展示
cpp 复制代码
//前插操作:在p结点之前插入元素e
bool InsertPriorNode(LNode* p, int e) {
	if (p == NULL)		//内存分配失败
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->next = p->next;
	p->next = s;		//新结点s连到p之后
	s->data = p->data;	//将p中元素复制到s中
	p->data = e;		//p中元素覆盖为e
	return true;
}
2.3.7.5 按位序删除(带头结点)
  • ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
  • 代码展示
cpp 复制代码
//删除表中第i个元素
bool ListDelete(LinkList& L, int i, int& e) {
	if (i < 1)
		return false;
	LNode* p;		//指针p指向当前扫描到的结点
	int j = 0;		//当前p指向的是第几个结点
	p = L;			//L指向头结点,头结点是第0个结点(不存数据)
	while (p != NULL && j < i - 1) {	//循环找到第i-1个结点
		p = p->next;
		j++;
	}
	if (p == NULL)	//i值不合法
		return false;
	if (p->next == NULL)	//第i-1个结点之后无其他结点
		return false;
	LNode* q = p->next;		//令q指向被删除结点
	e = q->data;			//用e返回元素的值
	p->next = q->next;		//将*q结点从链中"断开"
	free(q);				//释放结点的存储空间
	return true;			//删除成功
}
2.3.7.6 知识回顾与重要考点
2.3.8 单链表的查找
  • GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
  • LocalteElem(L,e):按值查找。在表L中查找具有给定关键字值的元素。
2.3.8.1 按位查找
  • 代码展示
cpp 复制代码
//按位查找,返回第i个元素(带头结点)
LNode* GetElem(LinkList L, int i) {
	if (i < 0)
		return false;
	LNode* p;		//指针p指向当前扫描到的结点
	int j = 0;		//当前p指向的是第几个结点
	p = L;			//L指向头节点,头结点是第0个结点(不存数据)
	while (p != NULL && j < i) {	//循环找到第i个结点
		p = p->next;
		j++;
	}
	return p;
}
2.3.8.2 按值查找
  • 代码展示
cpp 复制代码
//按值查找,找到数据域==e的结点
LNode* LocateElem(LinkList L, int e) {
	LNode* p = L->next;
	//从第1个结点开始查找数据域为e的结点
	while (p != NULL && p->data != e)
		p = p->next;
	return p;	//找到后返回该结点指针,否则返回NULL
}
2.3.8.3 知识回顾与重要考点
2.3.9 单链表的建立

如果给你很多个数据元素(ElemType),要把它们存到一个单链表里边,怎么办呢?

step1:初始化一个单链表

step2:每次取一个数据元素,插入到表尾/表头

2.3.9.1 尾插法
  • 代码展示
cpp 复制代码
//尾插法
LinkList List_TailInsert(LinkList& L) {	//正向建立单链表
	int x;								
	L = (LinkList)malloc(sizeof(LNode));//建立头结点
	LNode* s, * r = L;					//r为表尾指针
	scanf("%d", &x);					//输入结点的值
	while (x != 9999) {					//输入9999表示结束
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;
		r = s;							//r指向新的表尾结点
		scanf("%d", &x);
	}
	r->next = NULL;						//尾结点指针置空
	return L;
}
2.3.9.2 头插法
  • 代码展示
cpp 复制代码
//头插法
LinkList List_HeadInsert(LinkList& L) {
	LNode* s;
	int x;
	L = (LNode*)malloc(sizeof(LNode));	//建立头结点
	L->next = NULL;						//初始化空链表
	scanf_s("%d", &x);					//输入结点的值
	while (x != 9999) {					//输入9999表示结束
		s = (LNode*)malloc(sizeof(LNode));//创建新结点
		s->data = x;
		s->next = L->next;
		L->next = s;					//将新结点插入表中,L为头指针
		scanf_s("%d", &x);
	}
	return L;
}
相关推荐
努力学习编程的伍大侠12 分钟前
基础排序算法
数据结构·c++·算法
XiaoLeisj40 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jackey_Song_Odd2 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
乐之者v2 小时前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
️南城丶北离4 小时前
[数据结构]图——C++描述
数据结构··最小生成树·最短路径·aov网络·aoe网络
✿ ༺ ོIT技术༻4 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
菜鸡中的奋斗鸡→挣扎鸡11 小时前
滑动窗口 + 算法复习
数据结构·算法
axxy200012 小时前
leetcode之hot100---240搜索二维矩阵II(C++)
数据结构·算法
Uu_05kkq14 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法