链表的基本操作(增删改查)

2023-11-02

链表的基本操作(增删改查)

前提:
节点的类型:

typedef struct Node {

	int id;
	struct Node* next;
}SNode;

目录:
1链表的创建;
2 链表的插入;
3 链表的删除;
4 改,查;
5 链表的排序;
6 链表的翻转;
7 链表的打印;

1 链表的创建

//创建链表
SNode *CreateList() {

	SNode *head = NULL;
	head = (SNode*)malloc(sizeof(SNode));
	head->id = -1;
	head->next = NULL;

	//需要一个当前节点连接新节点
	SNode *pCur = head;

	int data; //可要可不要,用于保存键盘输入的值

	SNode *pNew = NULL; //新节点

	//循环创建新节点
	while (1) {

		cout << "请输入节点数据域:";
		scanf("%d", &data);

		//若输入值为-1让他停止创建节点
		if (data == -1) {
			break;
		}

		pNew = (SNode*)malloc(sizeof(SNode));
		pNew->id = data;
		pNew->next = NULL;

		//新节点与链表建立关系
		pCur->next = pNew;
		pNew->next = NULL;

		//最后要将当前节点往新节点移------我漏了
		pCur = pNew;

	}

	return head;
}

2 插入节点
1)要求:查找x,找到就创建新节点,放在该节点前;没找到就放在最后面。

void InsertList(SNode *head, int x, int y) {

	//由于插入需要知道新节点的前后节点,所以在定义两个节点指针
	SNode* pPre = head;           //指向头结点
	SNode* pCur = head->next;     //指向第二个节点

	//遍历寻找
	while (pCur != NULL) {
		//找到直接退出
		if (pCur->id == x) {
			break;
		}
		
		//没找到就就将两节点后移
		pPre = pCur;              //指向后一节点,即pCur
		pCur = pCur->next;        //指向后一节点,即pCur的下一节点
	}

	//while退出的两种情况
	//1.找到退出,pCur为匹配到的节点;pPre为匹配的前一节点
	//2.没找到末尾退出。 pCur为空节点NULL;pPre为最后一个节点

	//但是插入的代码是一样的
	SNode * pNew = (SNode*)malloc(sizeof(SNode));
	pNew->id = y;
	pNew->next = NULL;

	pPre->next = pNew;
	pNew->next = pCur;

}

2)排序(升序)后的插入,SortList()为下面讲到的排序函数。
要求:在值为x的节点前插入。
这里的插入都是按值来插。 当然你也可以按下标插入,这里不写,因为删除一会会说到。

int InsertListPro(SNode *head, int x) {


	cout << "=====================" << endl;
	int ret = SortList(head);
	if (ret != 0) {
		cout << "插入失败" << endl;
		return -2;
	}
	PrintList(head);

	//升序排序后插入

	//由于插入需要知道新节点的前后节点,所以在定义两个节点指针
	SNode* pPre = head;           //指向头结点
	SNode* pCur = head->next;     //指向第二个节点

	//遍历寻找
	while (pCur != NULL) {
		//找到直接退出
		if (x < pCur->id) {    //就改了这个条件和y改成x与插入相比
			break;
		}

		//没找到就就将两节点后移
		pPre = pCur;              //指向后一节点,即pCur
		pCur = pCur->next;        //指向后一节点,即pCur的下一节点
	}

	//while退出的两种情况
	//1.找到退出,pCur为匹配到的节点;pPre为匹配的前一节点
	//2.没找到末尾退出。 pCur为空节点NULL;pPre为最后一个节点

	//但是插入的代码是一样的
	SNode * pNew = (SNode*)malloc(sizeof(SNode));
	pNew->id = x;
	pNew->next = NULL;

	pPre->next = pNew;
	pNew->next = pCur;

	return 0;
}

3 删除节点
1)删除链表的所有节点。(当然会包括头结点)

//删除链表所有节点   需要一个保存下一个节点的临时节点tmp
int DelListall(SNode *head) {
	if (head == NULL) {
		cout << "链表为空." << endl;
		return -1;
	}

	//用于统计删除节点次数
	int i = 0;
	while (head != NULL) {
		SNode *tmp = head->next;
		free(head);
		head = tmp;
		i++;
	}

	//因为也删除头结点了,所以值为显示的节点加上头结点
	cout << "删除所有节点的次数:" << i << endl;

	return 0;
}

2)删除表中第一个值为x的节点。

