《数据结构学习笔记---第五篇》---链表OJ练习上

2024-03-28 05:20

本文主要是介绍《数据结构学习笔记---第五篇》---链表OJ练习上,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

CM11链表分割

 OR36 链表的回文结构

 160.相交链表

 141&142环形链表

CM11链表分割

step1:思路分析 

1.首先可以想到,我们可以将原链表的元素划分到两个新的链表之中,由于必须保持顺序,所以新链表我们要用尾插。

2.为了方便进行尾插我们可以选择定义两个新的头结点。

step2:书写代码

class Partition {
public:ListNode* partition(ListNode* pHead, int x) {struct ListNode *LessPhead,*LessTail,*GreaterPhead,*GreadtTail;LessTail=LessPhead=( struct ListNode*) malloc(sizeof(struct ListNode));GreadtTail=GreaterPhead=( struct ListNode*) malloc(sizeof(struct ListNode));LessTail->next=GreadtTail->next= NULL;ListNode*cur=pHead;while(cur){if(cur->val<x){ListNode*pre1=cur;LessTail->next=pre1;LessTail=pre1;}else{   ListNode*pre2=cur;GreadtTail->next=pre2;GreadtTail=pre2;}cur=cur->next;}LessTail->next=GreaterPhead->next;GreadtTail->next= NULL;pHead=LessPhead->next;free(LessPhead);free(GreaterPhead);return  pHead;}
};

 OR36 链表的回文结构

step1:分析思路 

1.首先可以看到我们空间复杂度是有O(1)的要求的,所以不能开辟数组暴力解决。

2.我们想到回文结构是对称的,首先想到肯定是如何找到中间节点,之前写过求中间结点的代码,这次可以直接引用。

3.找到中间节点后,我们们可以让后续的翻转一下位置,又可以借用翻转链表的代码。

4.然后就是定义头和中间节点,遍历比对,实现判断。

step2:代码书写

struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}};#include <exception>
struct ListNode* middleNode(struct ListNode* head) {struct ListNode* cur1=head;struct ListNode* cur2=head;int count=0;int i=1;while(cur1->next){cur1=cur1->next;count++;}count++;while(i!=count/2+1){cur2=cur2->next;i++;}    return cur2;}
struct ListNode* reverseList(struct ListNode* head){//我的想法:第一种 建立一个新的链表头插struct ListNode* cur=head;struct ListNode* tail=NULL;while(cur){struct ListNode* next=cur->next; cur->next=tail;tail=cur;cur=next;}return tail;}
class PalindromeList {
public:bool chkPalindrome(ListNode* A) {struct ListNode*mid= middleNode(A);struct ListNode*rehead=reverseList(mid);while(A&&rehead){if(A->val!=rehead->val){return false;}rehead=rehead->next;A=A->next;}return true;}
};

 160.相交链表

 

step1:分析思路

我们很容易想到由于单链表的特性 不可能存在next指针指向两个位置,因此只能是二合一并且尾部是保持单链的情况。

1.尝试反向遍历,违反单链表特性。

2.用一个数组存储一个链表的元素然后和另一个链表元素进行比较,显然时间复杂度因该是

O(N)空间复杂度也变成了O(N)。可行

3.统计链表长度,然后计算长度差值,长表先走相应的差值使得两个指针能够同步走,对比第一次元素相等时,就是想要的结果。时复O(N)  空复O(1)

step2:代码书写

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode* cur1=headA;struct ListNode* cur2=headB;int count1=0;int count2=0;while(cur1->next){cur1=cur1->next;count1++;}count1++;while(cur2->next){cur2=cur2->next;count2++;}count2++;if(cur1!=cur2)//勿忽略{return NULL;}int gap=abs(count1-count2);struct ListNode*longlist=headA;struct ListNode*shortlist=headB;if(count1<count2){longlist=headB;shortlist=headA;}while(gap--){longlist=longlist->next;}while(longlist!=shortlist){longlist=longlist->next;shortlist=shortlist->next;}return shortlist;}

注意1:int gap=abs(count1-count2); //这是取了一个绝对值。

注意2:if(cur1!=cur2)//勿忽略
           {
                  return NULL;
            }
 

 141&142环形链表

step1:分析思路

判断是否有环

1.直接快慢指针,快指针会在环中兜圈会和慢指针相遇。

2.相遇的条件取决于两者的速度,这样就变成了一个数学的逻辑问题,如何制造相遇呢,假设slow一次走1步,fast一次走2步,入环前长度为L,环的长度为S,那么他们都差距步是一个2-1,4-2, 6-3,是一个顺序序列。那么当他们的差距步刚好为L+S时,就会相遇,所以如果有环他们迟早会相遇。

那么我们让fast一走3.4.5步又是什么情况呢,刚刚是通过差距步来判断的,现在同样如果fast一次走3步,那么差距步就是3-1,6-2,9-3是一个偶序列,最终如果(L+S )不是偶数或许永远不存在相遇。

判断环的入口

1.利用上面的参数,那么我们入环的入口就是L处,假设入口处与相遇处相差N

2. k是因为我们并不知道快指针走的圈数所以假设的值,但从图中我们可以得出相遇处到入口处S-N.

3.那么我们先走S-N将会到达入口处,其余走的都是整圈也就是我们定义一个meet再相遇处,一个origin在起始处,两者最终将会在入口处相遇。

step2:书写代码

判断是否有环


bool hasCycle(struct ListNode *head) {struct ListNode *fast=head;struct ListNode *slow=head;//如果没有环while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){return true;}}return false;}

判断环的入口


struct ListNode *detectCycle(struct ListNode *head) {struct ListNode *fast=head;struct ListNode *slow=head;//如果没有环while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){struct ListNode *meet=slow;struct ListNode *origin=head; while(meet!=origin){meet=meet->next;origin=origin->next;}return meet;}}return NULL;}

这篇关于《数据结构学习笔记---第五篇》---链表OJ练习上的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/854562

相关文章

C++链表的虚拟头节点实现细节及注意事项

《C++链表的虚拟头节点实现细节及注意事项》虚拟头节点是链表操作中极为实用的设计技巧,它通过在链表真实头部前添加一个特殊节点,有效简化边界条件处理,:本文主要介绍C++链表的虚拟头节点实现细节及注... 目录C++链表虚拟头节点(Dummy Head)一、虚拟头节点的本质与核心作用1. 定义2. 核心价值二

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定