11.单链表

11.单链表

链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻,那么怎么表示逻辑上的相邻关系呢?可以给每个元素附加一个指针域,指向下一个元素的存储位置。如图所示:

从图中可以看出,每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址,因此指针指向的类型也是结点类型

链表的核心要素:

  • 每个节点由数据域和指针域组成
  • 指针域指向下一个节点的内存地址

其结构体定义:

Typedef struct LinkNode
{
	ElemType data;
	struct LinkNode *next;
}LinkList, LinkNode;

11.1 链表结构

链表的节点均单向指向下一个节点,形成一条单向访问的数据链

typedef struct _LinkNode
{
	int data; //结点的数据域
	struct _LinkNode* next; //结点的指针域
}LinkNode, LinkList; //LinkList 为指向结构体LNode 的指针类型

11.2 链表初始化

bool InitList(LinkList*& L)
{
	L = new LinkNode;

	if (!L) return false;//生成节点失败

	L->next = NULL;
	return true;
}

11.3 插入数据

11.3.1前插

//前插法
bool ListInsert_front(LinkList* &L, LinkNode *node)
{
	if (!L || !node) return false;

	node->next = L->next;
	L->next = node;
}

11.3.2尾插

//尾插法
bool ListInsert_back(LinkList*& L, LinkNode* node)
{
	LinkNode* last = NULL;

	if (!L || !node) return false;

	last = L;

	while (last->next) last = last->next;

	node->next = NULL;
	last->next = node;
	return true;
}

11.3.3 任意位置插入元素

// ---------------------------------------------------------
// 指定位置插入(越界自动转为尾插)
// L: 链表头, i: 插入位置(从1开始计数), e: 插入的数据
// ---------------------------------------------------------
bool LinkListInsert(LinkList*& L, int i, int e)
{
	if (!L) return false;
	if (i < 0) 
	{
		cout << "插入位置必须大于等于0" << endl;
		return false;
	}

	// p 指向头结点,j 代表 p 当前指向的是第几个结点(头结点算第0个)
	LinkNode* p = L;
	int j = 0;

	// 核心逻辑:
	// 我们要找第 i-1 个结点(即插入位置的前驱)。
	// 循环条件:
	// 1. p->next != NULL:只要后面还有节点,就可以往后找
	// 2. j < i - 1:还没找到第 i-1 个位置
	// 如果 i 很大,p->next 会先变成 NULL,循环终止,此时 p 指向最后一个节点(满足越界插在尾部的需求)
	while (p->next != NULL && j < i - 1)
	{
		p = p->next;
		j++;
	}

	// 生成新节点
	LinkNode* node = new LinkNode;
	if (!node) return false; // 内存分配失败检查
	node->data = e;
	node->next = p->next;
	p->next = node;

	return true;
}

11.4 打印

//打印
void LinkPrint(LinkList*& L)
{
	LinkNode* p = NULL;

	if (!L)
	{
		cout << "链表为空。";
		return;
	}
	p = L->next;

	while (p)
	{
		cout << p->data << "\t";
		p = p->next;
	}
	cout << endl;
}

11.5 查询值

11.5.1 按位置查找值

//按位置查询值
// L: 链表头, i: 要查询的位置(从1开始), e: 用于带回结果的引用
bool GetLinkListElemBySite(LinkList* L, const int i, int& e)
{
	// 1. 安全性检查:链表头不能空,i必须合法
	if (!L || !L->next || i < 1) 
	{
		return false; 
	}

	// 2. 初始化遍历指针
	// index 初始化为 1,对应第一个有效数据节点
	int index = 1; 
	LinkNode* node = L->next; 

	// 3. 开始查找
	// 循环条件:node不为空(没走到尽头) 且 当前位置还没到目标位置 i
	while (node && index < i)
	{
		node = node->next; // 指针后移
		index++;           // 计数器+1
	}

	// 4. 结果判断
	// 循环结束有两种情况:
	// 情况A:node 变成 NULL 了(说明 i 超过了链表长度),查找失败
	// 情况B:index == i(说明找到了),此时 node 指向第 i 个节点
	if (!node) 
	{
		return false; // 没找到(越界)
	}

	// 找到了,赋值给 e
	e = node->data;
	return true;
}