void DelListone(SNode *head, int x) {
	SNode *pPre = head;
	SNode *pCur = head->next;

	//没找到为0,找到为1
	int flag = 0;

	//循环遍历查找
	while (pCur != NULL) {
		if (pCur->id == x) {
			pPre->next = pCur->next;
			free(pCur);
			pCur = NULL;
			cout << "找到并删除了:" << x << endl;
			flag = 1;
			break;
		}

		//没找到继续遍历
		pPre = pCur;     //或者PPre=pPre->next;
		pCur = pCur->next;
	}
	//不能用pCur==NULL为判断,因为他是被删除的节点,并不会循环到尾部,而且被删除后又置为NULL
	//所以用标志位
	if (flag == 0) {
		cout << "没找到:" << x << endl;
	}
}

3)删除链表中值为x的全部节点。(与删除一个点基本一样,不退出循环就行)

void DelListX(SNode *head, int x) {
	SNode *pPre = head;
	SNode *pCur = head->next;

	while (pCur != NULL) {
		if (pCur->id == x) {
			pPre->next = pCur->next;
			free(pCur);
			pCur = NULL;

			//break 删除一个节点时的代码

			//使pCur保持在pPre继续做判断
			pCur = pPre->next;

			//找到删除的点后,后面不需要节点后移,否则被删节点的后一节点就没做if判断
			continue;
		}

		//没找到就继续循环
		pPre = pPre->next;
		pCur = pCur->next;
	}
}

4)删除下标为n的节点。

void DelListPos(SNode* head, int n) {
	if (head == NULL) {
		return;
	}

	SNode* pPre = head;
	SNode* pCur = pPre->next;
	int count = 0;                      // 记录当前下标 用于和下标n比较
	int falg = 0;

	while (pCur != NULL) {

		//先判断
		if (count == n) {
			pPre->next = pCur->next;
			free(pCur);
			pCur = NULL;
			falg = 1;                    // 为1 说明下标n在链表范围内 否则就超过了 因为前面已经将小于0的排除
			//break; 用else逻辑好点
		}
		else {                         // 不等于就继续循环
			count++;
			pPre = pCur;
			pCur = pCur->next;
		}
	}

	//判断是否超出链表大小
	if (falg == 0) {
		cout << "下标n超过了链表的大小!" << endl;
	}
}

4 修改与查找值就不给出了,不难,都是遍历的问题。

5 链表的排序。(重要)

