链表总结

一、单链表结点的删除

0、删除单链表p指向的那个元素,(时间和空间复杂度尽量小)

二、单链表的存取

1、找出单链表的倒数第K个元素,(仅允许遍历一遍链表)

2、找出单链表的中间元素,(仅允许遍历一遍链表)

三、单链表与环的问题

3、判断单链表是否有环(6形状)?

4、如何找到环的入口?

5、如何知道环的长度?

6、带环链表的长度是多少?

四、单链表与相交、环的问题

7、如何知道两个单链表(无环)是否相交

8、如果两个单链表(无环)相交,如何知道它们相交的第一个节点是什么

9、如何知道两个单链表(有环)是否相交

10、如果两个单链表(有环)相交,如何知道它们相交的第一个节点是什么

~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~

一、单链表结点的删除

0、删除单链表p指向的那个元素,(时间和空间复杂度尽量小)

思路:把q指向结点的值 赋给 p指向的结点,再把q指向结点删除

二、单链表的存取

1、找出单链表的倒数第K个元素,(仅允许遍历一遍链表)

思路:使用指针追赶的方法。定义两个指针fast和slow,fast先走K步,然后从这时开始,fast和slow同时继续走,即fast和slow相差K个元素。当fast到头时,slow指向倒数第K个。注意要考虑链表长度应该大于K。

2、找出单链表的中间元素,(仅允许遍历一遍链表)

思路:使用指针追赶的方法。使用两个指针fast和slow,只是fast每次走一步,slow每次走两步。当fast到头时,slow指向链表的中间元素

三、单链表与环的问题

3、判断单链表是否有环(6形状)?

思路:使用指针追赶的方法,设定两个指针fast、slow,从头指针开始,每次分别前进1步、2步。如存在环,则两者相遇;如不存在环,fast遇到NULL退出。

[cpp] view
plain
copy

  1. while(直到步长2的跑到结尾)
  2. {
  3. 检查两个指针是否相等,直到找到为止。
  4. }

4、如何找到环的入口?

图说明:如上图,h是链表起始点,s是环入口,p是两个指针碰撞点。r  = x + y

可以证明, a = y + mr  (头指针 到 环入口的距离 = 碰撞点p 到 环入口的距离
  +  循环多次环 )

附录处有证明

思路:令两个指针分别从第一个结点、碰撞点开始走,每一次走一步,直到两指针相遇,此时相遇的那个点就是环入口

5、如何知道环的长度?

思路:记录下问题4的碰撞点p(或者找在环中任意一结点都可以),让slow从碰撞点开始,绕着环走一圈,再次到碰撞点的位置时,所走过的结点数就是环的长度s。(一个指针从起始点出发走一圈,再次到起始点,就是走了环一圈)

6、带环链表的长度是多少?

思路:带环链表长度 = a + x + y = 链表起始点h 到 环起始点s的距离 + 环的长度 。在第4题中可以找到a的值,在第5题可以找到
环的长度(x+y)的值

四、单链表与相交、环的问题

7、如何知道两个单链表(无环)是否相交

思路:

1、判断两链表最后一个节点是否相同,如果相交,则尾节点肯定是同一节点。

