前言 补充说明: 1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续 2、节点⼀般是从堆上申请的 3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续 本篇旨在实现链表的剩下的方法 while (pcur) { if (pcur->data == x) { return pcur; } pcur = pcur->next; } return NULL; } 2. 然而,链表的缺点是访问元素的时间复杂度为O(n),因为需要遍历整个链表。 链表常见的类型有单链表、双向链表和循环链表。 单链表:每个节点只有一个指向下一个节点的指针。 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。 循环链表:链表中最后一个节点的指针指向链表中的第一个节点。 链表的操作包括插入、删除和查找。 插入操作:将一个新的节点插入到链表的某个位置,需要修改前后节点的指针。 删除操作:删除链表中的某个节点,需要修改前后节点的指针。 查找操作:根据给定的值查找链表中的节点。
接着我们的第一篇文章,继续实现双向链表的方法。 这是我们定义好的双向链表的数据结构不要忘了: function TwoWayLinkList() { // 属性 this.head = null this.prev = null this.next = null } } append() 思路 双向链表与单向链表的区别是在头部和尾部都能找到我们的元素
在链表的使用中,除了无头单向不循环链表外,最常用的就是带头双向循环链表,相比单向链表具有更灵活的操作特性。 在之前的章节中已经讲解过了单链表,本文将从双向链表的基本概念出发,详细讲解其结构组成、基本操作实现、与单向链表的对比以及应用场景,帮助大家全面掌握双向链表这一数据结构。 一. 双向链表的结构和概念 (注:带头链表里的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这里“放哨 的” ) 双向链表,也叫双链表,是链表的一种。 这意味着,从双向链表中的任意一个节点出发,都可以很方便地访问它的前驱节点和后继节点,大大提高了链表操作的灵活性。 ,双向链表的节点结构更加复杂。
前言 本系列主要讲解链表的经典题 注:划重点!!必考~ 反转链表 力扣链接:206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com) 题目描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 示例: 提示: 链表中节点的数目范围是 [0, 5000] -5000 <= Node.val <= 5000 解题思路: 这里我们采用三指针进行反转链表: 指针cur进行遍历链表 指针next记录cur的下一个指针,防止反转时下一个节点地址丢失 (struct ListNode* head){ struct ListNode* prev=NULL,*cur=head,*next; while(cur)//cur为NULL时遍历链表结束
牛客网 链表的回文结构 https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa 思路 1、偶数情况: 2、奇数情况: 代码实现 head; } ListNode* n1, *n2, *n3; n1 = NULL; n2 = head; n3 = n2 ->next; while (n2) { n2->next = n1; n1 = n2; n2 = n3; 环形链表 | 思路 https://leetcode.cn/problems/linked-list-cycle/description/ slow⼀次走⼀步,fast⼀次走2步,fast先进环,假设slow 2、设置 radom 指针(注:这里未将所有的radom指针画完) 3、断开与原链表的链接(注:这里未将所有的radom指针画完) 代码实现 /** * Definition for a Node
链表的删除 ? 表的删除就是指针的移动,将待删除节点的指针移动到下一个节点 题1:删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。 ListNode next = node.next; node.val = next.val; node.next = next.next; } 链表的反转 题:反转一个单链表 链表的反转不需要不需要创建新的对象,整个过程也就是指针的移动 首先构建一个新的节点,将头结点与原来节点分离,然后重复前一个动作,将第二个节点从原来的节点分离,并加上新的节点上 ? 题:两个有序链表的合并 有序链表的合并,就是将另外一个链表的节点不断插入另一个节点 if(l1 == null || l2 ==null) { return l1 == null l1 : l2; ListNode cur1 = head == l1 ? l1 : l2; ListNode cur2 = head == l1 ?
} return slow; } }; 思路 简单说下思路,现在已经知道的条件: 快指针移动速度为慢指针两倍; 快指针要比慢指针多移动一圈; 可得: 假设 L 为链表入口到环入口的距离 , x 为环入口到相遇点的距离, C 为环的周长,可得: 2 \times (L + x) - (L + x) = C ,即 L + x = C ,可得: x = C - L ,即相遇点到环入口的距离与链表入口到环入口的距离相等 ,所以可以通过让慢指针回到链表入口,然后快慢指针同时一步一步地前进,最后相遇的地方就是环的入口。
. - 力扣(LeetCode) 第一种 先来讲下最简单的算法,创建一个新链表,将原链表的元素挨个头插到新链表上,就实现了顺序表的逆转,这里就不示例代码了,在之前的链表有提及。 第二种 可以试试将整个链表倒置,就是创建三个指针, 让n1指向空,n2指向头,n3指向head->next 接着进入循环,我们选不探讨循环条件,先研究循环语句,n2的next要存储n1,就是为了使最后的链表的 next的空,n1=n2,n2=n3,if(n3)n3=n3->, n1是整个链表,n2=n3是为了指向后面的元素,在将后面的元素插入到n1前面,然后再让n1是指向头节点,最后5就是新的头节点。 (head==NULL) { return head; } else { ListNode *n1,*n2, *n3; n1=NULL,n2=head,n3=n2->next; while(n2) { n2->next= n1; n1=n2; n2
10.环形链表2 142. 环形链表 II - 力扣(LeetCode) /* 解题思路: 如果链表存在环,则fast和slow会在环内相遇,定义相遇点到入口点的距离为X,定义环的长度为C,定义头到入口的距离为L,fast在slow 进入环之后一圈内追上slow,则会得知: slow所走的步数为:L + X fast所走的步数为:L + X + N * C 并且fast所走的步数为slow的两倍,故: 2*(L + X) = L +
2-6 链表逆序 我只介绍两种常用方法吧,非递归方法 和 递归 方法 我觉得够用就行 1、非递归方法: 将第二个元素后面的元素依次插入到头结点后面, 最后再把原始第一个元素放到原始第二个元素后面,整个链表就能够反转了 P2的顺序 p2->next = p1; (*h) =p1->next; p1->next = nullptr; } } ②带头结点的链表 可以将头结点视为第一个元素,那么就是直接把 A 的后继元素不断的往head后面插: 带头结点原始链表,将头结点视为第1个元素,那么其中第2个元素是 A Head -> A -> B -> C -> D -> E -> F -> null 先进入循环, ( list1_h ); 这样新的头指针list_2就是反转后的链表的头指针 ②带头结点 其实依然可以用上面的函数,只是, 对于带头结点的链表,直接向上面那样 把 头结点的地址作为参数传递进去 是不行的 所以我们改一下调用的那行代码,就可以拿来对带头结点的单链表 进行逆序操作了: list2->next = ReverseList_DG(list2->next) 上面这行代码,是把带头结点的单链表的下一个元素
问题描述: 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 说明: 1 ≤ m ≤ n ≤ 链表长度。 示例: 输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL class ListNode: def __init__(self ,x): self.val=x self.next=None n1=ListNode(1) n2=ListNode(2) n3=ListNode(3) n4=ListNode (4) n5=ListNode(5) n1.next=n2;n2.next=n3;n3.next=n4;n4.next=n5 def printListNode(head): while head 之后定义两个链接指针,p1,p2 ? 进入第二个while循环: 第一步: ? 第二步: ? 第三步: ? 此时退出循环。 然后p1.next指向prev,p2.next指向cur ?
合并两个有序链表 - 力扣(LeetCode) (leetcode-cn.com) 思路:从头开始取两个链表中小的那个尾插到新链表。 { //如果有一个链表是空的,那么直接返回另个一个链表 if(l1 == NULL) { return l2; } if(l2 == NULL) = NULL && l2 ! = NULL) { //如果取出来的值l1的小于l2的 if(l1->val < l2->val) { //如果新链表是第一次插入 l2 = l2->next; } } //循环结束 //如果链表l1或者链表l2其中的一个还有元素,那么就直接插到后面 if(l1
给定一个单链表 L_1→L_2→⋯→L_{n−1}→L_n,请编写程序将链表重新排列为 L_n →L_1 →L_{n-1}→L_2→⋯。 题目保证给出的链表上至少有两个结点。 输出格式: 对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。 68237 6 00100 00100 1 99999 99999 5 12309 12309 2 00000 00000 4 33218 33218 3 -1 分析: 用数组模拟链表就行,我的做法是先遍历一遍链表给每个节点编一个号 (1,2,3,4…),然后按照规律重新连接链表,规律就是最左边和最右边连,然后最右边和次左边连,有一个奇偶关系,连接从外向内推进,用一个循环就行。 此题有个坑, 可能有多余节点,即不在链表上的节点,所以不能直接用题目的n表示总节点数,应该用遍历链表后的编号最大值表示节点数。
一.双向循环链表 #include <stdio.h> #include <stdlib.h> // 双向循环链表数据节点 typedef struct node { int data; // 数据域 \n"); return; } // 2.操作pos的前后节点,使他们联系起来。 内核链表 #include <stdio.h> #include <stdlib.h> #include "kernel_list.h" // 内核链表节点结构体(大结构体) typedef struct 返回堆空间 return p; } 2。 内核链表_奇升偶降重排 #include <stdio.h> #include <stdlib.h> #include "kernel_list.h" // 内核链表节点结构体(大结构体) typedef
快慢指针,如若我让快指针先走k步,走完了再让慢指针走,此刻快慢指针就差k,双指针同时遍历,直到快指针走完,此刻慢指针返回的就是倒数第k的节点,所以一定要确保俩指针要差k 代码如下: 2.链表的回文结构 再反向一下以中间节点为首的之后的节点,然后中间指针与首指针遍历判断值是否相等,如图所示,这里有人有疑问,偶数个接点还可以看,但奇数个接点那,如图所示那个三,其实,再反转时,我们没有消除第一个二指向三的指针,所以两个2此刻都指向三 链表专题一博客链接:http://t.csdnimg.cn/zM8BB 好了整体总结一下 1.创建返回中间节点函数 2.创建反向链表函数,返回头结点 3.遍历原链表与函数返回的链表判断 代码如下 : 3.相交链表 首先我们要想一点,什么是链表相交 首先看一个图 这种可不是链表的相交 这种是 也就是说链表相交,是两个线合成一个线 为什么这种不可以,因为链表一个节点怎么可能会同时指向两个节点, : 判断完之后就可以,让谁先遍历差值,再一起遍历,一个一个判断是否相等就行了 这道题代码如下 结束语 链表专题2就结束了,还有几道典型的链表专题就放在下片博客说了 OK,感谢观看!!!
第二篇:JavaScript 数据结构(2-1):栈与队列-栈篇 第三篇:JavaScript 数据结构(2-2):栈与队列-队列篇 第四篇:JavaScript数据结构(3-1):单向链表与双向链表— —单向链表篇 从单链表到双链表 我们已经完整的实现了单链表,这真是极好的。 方法2/3 searchNodeAt(position) searchNodeAt(position)的实现与单链表相同。 0 || position < 1 || position > length) { throw new Error(message.failure); } // 2nd this.head) { this.head.previous = null; // 2nd use-case: there is no second node
1,先建实体类LinkNode类和实体类LinkList类; LinkNode:包括链表结点的数据域和指针域; 数据域是Object类型的,指针域是LinkNode类型的 LinkList:包括链表的头结点和链表元素个数 ; 头结点是LinkNode类型的,链表元素个数是int型的 LinkNode: package com.java.model; public class LinkNode { //链表结点的数据域 LinkList(LinkNode head, int size) { super(); this.head = head; this.size = size; } } 2, , 0, "A"); linkListDao.Insert_LinkList(list, 1, "B"); linkListDao.Insert_LinkList(list, 2, "C ); //删除指定位置的值 linkListDao.RemoveByPos_LinkList(list, 2); //打印链表 System.out.println
单链表上基本操作的实现 在实现单链表上基本操作之前,首先说一下我是基于什么样的单链表: 带头节点 头结点的数据域记录表长 初始化表 构造一个空的单链表。 查找插入位置的前驱结点 s = LinkList() s.data = e s.next = p.next # 1 p.next = s # 2 self.data += 1 算法中,语句 1 和语句 2 的顺序不能颠倒,否则,当先执行 p.next = s 后,指向其原后继的指针就不存在,再执行 s.next = p.next 双链表 单链表结点中只有一个指向其后继的指针,使得单链表只能从头结点依次顺序地向后遍历。 在单链表中只能从表头结点开始往后遍历整个链表,而循环单链表可以从表中的任意一个结点开始遍历整个链表。
上一回,我讲了一下链表的定义和基本操作的实现;这一会我们来看一下链表相关的一个典型应用:一元多项式!一元多项式的定义 形如 an*x^n+...+a1*x+a0 的表达式称之为一元多项式。 因为考虑到存在合并同类项的操作,合并同类项经常需要删除已经参与合并的元素,而顺序表删除元素可能需要大量移动元素,而链表不需要,所以我们选择链式存储结构。 下面我就来说一下我选择的是什么样的链式存储结构,我选择带头结点(头结点没有信息)的单链表。 这两步不能颠倒顺序,因为考虑到多项式 2*x+x^2+(-2)*x,如果先删去系数为 0 的项,再执行合并同类项,会使得这个多项式变成 0*x+x^2,这显然不是最简。 print('减:', p1-p2) print('乘:', p1*p2) print('除一个数:', p1/2) print('n 次方:', p1**2)
复习链表的插入 链表的一个节点是由数据域和指针域构成,指针域的地址值为下个元素的地址。那么我们需要插入或者删除一个元素怎么处理呢? ? 先查看原始链表结构,准备将结点x插入链表中。 ? 我们可以先思考导致空链表不能使用第一种方案的原因,因为它没有结点,我们自然无法获取其地址,所以采用增加一个头结点,那么此时空链表的结构如下图4,非空链表结构如下图5. ? ? 复习链表的删除 上面简单介绍了带头结点的链表,在删除处理的时候同样适用,所以我们以后就直接采用带头结点的链表讲解。下面直接看看删除节点图。 ? 说明: 链表至少包含两个节点。 链表中所有节点的值都是唯一的。 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 不要从你的函数中返回任何结果。 先思考一分钟哟! 效果更好哈! 2 python版本 ? 3 java版本 ?