//链表排序
int SortList(SNode *head) {
#if 0
	//参考选择排序
	int a[] = { 2,5,1,7,6 };
	int len = sizeof(a) / sizeof(int);
	for (int i = 0; i < len - 1; i++) {
		for (int j = i + 1; j < len; j++) {
			if (a[i] < a[j]) {
				int tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
	for (int i = 0; i < len; i++) {
		cout << a[i];
	}
#endif
	if (head == NULL || head->next == NULL) {
		cout << "无数据可排序" << endl;
		return -1;
	}
	SNode *pData1;  //指向第一个有效数据
	SNode *pData2;  //指向第二个有效数据

	//第一个for从1有效节点循环到倒数第一个.  第二个for从第二个循环到最后一个节点
	for (pData1 = head->next; pData1->next != NULL; pData1 = pData1->next) {
		for (pData2 = pData1->next; pData2 != NULL; pData2 = pData2->next) {
			if (pData1->id > pData2->id) {
				//将节点的数据两两交换,注意不是改变指针指向
				SNode tmp = *pData1;
				*pData1 = *pData2;
				*pData2 = tmp;

				// 经过上面的赋值后,SNode 里面的next元素指向是调换了
				// 所以将指向调整回来
				tmp.next = pData1->next;// 先保存pData2->next的内容
				pData1->next = pData2->next;// 恢复pData1->next的指向
				pData2->next = tmp.next;// 恢复pData2->next的指向
			}
		}
	}
	return 0;
}

6 链表的翻转。

int ReverseList(SNode *head) {
	//如果空链表或者只有头结点或者头结点+一个有效节点就不用翻转
	if (head == NULL || head->next == NULL || head->next->next == NULL) {
		return -1;
	}

	SNode *pPre = head->next;
	SNode *pCur = pPre->next;
	SNode *tmp = NULL;

	while (pCur != NULL) {    //或者写pPre->next!=NULL
		//先保存pCur的下一节点再翻转
		tmp = pCur->next;
		pCur->next = pPre;

		//翻转后pPre、pCur往后移
		pPre = pCur;
		pCur = tmp;

	}

	//最后head的next和第一个节点的next指针没做处理 pPre此时是最后一个节点即翻转后的第一个节点
	head->next->next = NULL;   //使第一个有效节点指向NULL   
	head->next = pPre;         //使头结点指向最后节点

	//这两个顺序不能调换,必须第一个节点的next置为NULL先,否则只输出一个节点

	return 0;

}

7 最后就是链表的打印。

int PrintList(SNode *head) {

	if (head == NULL) {
		return -1;
	}
	//从有效数据第二个开始遍历
	SNode *pCur = head->next;

	cout << "head->";

	//循环遍历链表打印
	while (pCur != NULL) {

		cout << pCur->id << "->";
		pCur = pCur->next;

	}

	cout << "NULL" << endl;

	return 0;
}

总结上面的代码:
1)所有的操作基本上都是基于遍历链表来进行的。
2)这里只有链表的翻转和链表的排序用到三个临时节点。当然,如果你有更好的方法,用两个节点实现也是可以的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

链表的基本操作(增删改查) 的相关文章

  • 华为OD机试 - 生日礼物(Java)

    题目描述 小牛的孩子生日快要到了 他打算给孩子买蛋糕和小礼物 蛋糕和小礼物各买一个 他的预算不超过x元 蛋糕cake和小礼物gift都有多种价位的可供选择 请返回小牛共有多少种购买方案 输入描述 第一行表示cake的单价 以逗号分隔 第二行
  • C++新特性03_迭代器iterator及类型推导auto(迭代器:用于容器中数据遍历;动态数组(vector)和链表(list)遍历;堆上下限标志位;类型推导auto:编译时自动推导数据类型)

    迭代器iterator及类型推导auto 1 迭代器 用于容器中数据的遍历操作 1 1 普通数组与动态数组定义及遍历方式 1 1 1 数组 普通的数组 一旦申请 不能再扩增 1 1 2 动态数组 vector 不用指定其大小 会根据数组当前
  • 华为OD机试 - 解密犯罪时间(Java)

    题目描述 警察在侦破一个案件时 得到了线人给出的可能犯罪时间 形如 HH MM 表示的时刻 根据警察和线人的约定 为了隐蔽 该时间是修改过的 解密规则为 利用当前出现过的数字 构造下一个距离当前时间最近的时刻 则该时间为可能的犯罪时间 每个
  • 2023最新华为OD机试,独家整理总结上岸技巧,答读者问华为OD 华为OD机试备考攻略

    文章目录 华为OD在线刷题OJ 华为OD统一考试A卷 B卷 新题库说明 什么是华为OD 华为 OD 应聘流程 华为 od 机试 机试 分数 院校问题 华为 OD 机试 二本院校有机会吗 华为 OD 机试 跨专业可以参加华为OD 华为 OD
  • 使用c++超详细解释数据结构中的顺序栈和链栈

    在C 中 栈 Stack 是一种数据结构 它可以用来存储数据 并支持两种基本操作 压入 Push 和弹出 Pop 栈的特点是后进先出 Last In First Out LIFO 也就是最后压入的元素最先弹出 栈可以用数组或链表等数据结构来
  • leetcode算法面试题:对称二叉树、对链表进行插入排序、多数元素

    对称二叉树问题 给定一个二叉树 检查它是否是镜像对称的 例如 二叉树 1 2 2 3 4 4 3 是对称的 1 2 2 3 4 4 3 但是下面这个 1 2 2 null 3 null 3 则不是镜像对称的 1 2 2 3 3 参考答案 c
  • 剑指 Offer 18. 删除链表的节点 -- 双指针

    0 题目描述 leetcode原题链接 剑指 Offer 18 删除链表的节点 1 双指针解法 删除值为 val 的节点分需为两步 定位节点 修改引用 定位节点 遍历链表 直到 head val val 时跳出 即可定位目标节点 修改引用
  • 【力扣经典题目】链表的回文结构,赶快收藏起来

    题目描述 对于一个链表 请设计一个时间复杂度为O n 额外空间复杂度为O 1 的算法 判断其是否为回文结构 给定一个链表的头指针A 请返回一个bool值 代表其是否为回文结构 保证链表长度小于等于900 测试样例 1 gt 2 gt 2 g
  • 数据结构之数组

    目录 前言 线性表与连续内存 数组是如何支持随机访问 数组的插入与删除 数组越界 总结 参考文章 前言 数组是我们平时开发中经常遇到的一种数据结构 提起数组 我们能想到最大的特点就是 要提前定义好 需要提前申请好内存空间 数组是一种线性表数
  • 2020.11.13 奇偶链表

    2020 11 13 奇偶链表 题目描述 给定一个单链表 把所有的奇数节点和偶数节点分别排在一起 请注意 这里的奇数节点和偶数节点指的是节点编号的奇偶性 而不是节点的值的奇偶性 请尝试使用原地算法完成 你的算法的空间复杂度应为 O 1 时间
  • 夯实C++基础之刷题:链表——3合并两个有序列表

    题目 解题 递归和迭代 我的理解 递归是自己调用自己 迭代是按思路往下走 1 递归 class Solution public ListNode mergeTwoLists ListNode list1 ListNode list2 递归
  • 刷题之142. 环形链表 II

    给定一个链表的头节点 head 返回链表开始入环的第一个节点 如果链表无环 则返回 null 如果链表中有某个节点 可以通过连续跟踪 next 指针再次到达 则链表中存在环 为了表示给定链表中的环 评测系统内部使用整数 pos 来表示链表尾
  • 数据结构之双向链表,实现双向链表的增删改查

    目录 一 双向链表的定义 1 双向链表节点的定义 2 双向链表的初始化 二 双向链表的函数接口实现 1 双链表的尾插 2 双向链表的尾删 3 双向链表的头插 4 双向链表的头删 6 双向链表在pos前面插入 7 双向链表删除pos位置的节点
  • 算法题-简单系列-03-判断链表中是否有环

    文章目录 1 题目 1 1 思路1 双指针 1 2 思路2 哈希表 1 题目 判断给定的链表中是否有环 如果有环则返回true 否则返回false 1 1 思路1 双指针 我们使用两个指针 fast 与 slow 它们起始都位于链表的头部
  • 算法题-简单系列-01-链表反转

    文章目录 1 题目 1 1 使用栈解决 1 2 反转链表 1 题目 给定一个单链表的头结点pHead 该头节点是有值的 比如在下图 它的val是1 长度为n 反转该链表后 返回新链表的表头 如当输入链表 1 2 3 时 经反转后 原链表变为
  • 【数据结构/C++】树和二叉树_二叉链表

    include
  • 华为OD机试 Java 【最大载货量】

    描述 在火车站旁的货运站 小明负责调度2K辆中转车 其中K辆用于干货 K辆用于湿货 每批到站的货物来自不同的供货商 需要按照顺序装入中转车 注意 一个供货商的货物只能装在一辆车上 不能分开 但是 一辆车可以放多个供货商的货物 问题是 要让所
  • 华为OD机试 C++【最大载货量】

    描述 在火车站旁的货运站 小明负责调度2K辆中转车 其中K辆用于干货 K辆用于湿货 每批到站的货物来自不同的供货商 需要按照顺序装入中转车 注意 一个供货商的货物只能装在一辆车上 不能分开 但是 一辆车可以放多个供货商的货物 问题是 要让所
  • 《LeetCode力扣练习》代码随想录——双指针法(反转链表---Java)

    LeetCode力扣练习 代码随想录 双指针法 反转链表 Java 刷题思路来源于 代码随想录 206 反转链表 双指针 Definition for singly linked list public class ListNode int
  • 链表的中间节点

    链表的中间节点 力扣 LeetCode 官网 全球极客挚爱的技术成长平台 备战技术面试 力扣提供海量技术面试资源 帮助你高效提升编程技能 轻松拿下世界 IT 名企 Dream Offer https leetcode cn problems

随机推荐

  • 基于以太坊的USDT(基于ERC-20协议发行)

    这种USDT存储在以太坊地址上 相对应的 每次转账 链上转账 时 需要消耗Gas 也就是ETH 目前 市场上的USDT绝大部分是基于比特币的USDT 基于以太坊的USDT份额很低 约3 基于TRON网络TRC20协议的USDT 存储在TRO
  • 当心互联网抢了你的饭碗

    两年前 供职于帕洛阿尔托研究中心 Palo Alto Research Center 的学者布莱恩 亚瑟 Brian Arthur 做出了一项惊人预测 未来二三十年 西方数字网络履行的功能最终将相当于美国 实体 经济的规模 亚瑟写道 或者
  • Qt 信号和槽学习

    使用一个按钮按下时 我们可能想要窗口的 close 函数被调用 这个操作可以通过设置回调函数实现 但回调函数不够直观 而且容易出现参数类型错误等问题 Qt中使用的代替方案是信号和槽机制 信号和槽 当特定的事件出现时 一个信号被发出 槽函数作
  • 探索Java8——Lambda方法引用

    管中窥豹 方法引用让你可以重复使用现有的方法定义 并像Lambda一样传递它们 在一些情况下 比起使用Lambda表达式 它们似乎更易读 感觉也更自然 inventory sort Apple a1 Apple a2 gt a1 getWe
  • 数字孪生万亿市场显现,缺的不止是硬件落地

    数字孪生是从真实世界到虚拟世界的1 1映射 它通过控制虚拟世界中的生产过程和生产设备 模拟现实世界中的工业生产 更加注重 从虚拟到真实 工业元宇宙所反映的虚拟世界不仅具有现实世界的映射 而且具有现实世界中尚未实现甚至无法实现的体验和互动 这
  • Python实现发送邮件

    SMTP模块发送普通邮件 import smtplib from email mime text import MIMEText from email header import Header 发送方邮箱 msg from 授权码 pass
  • 实习周记1:跨vlan通信

    跨vlan通信 1 拓扑图 2 要求 不同vlan的两台pc通过二层交换机实现二层互通 3 命令 H3C GigabitEthernet2 0 1 port link type hybrid 把端口模式改为hydrid H3C Gigabi
  • Sybase服务无法启动

    刚刚改完数据库的最大连接数 重启服务时 却发现服务无法启动 找了大半天的原因 终于找到了 可惜不会弄 只好有网上搜索一下 发现这种问题还比较常见 服务起不来 在应用程序事件查看器中发现有如下错误 300122 The value of th
  • 工程师的基本素质

    昨天开会 印象最为深刻的是领导的这么一句话 工程师的基本素质 专注 专心 责任 放心 真的 我没有真正意识到并做到这些 所以对于人的准则的教育是必须的 要让人认识到 职责所在 用心做事 愧对自己 洗心革面矣
  • 【调制BFSK】二进制频移键控FSK的数字调制(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 相移键控 PS K 是利用载波相位直接表示
  • python给图片添加水印图片_python 给图片添加数字水印示例

    实例简介 实例截图 核心代码 coding utf 8 Spyder Editor This is a temporary script file from PIL import Image import numpy as np count
  • pycuda学习笔记(二)

    1 pycuda driver LogicError cuDeviceGet failed initialization error报错怎么办 Traceback most recent call last File usr lib pyt
  • 0-1背包问题:动态规划的经典应用

    文章目录 引言 背包问题简介 0 1背包问题定义 0 1背包问题的限制条件 动态规划解决思路 状态定义 状态转移方程 背包问题的Java实现 示例与分析 总结 引言 背包问题是在给定一组物品和一个背包容量的情况下 如何选择物品放入背包 以使
  • 人工智能里有至简的优雅吗?

    我很喜欢这首古诗 静夜思 唐 李白 床前明月光 疑是地上霜 举头望明月 低头思故乡 二十个简单汉字 勾画出生动的画面 并在无数人心里掀起波澜 物理学里 爱因斯坦用5个符号 简单到不能再简单 揭示了我们的世界的规律 目光转回我的本行 我在模式
  • 一文搞定全进程间通讯(IPC)八大方式-管道、命名管道、信号、信号量、消息队列、共享内存+内存映射、套接字

    目录 进程间通讯 IPC UNIX IPC 管道 Pipe 命名管道 FIFO 信号 Signal System V IPC 信号量 Semaphore 消息列队 Message Queue 共享内存 Shared Memory IPC 额
  • linux 中增加路由(route)命令详解

    linux route 命令 route n显示现在所有路由 root Ubuntu route 结果是自上而下 就是说 哪条在前面 哪条就有优先 前面都没有 就用最后一条default 添加一条路由 发往192 168 62这个网段的全部
  • 代码走查该走查什么

    代码走查在很多公司都是一个必要的过程 但是很多时候却时候一个形同虚设的过程 通常检查的同事只要保证你的代码能够编译通过 不出现问题就pass了 到底代码走查有没有一定的规范性呢 如果公司是严格按照开发流程来的话 那么代码走查可能也会是下图中
  • Java文档注释用法+JavaDoc的使用详解

    Java文档注释 JavaDoc的使用详解 简介 文档注释负责描述类 接口 方法 构造器 成员属性 可以被JDK提供的工具 javadoc 所解析 自动生成一套以网页文件形式体现该程序说明文档的注释 注意 文档注释必须写在类 接口 方法 构
  • 关于长连接的使用

    本次项目我尝试了使用长连接 先说一下长连接的作用吧 以前我们从数据库获取数据是只有我们前端触发某一事件去发请求 后端才会返回数据 也就是说必须有人为操作才能完成这一过程 但是对于websocket长连接来说 实现了前后端牵手 使得发送请求与
  • 链表的基本操作(增删改查)

    链表的基本操作 增删改查 前提 节点的类型 typedef struct Node int id struct Node next SNode 目录 1链表的创建 2 链表的插入 3 链表的删除 4 改 查 5 链表的排序 6 链表的翻转