时间复杂度O((length(A)+ length(B))、空间复杂度 = O(1)(存储最后结点的地址)

2、人为构环,如上图。将链表A的尾节点指向链表B,如果B链表有环,则两个链表相交,此时从链表B的头指针往下遍历,如果能够回到B,则说明相交

时间复杂度O((length(A)+ length(B)),没有额外的空间消耗

8、如果两个单链表(无环)相交,如何知道它们相交的第一个节点是什么

思路:

1、先取得两个链表A和B的长度len(A)和len(B)

2、沿着A和B链表中较长的链表遍历,使用指针追赶的方法。

设定两个指针fast、slow。fast先出发前进(lengthMax-lengthMin)步(即是二者的长度之差)使fast和slow指针到相交点的距离相等,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。

9、如何知道两个单链表(可能有环)是否相交

思路:根据两个链表是否有环来分别处理

1、如果两个链表都没有环。可用 问题7 来判断

2、一个有环,一个没环。肯定不相交

3、两个都有环。(注,两个有环链表相交是指这个环属于两个链表共有)

具体方法:如果两个都有环,则求出A的环入口,判断其是否在B链表上,如果在,则相交。

或者,在A链表上,使用指针追赶的方法,找到两个指针碰撞点,之后判断碰撞点是否在B链表上。如果在,则相交 。

10、如果两个单链表(有环)相交,如何知道它们相交的第一个节点是什么

(注,两个有环链表相交是指这个环属于两个链表共有)

情况1:相交的点,在环外

思路:使用指针追赶的方法。

1、求两个链表A和B的长度Length(A)和Length(B)

2、如果Length(A)> Length(B),则链表A指针先走 Length(A)-  Length(B),链表B指针再开始走,则两个指针相遇的位置就是相交的第一个节点。

      如果 Length(B)> Length(A),则链表B指针先走 Length(B)- Length(A),链表A指针再开始走,则两个指针相遇的位置就是相交的第一个节点。

情况2(图2)、相交的点,在环内,还不能处理

分析:当交点在环中时,此时的交点可以是A链表中的环入口点,也可以是B链表中环入口点。

为什么这么说?这是因为如果把B看出一个完整的链表,而A指向了B链表,则此时交点是A的环入口点。反之交点是链表B的环入口点。

思路:根据上述分析,可以直接求出A的环入口点或者B的环入口点就可以了。

综合这两种情况,给出两个方法求出结点

方法一、先检测交点是否在第一种情况中。如果是,则直接输出该结点。如果不是,则直接输出任意一个环交点。

方法二、使用哈希,只需要把链表A或者B所有的元素全部放入哈希表中,之后把B链表或者A链表中的每一个元素去哈希探测,即可找到交点。

附录1、

4、如何找到环的入口?

图说明:如图,h是链表起始点,s是环入口,p是两个指针碰撞点。r表示环的长度,r = x+y

可以证明, a = y + mr  (头指针 到 环入口的距离 = 碰撞点p 到 环入口的距离   +  循环多次环 )

证明:

当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。

假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈)。

设环长为r,则:

2s = s + nr,即s=
nr

设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。

s = nr

a + x = nr

a + x = (n – 1)r +r = (n-1)r + r

a = (n-1)r + r - x

a = (n-1)r + y

由此可知,从链表头到环入口点等于(n-1)循环内环+ 相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 13:45:47

链表总结的相关文章

c语言动态链表的创建

创建动态连链表就是将一个个节点连接起来 (1)动态生成节点 (2)输入节点数据 (3)将节点链在一起 例: typedef struct Data { char num[20]; char name[10]; char sex; float english; float chinese; float math; }; typedef struct Node { struct Data data;//结构体类型//结构体嵌套 struct Node* next;//结构体指针型 }node,*Pn

算法学习——单链表快排

/**  * 以p为轴对start-end间的节点进行快排(包括start && 不包括end):  * 思路:  * 1.将头节点作为轴节点start,从start.next开始遍历,如果节点小于轴start的值,将该节点插入到轴节点后面:  * 2.将轴节点插入合适位置,即找到最后一个小于轴的节点,将该节点与轴节点值互换,此时就链表分为两部分,小于轴节点和大于轴节点:  * 3.递归的遍历2中两部分节点.  *   * @param p  * @param start  * @para

【数据结构】之散列链表(Java语言描述)

散列链表,在JDK中的API实现是 HashMap 类. 为什么HashMap被称为"散列链表"?这与HashMap的内部存储结构有关.下面将根据源码进行分析. 首先要说的是,HashMap中维护着的是一个数组: transient Node<K,V>[] table; ,数组中的每个元素都是一个 Node 对象.这里的Node是HashMap的一个内部类,代码如下: static class Node<K,V> implements Map.Entry<

单链表逆置

重写单链表逆置,熟能生巧- #include <iostream> #include <cstdlib> using namespace std; typedef struct List{ int num; struct List *next; }ListNode,*pListNode; void display(ListNode *pHead) { while(pHead) { cout<<pHead->num<<"--"; pH

java-------单链表

单链表: * 1.链表可以是一种有序或无序的列表 * 2.链表的内容通常存储在内存中分散的为止 * 3.链表由节点组成,每一个节点具有相同的结构 * 4.节点分为数据域和链域,数据域存放节点内容,链域存放下一个节点的指针 package myLinkList; public class MyLinkedList<T> { /** *Node:节点对象 * 包括数据域data和链域next(指向下一个节点对象) */ class Node { private T data; private No

数据结构之链表

---恢复内容开始--- 1:有头节点.单向.不循环链表 对于这种有头节点的单向不循环链表插入数据如下图: 1)头部插入 2)尾部插入 代码如下 : 1 #include "stdafx.h" 2 #include<iostream> 3 4 using namespace std; 5 6 typedef int DATA; 7 typedef struct node* LIST; 8 9 struct node 10 { 11 DATA data; 12 struct

拿java写了一个有点像指针的单链表

public class LinkList { private Node firstNode; private Integer position; public LinkList() {  super(); } public LinkList(Node firstNode) {  super();  this.firstNode = firstNode; } public Node getFirstNode() {  return firstNode; } public void setFirs

02 单链表

线性表之链式存储---单链表 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 // 数据结构 6 typedef struct node 7 { 8 int data; 9 struct node *next; 10 }linkList; 11 12 // 创建单链表,并初始化 13 linkList *linkList_init(void) 14 { 15 linkList *l

(单链表)单链表的整体逆序和局部逆序

题目一:将单链表翻转. 思路:有三种方式. 一:用数组存储单链表的值,然后重新逆序赋值,效率较低. 二:利用三个指针,在原来的基础上进行逆序.这种方法比较实用,效率也高. 三:从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,最后将第一个节点挪到新表的表尾.需要新建一个链表,这种方法和第二种差不多. 这里我就写出第二种方法,比较实用. 代码(方法二): struct ListNode { int val; ListNode *next; ListNode(int x) :

链表操作法则之逆向遍历与倒置算法

一.创建链表: 对链表进行操作的所有算法的前提,就是我们首先要创建一个链表,我们可以选择正向建链和逆向建链: (一).正向建链: 首先,我们得自定义节点类型: typedef struct Node { int data;//数据域 struct Node * pNext;//指针域 }NODE,*PNODE; 通过数组进行链表数据域的赋值: int main (void) { PNODE pHead;//头指针,接收创建链表时返回的头结点地址 int a[8] = {12,37,49,65,2