11.5.2按值查找

按位置查询值
bool GetLinkListElemByValue(LinkList*& L, const int e, int& index)
{
	if (!L || !L->next)
	{
		index = 0;
		return false;
	}

	LinkNode* p = L->next;
	index = 1;
	while (p != NULL)
	{
		if (p->data == e)
		{
			return true;
		}
		p = p->next;

		index++;
	}

	// 如果循环跑完了还没返回,说明没找到。
	// 此时 index 的值是 (链表长度 + 1),这是错误的,必须手动置 0
	index = 0;
	return false;
}

11.6 单链表的删除

//链表结点删除
bool DeleteLinkListNodeByIndex(LinkList*& L, const int index)
{
	if (!L || !L->next)
	{
		return false;
	}

	LinkNode* p = L;
	LinkNode* q = p->next;
	int i = 1;
	while (q)
	{
		if (i == index)
		{
			break;
		}

		p = p->next;
		q = q->next;
		i++;
	}

	// 检查是否找到了要删除的节点
	if (!q)
	{
		return false;  // index超出范围
	}

	// 删除节点q(无论是第几个节点,处理方式都一样)
	p->next = q->next;
	delete q;  // 释放内存

	return false;
}

11.7 链表销毁

void LinkDestroy(LinkList*& L)//单链表的销毁
{
	//定义临时结点p指向头结点
	LinkList* p = L;
	cout << "销毁链表!" << endl;

	while (p)
	{
		L = L->next;//L指向下一个结点
		delete p;//删除当前结点
		p = L;//p移向下一个结点
	}
}

完整代码

#include <iostream>
#include <string>
#include <stdlib.h>

using namespace std;

typedef struct _LinkNode
{
	int data;             // 结点的数据域
	struct _LinkNode* next; // 结点的指针域
} LinkList, LinkNode;

// 初始化
bool InitLinkList(LinkList*& L)
{
	L = new LinkNode;
	if (!L) return false;

	L->next = NULL;
	L->data = -1; // 头结点通常不存有效数据,或者用于存长度
	return true;
}

// 前插法
bool LinkListInsert_front(LinkList*& L, LinkNode* node)
{
	if (!L || !node) return false;
	node->next = L->next;
	L->next = node;
	return true;
}

// 尾插法
bool LinkListInsert_back(LinkList*& L, LinkNode* node)
{
	if (!L || !node) return false;
	LinkNode* last = L;
	while (last->next)
	{
		last = last->next;
	}
	node->next = NULL;
	last->next = node;
	return true;
}

// ---------------------------------------------------------
// 指定位置插入(越界自动转为尾插)
// L: 链表头, i: 插入位置(从1开始计数), e: 插入的数据
// ---------------------------------------------------------
bool LinkListInsert(LinkList*& L, int i, int e)
{
	if (!L)  return false;
	if (i < 1)
	{
		cout << "插入位置必须>=1!" << endl;
		return false;
	}

	LinkNode* node = L;
	int j = 0;
	while (node->next != NULL && j < i - 1)
	{
		node = node->next;
		j++;
	}

	LinkNode* newNode = new LinkNode;
	if (!newNode)  return false;
	newNode->data = e;
	newNode->next = node->next;
	node->next = newNode;

	return true;
}

// 打印链表
void LinkListPrint(LinkList*& L)
{
	if (!L)
	{
		cout << "链表不存在!" << endl;
		return;
	}
	LinkNode* node = L->next;
	if (!node) {
		cout << "链表为空" << endl;
		return;
	}
	while (node)
	{
		cout << node->data << "\t";
		node = node->next;
	}
	cout << endl;
}

//按位置查询值
bool GetLinkListElemByValue(LinkList*& L, int e, int& index)
{
	if (!L || !L->next)
	{
		index = 0;
		return false;
	}

	LinkNode* p = L->next;
	index = 1;
	while (p != NULL)
	{
		if (p->data == e)
		{
			return true;
		}
		p = p->next;

		index++;
	}

	// 如果循环跑完了还没返回,说明没找到。
	// 此时 index 的值是 (链表长度 + 1),这是错误的,必须手动置 0
	index = 0;
	return false;
}

