当前位置: 首页 > 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.lryc.cn/news/586383.html

相关文章:

  • C++类模版1
  • HTTP和HTTPS部分知识点
  • JAVA开发
  • 【数据结构初阶】--顺序表(三)
  • 广东省省考备考(第四十三天7.12)——数量(第四节课)
  • kettle从入门到精通 第101课 ETL之kettle DolphinScheduler调度kettle
  • 亚矩阵云手机:重构物流供应链,让跨境包裹“飞”得更快更准
  • 配置驱动开发:初探零代码构建嵌入式软件配置工具
  • ESP32使用freertos更新lvgl控件内容
  • TDengine 使用最佳实践(1)
  • Cell2location maps fine-grained cell types in spatial transcriptomics 文章解析
  • 全局唯一id生成
  • JavaScript加强篇——第七章 浏览器对象与存储要点
  • 深度学习-卷积化
  • 深入详解:决策树在医学影像领域心脏疾病诊断的应用及实现细节
  • Vue框架之钩子函数详解
  • ngrok使用
  • 企业商业秘密保卫战:经营信息类案件维权全攻略
  • 第三章第三节 GPIO 输入
  • Unity开发中常用的洗牌算法
  • 程序改错---字符串
  • 【离线数仓项目】——电商域DIM层开发实战
  • [特殊字符] 实时数据洪流突围战:Flink+Paimon实现毫秒级分析的架构革命(附压测报告)——日均百亿级数据处理成本降低60%的工业级方案
  • Spring Boot 2.4+中bootstrap.yml加载顺序的源码深度解析
  • 北京高铁3h可达城市周末游攻略
  • 堆内存的详细结构以及java中内存溢出和排查方式
  • 大模型量化相关
  • 钉钉企业应用开发实战:从零构建组织级业务工具
  • cuDNN 的 IMPLICIT_GEMM 算法
  • bp使用爆破模块破解pikachu的登陆密码