当前位置: 首页 > news >正文

day03-链表part1

目录

一、链表基础知识

1.概念

2.链表的分类

3.单链表定义

4.链表基本操作

二、移除链表元素

三、设计链表

四、反转链表


一、链表基础知识

1.概念

链表是一种依靠指针连接起来的一种在存储上非连续的数据结构,由结点相连。结点包含两部分,数据部分和指针部分。

2.链表的分类

  • 单链表

  • 双链表

  • 循环链表

3.单链表定义

// 单链表
struct ListNode {int val;  // 节点上存储的元素ListNode *next;  // 指向下一个节点的指针ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

4.链表基本操作

  • 查询、搜索
  • 插入
  • 删除

二、移除链表元素

leetcode 203

在进行链表移除元素操作时,分为两种情况:

  1. 直接用head作为头指针,此时head需要特殊处理,因为head前面没有节点了,这种处理不是特别推荐,因为相对较为繁琐。参考下面的代码,同时注意删除链表的指针变化(删除时一定要找到要删除节点的前驱):
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {//先处理头节点(因为头节点没有前节点)while(head != nullptr && head->val == val){ListNode *q = head;head = head->next;delete q; }//再处理其他节点ListNode *p = head; //通过节点p遍历单链表while(p != nullptr && p->next != nullptr){if(p->next->val == val){ListNode *q = p->next;p->next = q->next;delete q;q=nullptr;}else{p = p->next;}}return head;}
};

2.设置虚拟节点指向头结点,这样就可以统一处理,不用分类讨论。参考代码如下:

class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作ListNode* cur = dummyHead;while (cur->next != NULL) {if(cur->next->val == val) {ListNode* tmp = cur->next;cur->next = cur->next->next;delete tmp;} else {cur = cur->next;}}head = dummyHead->next;delete dummyHead;return head;}
};

三、设计链表

leetcode 707

设计链表设计删除、插入指定值的结点,或者删除、插入指定索引的节点,其核心思想就是要找到待删除节点或插入节点的前驱,然后进行指针变换即可。(这里采用的仍然是虚拟头结点的方法)

有一个要注意的点是,用来遍历链表的指针q初始值是p(虚拟头结点),还是head。通常要指向要找的结点,q的初始值是head,而要指向待找结点的前驱,其初始值为p。

在初始化链表时,还设置了size用来记录链表长度,注意插入或删除时,size的变化。参考代码如下:

class MyLinkedList {
public:struct ListNode{int val;ListNode *next;ListNode(int x):val(x),next(nullptr){}};MyLinkedList() {//用来设置不存放数据的头节点和链表长度p = new ListNode(0);size = 0;}int get(int index) {if(index<0 || index>size-1) return -1;ListNode *q = p->next;while(index--){q = q->next;}return q->val;}void addAtHead(int val) {ListNode *q = new ListNode(val);//注意p是不存放数据的头节点q->next = p->next;p->next = q;size++;//注意长度}void addAtTail(int val) {ListNode *t = new ListNode(val);ListNode *q = p;while(q->next!=nullptr){q=q->next;}q->next = t;size++;}void addAtIndex(int index, int val) {if(index>size) return ;else if(index==size) addAtTail(val);else{ListNode *t = new ListNode(val);ListNode *q = p;while(index--){q=q->next;}t->next = q->next;q->next = t;size++;}}void deleteAtIndex(int index) {if(index <0 || index>size-1) return ;ListNode *q = p;while(index--){q = q->next;}ListNode *t = q->next;q->next = t->next;delete t;t = nullptr;size--;}private:ListNode* p;int size;
};/*** Your MyLinkedList object will be instantiated and called as such:* MyLinkedList* obj = new MyLinkedList();* int param_1 = obj->get(index);* obj->addAtHead(val);* obj->addAtTail(val);* obj->addAtIndex(index,val);* obj->deleteAtIndex(index);*/

四、反转链表

leetcode 206

这道题有多种方法

1.双指针法

通过双指针直接实现链表中指针的反转

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode *cur = head;ListNode *pre = NULL;while(cur){LinkList *t = cur->next;cur ->next = pre;pre = cur;cur = t;}return pre; // 返回反转后的头结点}
};

2.递归法

递归法与双指针法类似

class Solution {
public:ListNode* reverse(ListNode* pre,ListNode* cur){if(cur == NULL) return pre;ListNode* temp = cur->next;cur->next = pre;// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步// pre = cur;// cur = temp;return reverse(cur,temp);}ListNode* reverseList(ListNode* head) {// 和双指针法初始化是一样的逻辑// ListNode* cur = head;// ListNode* pre = NULL;return reverse(NULL, head);}};

3.头插法

从第二个元素开始,将每个结点插入到虚拟头结点后面,从而实现反转,参考代码如下,注意头指针或首结点为空的情况和指针变换:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {if(head==nullptr || head->next==nullptr) return head;ListNode *p = new ListNode(0);p->next = head;ListNode *q = head->next;head->next = nullptr;while(q!=nullptr){ListNode *t = new ListNode(q->val);t->next = head;p->next = t;head = t;q=q->next;} return head;}
};

4.尾插法

从首结点开始,全部插入到链表最后,尾插法通常用于有一指针指向链表尾节点的情况,这里用尾插法其实会增大时间复杂度,因此只给大家提供一个思路

http://www.dtcms.com/a/275306.html

相关文章:

  • 秋招笔试考什么?如何针对性去练习?
  • 10. 垃圾回收的算法
  • python excel处理
  • 实用技巧 Excel 与 XML互转
  • Go 设计模式 - 组合复用
  • 校园幸运抽(抽奖系统)测试报告
  • Agent 设计模式
  • 大模型KV缓存量化误差补偿机制:提升推理效率的关键技术
  • 【设计模式】外观模式(门面模式)
  • pdf合并
  • Git系列--4.Git分支设计规范
  • 计算机视觉 之 经典模型汇总
  • QT跨平台应用程序开发框架(6)—— 常用显示类控件
  • 【电脑】硬盘驱动器(HDD)的基础知识
  • Qt 多线程编程:单例任务队列的设计与实现
  • Spring Ai Alibaba Gateway 实现存量应用转 MCP 工具
  • 力扣 hot100 Day42
  • AI 时代的分布式多模态数据处理实践:我的 ODPS 实践之旅、思考与展望
  • RabbitMQ面试精讲 Day 1:RabbitMQ核心概念与架构设计
  • ntfs - SELinux
  • Vue框架之钩子函数详解
  • 路由双向引入实验:将一种路由协议学习到的路由注入另一种协议中
  • Linux中的git命令
  • Spring Cloud Gateway中常见的过滤器
  • 【kubernetes】--controller(DaemonSet)
  • Git入门教程
  • 【离线数仓项目】——电商域DIM层开发实战
  • 【一起来学AI大模型】RAG系统流程:查询→向量化→检索→生成
  • 医疗AI前端开发中的常见问题分析和解决方法
  • OpenCL study - code02