//链表结点删除
bool DeleteLinkListNodeByIndex(LinkList*& L, int index)
{
	if (!L || !L->next)
	{
		return false;
	}

	LinkNode* p = L;
	LinkNode* q = p->next;
	int i = 1;
	while (q)
	{
		if (i == index)
		{
			break;
		}

		p = p->next;
		q = q->next;
		i++;
	}

	// 检查是否找到了要删除的节点
	if (!q)
	{
		return false;  // index超出范围
	}

	// 删除节点q(无论是第几个节点,处理方式都一样)
	p->next = q->next;
	delete q;  // 释放内存

	return true;
}

//链表销毁
void DestroyLinkList(LinkList*& L)
{
	LinkNode* p = L;

	while (p)
	{
		L = L->next;
		delete p;
		p = L;
	}
}

int main()
{
	LinkList* L = NULL;
	// 1.初始化空链表
	InitLinkList(L);

	// 2.使用前插法插入数据
	cout << "--- 前插法测试 ---" << endl;
	cout << "请输入需要插入的元素个数:";
	int insertFrontNum;
	cin >> insertFrontNum;
	LinkNode* s = NULL;
	while (insertFrontNum--)
	{
		cout << "请输入数据:";
		s = new LinkNode;
		cin >> s->data;
		LinkListInsert_front(L, s);
	}
	cout << "前插法结果:";
	LinkListPrint(L);

	// 3.使用尾插法插入数据
	cout << "\n--- 尾插法测试 ---" << endl;
	cout << "请输入需要插入的元素个数:";
	int insertBackNum; // 建议用新变量,避免逻辑混淆
	cin >> insertBackNum;

	while (insertBackNum--)
	{
		cout << "请输入数据:";
		s = new LinkNode;
		cin >> s->data;
		LinkListInsert_back(L, s);
	}
	cout << "尾插法结果:";
	LinkListPrint(L);

	// 4.任意位置插入元素(核心测试)
	cout << "\n--- 任意位置插入测试 ---" << endl;
	while (true) {
		int i, e;
		cout << "请输入插入位置 i (输入0退出) 和 数据 e:";
		cin >> i;
		if (i == 0) break;
		cin >> e;

		LinkListInsert(L, i, e);
		cout << "插入后链表状态:";
		LinkListPrint(L);
	}

	// ---------------------------------------------------------
	// 接您代码中的步骤 5...
	// ---------------------------------------------------------

	// 5. 按值查询 (测试 GetLinkListElemByValue)
	cout << "\n>>> 5. 测试按值查询 <<<" << endl;
	int searchVal, index;
	cout << "请输入要查询的数值:";
	cin >> searchVal;

	// 调用查找函数,通过返回值判断是否存在
	if (GetLinkListElemByValue(L, searchVal, index))
	{
		cout << "查询成功!值 " << searchVal << " 在第 " << index << " 个位置。" << endl;
	}
	else
	{
		cout << "查询失败!链表中不存在该值。" << endl;
	}

	// 6. 按位置删除 (测试 DeleteLinkListNodeByIndex)
	cout << "\n>>> 6. 测试按位置删除 <<<" << endl;
	int delIndex;
	cout << "请输入要删除第几个节点:";
	cin >> delIndex;

	// 调用删除函数
	if (DeleteLinkListNodeByIndex(L, delIndex))
	{
		cout << "删除成功!当前链表如下:" << endl;
		LinkListPrint(L); // 打印验证
	}
	else
	{
		cout << "删除失败!(位置越界或链表为空)" << endl;
	}

	// 7. 链表销毁 (测试 DestroyLinkList)
	cout << "\n>>> 7. 测试链表销毁 <<<" << endl;
	cout << "按任意键销毁链表..." << endl;
	system("pause"); // 暂停一下,让用户准备好

	DestroyLinkList(L);

	cout << "销毁完成,尝试打印链表:" << endl;
	LinkListPrint(L); // 这里应该提示“链表不存在”

	system("pause");
	return 0;
}

参考资料:

奇牛学院

posted @ 2023-06-25 21:57  CodeMagicianT  阅读(52)  评论(0)    收藏  举报