文章目录

  • 1 链表
    • 1.1 常见题型及解题策略
      • 1.1.1 LeetCode中关于链表的题目有以下五种类型题:
      • 1.1.2 解题策略
    • 1.2 链表的基本内容
      • 1.2.1 链表的基本结构:
      • 1.2.2 插入新元素
      • 1.2.3 删除某个元素
      • 1.2.4 遍历单链表
    • 1.3 删除链表结点类题目
      • 1.3.1 题解方法
      • 1.3.2 可能出现的问题
      • 1.3.3 题库列表:
        • 237、删除链表中的节点
        • 203、移除链表元素
        • 剑指 Offer 18. 删除链表的节点
        • 面试题 02.01. 移除重复节点
        • 82. 删除排序链表中的重复元素 II
        • 19、删除链表的倒数第 N 个结点
        • 876、链表的中间结点
        • 86、分隔链表
        • 328、奇偶链表(分割链表的变形)
    • 1.4 反转链表节点类题目
      • 1.4.1 动图解释,双指针修改指针的方向
      • 1.4.2 题库列表
        • 206、反转链表
        • 92、反转链表 II
        • 234、 回文链表
        • 25. K 个一组翻转链表
    • 1.5 合并链表
      • 1.5.1 题库列表
        • 21、合并两个有序链表
        • 23、合并K个升序链表
    • 1.6 排序链表
      • 1.6.1 题库列表
        • 147、对链表进行插入排序
        • 148、排序链表:归并法
    • 1.7 环形链表
      • 1.7.1 题库列表
        • 160、相交链表
        • 141、环形链表
        • 142、环形链表II

1 链表

链表是最基本的数据结构,面试官常常用链表来考察面试者的基本能力,而且链表相关的操作相对而言比较简单,也适合考察写代码的能力。链表的操作也离不开指针,指针又很容易导致出错。综合多方面的原因,链表题目在面试中占据着很重要的地位。

以下内容思路主要参考:算法面试题 | 链表问题总结

1.1 常见题型及解题策略

1.1.1 LeetCode中关于链表的题目有以下五种类型题:

  • 删除链表节点
  • 反转链表
  • 合并链表
  • 排序链表
  • 环形链表


1.1.2 解题策略

  • dummy虚拟头节点,专门处理头结点可能会被改动的情况
  • 快慢双指针

1.2 链表的基本内容

推荐一篇文章:基础知识讲的很清楚,Java数据结构与算法之链表

链表的分类:单链表(分带头结点和不带头结点的单链表,就是head里面有没有data的区别)、双向链表、循环链表

重点理解指针的概念

如下代码 ans.next 指向什么?ans = ListNode(1)
ans.next = head   //ans.next 指向取决于最后切断 ans.next 指向的地方在哪,所以ans.next指向head
head = head.next  //ans 和 head 被切断联系了
head = head.next

ans = ListNode(1)
head = ans// ans和head共进退
head.next = ListNode(3)
head.next = ListNode(4)
// ans.next 指向什么?ListNode(3)
ans = ListNode(1)
head = ans           //head 和 ans共进退
head.next = ListNode(3)
head = ListNode(2)   //head 和 ans 的关系就被切断了
head.next = ListNode(4)

居然找到人跟我一样卡在这里了,笑死

1.2.1 链表的基本结构:

* public class ListNode {*     int val;
*     ListNode next;
*     ListNode() {}
*     ListNode(int val) { this.val = val; }
*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }

1.2.2 插入新元素

//0.找到要插入的位置
temp = 待插入位置的前驱节点.next       //1.先用一个临时节点把 待插位置后面的内容先存起来
待插入位置的前驱节点.next = 待插入指针  //2.将新元素插入
待插入指针.next = temp           //3.再把后面的元素接到新元素的next

1.2.3 删除某个元素

待删除位置的前驱节点.next = 待删除位置的前驱节点.next.next

1.2.4 遍历单链表

当前指针 =  头指针
while 当前节点不为空 {print(当前节点)当前指针 = 当前指针.next
}for (ListNode cur = head; cur != null; cur = cur.next) {print(cur.val)
}

1.3 删除链表结点类题目

1.3.1 题解方法

  1. 画草图:理解指针的变动与思考逻辑!!(重要!实用!)
  2. 边界条件:怎么处理不会有空指针异常?在循环里放什么停止条件
  • 如果是遍历链表元素,while(node!=null)
  • 如果是删除某个元素,需要,while(node.next!=null)
  • 需要考虑的仅仅是被改变 next 指针的部分,并且循环之后哪个指针在最后的节点处,就判断谁
//比如快慢指针,输出中间节点,slow和fast的指针都在变,但是fast先指向链表尾巴,所以判断 fast
//同时每个判断next.next的都必须先判断,next,才能保证 奇偶链长 中不会出现空指针异常
while(fast.next!=null && fast.next.next!=null){slow = slow.next;fast = fast.next.next;}
  1. 只要会删除头结点,都要进行dummy虚指针
  2. 特殊的需求可以考虑结合各种工具类,比如删除重复里面,利用HashSet,删除倒数第k个,利用栈LinkedList

1.3.2 可能出现的问题

NullPointerException,就是当前节点为空,我们还去操作它的 next

② 输出不了结果,一定是指针移动出了问题

1.3.3 题库列表:

237. 删除链表中的节点 ====面试题 02.03. 删除中间节点

203. 移除链表元素(虚拟头结点)

  • 83. 删除排序链表中的重复元素
  • 剑指 Offer 18. 删除链表的节点
  • 面试题 02.01. 移除重复节点
  • 82. 删除排序链表中的重复元素 II

19. 删除链表的倒数第 N 个结点(双指针经典类型)

  • 876. 链表的中间结点
  • 86. 分隔链表
  • 328. 奇偶链表
237、删除链表中的节点
//237.传入待删除结点,直接将当前节点的值改为next的值,next指向next.next,实现原地更新。
public void deleteNode(ListNode node) {node.val = node.next.val;node.next = node.next.next;
}
203、移除链表元素

① 如果删除的节点是中间的节点,则问题似乎非常简单:

  • 选择要删除节点的前一个结点 prev
  • prevnext 设置为要删除结点的 next

② 当要删除的一个或多个节点位于链表的头部时,要另外处理

三种方法:

  1. 删除头结点时另做考虑(由于头结点没有前一个结点)
  2. 添加一个虚拟头结点,删除头结点就不用另做考虑
  3. 递归
  4. 双指针法

即便是参考别人的代码,一看就看的懂,但其实我们有时候不知道内涵,只要自己闭着眼睛敲一遍,发现了问题,才知道是怎么考虑出来的

// 执行耗时:1 ms,击败了99.79% 的Java用户
// 内存消耗:39.4 MB,击败了49.10% 的Java用户
// 时间复杂度是O(n)。空间复杂度O(1)public ListNode removeElements(ListNode head, int val) {//删除值相同的头结点后,可能新的头结点也值相等,用循环解决//比如输入 [7 7 7 7] 删除7,我一开始是直接用 if,发现有些案例无法通过才知道用while的原因while(head!=null && head.val==val){head = head.next;}//因为前面是对head的操作,所以极可能最后完了,head为空,所以把判断的过程放在后面//我本来是吧if放在删除头结点的前面打,结果报错空指针异常,所以才知道为什么判空的要放在后面if(head==null){return head;}ListNode temp = head;//临时指针while(temp.next!=null){if(temp.next.val==val){temp.next = temp.next.next;}else{temp = temp.next;}}return head;}

添加一个虚拟头结点

//执行耗时:1 ms,击败了99.79% 的Java用户
//内存消耗:39.2 MB,击败了82.52% 的Java用户
//时间复杂度是O(n)。空间复杂度O(1)public ListNode removeElements(ListNode head, int val){//        创建虚节点ListNode dummyNode = new ListNode(val-1);dummyNode.next = head;ListNode prev = dummyNode;while(prev.next!=null){if(prev.next.val==val){prev.next = prev.next.next;}else{prev = prev.next;}}return dummyNode.next;}

递归

//时间复杂度是O(n)。递归的方法调用栈深度是n,所以空间复杂度O(n),会超时
public ListNode removeElements(ListNode head, int val){if(head==null){return head;}// 因为递归函数返回的是已经删除节点之后的头结点// 所以直接接上在head.next,最后就只剩下判断头结点是否与需要删除的值一致了head.next = removeElements(head.next,val);if(head.val==val){return head.next;}else{return head;}}
剑指 Offer 18. 删除链表的节点

双指针

// 剑指 Offer 18. 删除链表的节点public ListNode deleteNode(ListNode head, int val){if(head.val==val){//因为互不相等,如果头指针相等,直接返回return head.next;}//双指针ListNode pre = head;ListNode cur = head.next;while(cur!=null && cur.val!=val){//找元素pre = cur;cur = cur.next;}if(cur!=null){//找到了,进行删除操作pre.next = cur.next;}return head;//删完了,返回}
面试题 02.01. 移除重复节点
// 面试题 02.01. 移除重复节点
// 法一:借助HashSet的特征
// 移除未排序链表中的重复节点。保留最开始出现的节点,重复的元素不一定连续public ListNode removeDuplicateNodes(ListNode head) {if (head == null) {return head;}ListNode temp = head;HashSet<Integer> set = new HashSet<>();set.add(head.val);while(temp.next!=null){if(set.add(temp.next.val)){//加进去说明不重复temp = temp.next;}else{temp.next = temp.next.next;//原地删除}}return head;}
// 法二:用空间换时间
// 双重循环,一个定位一个遍历后序,用时间换空间public ListNode removeDuplicateNodes(ListNode head) {if(head==null){return head;}ListNode pre = head;while(pre!=null){ListNode cur = pre;while(cur.next!=null){if(cur.next.val==pre.val){cur.next = cur.next.next;}else{cur = cur.next;}}pre = pre.next;}return head;}
82. 删除排序链表中的重复元素 II
    //升序链表,删除链表中所有重复的节点【1 1 1 1 2 3】-->【2 3】//双指针记录pre 用cur记录相同的数,加虚头节点public ListNode deleteDuplicates(ListNode head) {if(head==null){return head;}ListNode dummy = new ListNode(0);//可能删除头结点,所以使用虚节点dummy.next = head;ListNode pre = dummy;ListNode cur = dummy.next;while(cur!=null && cur.next!=null){//画图最好理解if(cur.val==cur.next.val ){//如果有奇数个相同的值,就删不完,所以必须用while循环while(cur!=null && cur.next!=null && cur.val==cur.next.val ){cur = cur.next;//找到最后一个相等的数}pre.next = cur.next;cur = pre.next;}else{pre = cur;cur = cur.next;}}return dummy.next;}
19、删除链表的倒数第 N 个结点
// 删除链表的倒数第 n 个结点,并且返回链表的头结点
// 双指针public ListNode removeNthFromEnd(ListNode head, int k){if(head==null) return head;
//        可能会删除头结点ListNode dummy = new ListNode(0,head);ListNode pre = dummy.next;for (int i = 0; i < k; i++) {pre = pre.next;}ListNode cur = dummy;while(pre!=null){cur = cur.next;pre = pre.next;}cur.next = cur.next.next;return dummy.next;}
//    另外一个方法,利用栈的先进后出特点,效率会更低
//    执行耗时:1 ms,击败了19.42% 的Java用户
//    内存消耗:37.7 MB,击败了5.02% 的Java用户
public ListNode removeNthFromEnd(ListNode head, int n) {if(head==null){return head;}ListNode dummy = new ListNode(0,head);ListNode temp = dummy;LinkedList<ListNode> stack = new LinkedList<>();while(temp!=null){stack.push(temp);temp = temp.next;}for (int i = 0; i < n; i++) {stack.pop();}ListNode pre = stack.peek();pre.next = pre.next.next;return dummy.next;
}
876、链表的中间结点
//执行耗时:0 ms,击败了100.00% 的Java用户
//内存消耗:35.7 MB,击败了68.38% 的Java用户
public ListNode middleNode(ListNode head) {ListNode slow = head;ListNode fast = head;//如果不加fast != null,链表元素个数为偶数时会报空指针异常while(fast!=null && fast.next!=null){slow = slow.next;fast = fast.next.next;}return slow;}
86、分隔链表

两个临时链表

// 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
// 另外创建一个链表,遍历原来的链表,删除小于的接上去。可能删除头结点public ListNode partition(ListNode head, int x) {ListNode small = new ListNode(0);//可能会动头结点,所以需要虚节点ListNode smallHead = small;//要记住头结点,所以需要另外设置HeadListNode large = new ListNode(0);ListNode largeHead = large;while(head!=null){if(head.val<x){small.next = head;small = small.next;}else{large.next = head;large = large.next;}head = head.next;}large.next = null;//再拼接两个链表,尾巴指向nullsmall.next = largeHead.next;return smallHead.next;}
328、奇偶链表(分割链表的变形)

两个临时链表的变形

    // 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。// 请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。// 法一,利用额外空间public ListNode oddEvenList(ListNode head) {if(head==null){return head;}ListNode odd = new ListNode(0);ListNode oddHead = odd;ListNode even = new ListNode(0);ListNode evenHead = even;int count = 1;while(head!=null){if(count%2==1){//奇数odd.next = head;odd = odd.next;}else{even.next = head;even = even.next;}head = head.next;count++;}even.next = null;odd.next = evenHead.next;return oddHead.next;}

直接双指针前后遍历奇数偶数

//    不需要额外空间,双指针操作public ListNode oddEvenList(ListNode head) {if(head==null){return head;}ListNode odd = head;ListNode even = head.next;ListNode evenHead = even;while(even!=null && even.next!=null){odd.next = even.next;//先把奇数连起来odd = odd.next;//移动奇数的指针even.next = odd.next;//此时加偶数even = even.next;//移动偶数的指针}odd.next = evenHead;return head;}

1.4 反转链表节点类题目

反转的这些操作尤其需要记忆,要每天多写几遍才可以。


1.4.1 动图解释,双指针修改指针的方向

1.4.2 题库列表

206. 反转链表====剑指 Offer 24. 反转链表

92. 反转链表 II

234. 回文链表====面试题 02.06. 回文链表

25. K 个一组翻转链表

206、反转链表

双指针法迭代

//给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。public ListNode reverseList(ListNode head) {if(head==null){return head;}ListNode pre = null;ListNode cur = head;while(cur!=null){ListNode temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}

递归法:可参考文章:labuladong:递归反转链表的一部分

  • 使用递归的3个条件

    1. 大问题可以拆解成两个子问题
    2. 子问题的求解方式和大问题一样
    3. 存在最小子问题
//    递归法public ListNode reverseList(ListNode head) {if(head==null || head.next==null){return head;}//递归最重要的是明确递归函数的定义。//reverseList代表”「以 head 为起点」的链表反转,并返回反转之后的头结点“//所以last表示 head.next后面一整段反转之后的头结点。所以最后return lastListNode last = reverseList(head.next);//重点理解,此时head.next指向的已经是反转部分的尾巴,也就是图中的2head.next.next = head;head.next = null;//head指向null,表示此时head已经是尾巴了。return last;}

92、反转链表 II

① 穿针引线法,三个指针

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mv8Avw5Y-1633245545523)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210920172349817.png)]

node = cur.next;//cur表示当前操作的指针,node保存后面的顺序
cur.next = node.next;
node.next = pre.next;
pre.next = node;

方法一:迭代

// 反转位置left到位置right中间的部分,其余部分不变,
public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummy = new ListNode(-1,head);//迭代法,先找到起点ListNode pre = dummy;for (int i = 0; i < left-1; i++) {pre = pre.next;//来到 left 节点的前一个节点}ListNode cur = pre.next;//cur是真正反转的指针ListNode node;//node的保存cur.next 的临时指针for (int i = 0; i < right - left; i++) {node = cur.next;//保存后面的顺序cur.next = node.next;//穿针引线,其实很有规律node.next = pre.next;pre.next = node;}return dummy.next;
}

方法二,递归

//  递归法反转前n个元素public ListNode reverseN(ListNode head,int n){ListNode succssor = null;if(n==1){successor = head.next;//把后继记录下来,基线是只有一个元素return head;}
//        看递归不要进递归函数里面,而是看函数返回了什么结果ListNode last = reverseN(head.next,n-1);
//        整个链表 = head+reverseN(n-1)这一个已经反转的小团体 + succssor后继
//        注意此时head.next指向的是已经反转的小团体的末尾head.next.next = head;head.next = succssor;return last;}public ListNode reverseBetween(ListNode head, int left, int right) {if(left==1){return reverseN(head,right);}head.next = reverseBetween(head.next,left-1,right-1);return head;}
234、 回文链表

//    请判断一个链表是否为回文链表。
//    反转后半段,两头往中间遍历是否相等,返回前复原链表public boolean isPalindrome(ListNode head) {//        1.快慢指针找到中间节点ListNode slow = head;ListNode fast = head;while(fast!=null && fast.next!=null){slow = slow.next;fast = fast.next.next;}ListNode point = slow;//存储中间的断点,用于恢复原来的顺序if(fast!=null){slow = slow.next;//slow要定位到中间的后一个位置}
//        2.反转slow后面的元素ListNode left = head;ListNode right = reverse(slow);ListNode q = right;//存储末尾的断点用于恢复原来链表的顺序//        3.两头往中间遍历,是否相等,因为后半段尾巴是nullwhile(right!=null){if(left.val!=right.val){return false;}left = left.next;right = right.next;}point.next = reverse(q);//还原链表return true;}public ListNode reverse(ListNode head){if(head==null){return head;}ListNode pre = null;ListNode cur = head;while(cur!=null){ListNode temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}
25. K 个一组翻转链表

感觉一点设计这种一段一段的都可以考虑递归等算法,但对我的下意识总是,暴力求解。。。

学习!努力学习!

 public ListNode reverseKGroup(ListNode head, int k) {if(head==null){return head;}
//        1.反转前k个元素ListNode pre = head;ListNode cur = head;for (int i = 0; i < k; i++) {if(cur==null){return head;//不够长,保持不变}cur = cur.next;}ListNode newHead = reverse(pre,cur);//        2.递归反转后面的,并将后续的连接
//        pre一直没有动,所以pre变成最后一个元素之后将next连上pre.next = reverseKGroup(cur,k);return newHead;}//  反转区间[head end)之间的元素public ListNode reverse(ListNode head,ListNode end){ListNode pre = null;ListNode cur = head;while(cur!=end){ListNode temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}

1.5 合并链表

合并有序链表问题在面试中出现频率 较高

1.5.1 题库列表

21. 合并两个有序链表

23. 合并K个升序链表

21、合并两个有序链表
/*** 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的*/
public class _03_01_mergeList {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {//        双指针比较两位置大小,新建一链表ListNode newHead = new ListNode(0);ListNode head = newHead;while(l1!=null && l2!=null){if(l1.val>l2.val){newHead.next = l2;l2 = l2.next;}else{newHead.next = l1;l1 = l1.next;}newHead = newHead.next;}//      只有一个是空的,那么便把另一个直接接上去就可以了newHead.next = l1==null?l2:l1;return head.next;}//    2.递归法public ListNode mergeTwoLists2(ListNode l1, ListNode l2) {//        递归基线是当前数组为空,直接返回if(l1==null){return l2;}if(l2==null){return l1;}
//          判断当前的大小,原地修改if(l1.val<=l2.val){l1.next = mergeTwoLists(l1.next,l2);return l1;}else{l2.next = mergeTwoLists(l1,l2.next);return l2;}}
}
23、合并K个升序链表

方法一:逐个合并,方法二,递归分治,时间消耗相对少很多

public ListNode mergeKLists(ListNode[] lists) {if(lists.length==0){return null;}
//        遍历链表数组,每次选择两个链表,进行合并int i=0;ListNode head = null;while(i<lists.length){head = merge(head,lists[i]);i++;}return head;}public ListNode merge(ListNode l1,ListNode l2){ListNode newHead = new ListNode(0);ListNode head = newHead;while(l1!=null && l2!=null){if(l1.val>l2.val){newHead.next = l2;l2 = l2.next;}else{newHead.next = l1;l1 = l1.next;}newHead = newHead.next;}//      只有一个是空的,那么便把另一个直接接上去就可以了newHead.next = l1==null?l2:l1;return head.next;}

 public ListNode mergeKLists(ListNode[] lists) {return mergeFrom(lists,0,lists.length-1);}public ListNode mergeFrom(ListNode[] lists,int left,int right){if(left==right){return lists[left];}if(left>right){return null;}int mid = left + (right-left)/2;//merge是合并两个链表的方法,如上return merge(mergeFrom(lists,left,mid),mergeFrom(lists,mid+1,right));}

1.6 排序链表

1.6.1 题库列表

147. 对链表进行插入排序

148. 排序链表

147、对链表进行插入排序
 public ListNode insertionSortList(ListNode head) {if(head==null){return head;}
//        1.会移动头结点,所以用到虚拟头结点ListNode dummy = new ListNode(0);dummy.next = head;//        2.外层循环遍历完链表所有数,遍历[head,lastSort]这段位置找插入ListNode cur = dummy.next;ListNode lastSort = head;//维护已排序部分的最后一个位置while(cur!=null){//cur为遍历的待插入元素if(lastSort.val<=cur.val){lastSort = cur;//大,直接后移}else{ListNode pre = dummy;//用来遍历已经排序的部分//            3.从前往后比较,找插入的位置while(cur.val>pre.next.val){pre = pre.next;}
//            4.找到位置进行插入操作lastSort.next = cur.next;cur.next = pre.next;pre.next = cur;}
//            5.指针后移cur = lastSort.next;}return dummy.next;}
148、排序链表:归并法

// 要求时间空间复杂度分别为O(nlogn)和O(1):归并排序
// 递归额外空间:递归调用函数将带来O(logn)的空间复杂度
//    使用递归版归并,会额外用到logn的时间复杂度public ListNode sortList(ListNode head) {//        1.base lineif(head==null || head.next==null){return head;}//        2.找中点,偶数找的前面那个中点的位置,奇数找到中点ListNode slow =head;ListNode fast = head.next;while(fast!=null && fast.next!=null){slow = slow.next;fast = fast.next.next;}//        3.将链表分割成两个子链表ListNode temp = slow.next;slow.next = null;ListNode left = sortList(head);ListNode right = sortList(temp);//        4.新建一个链表,对已排序的链表进行归并操作ListNode newHead = new ListNode(0);ListNode res = newHead;while(left!=null && right!=null){if(left.val<right.val){newHead.next = left;left = left.next;}else{newHead.next = right;right = right.next;}newHead = newHead.next;}newHead.next = left==null?right:left;return res.next;}

1.7 环形链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ya2BbAvB-1633245545528)(image/算法/e1dbea51f21247a4972c2cb28855609a~tplv-k3u1fbpfcp-watermark.webp?lastModify=1632129814)]

1.7.1 题库列表

  • 160. 相交链表

  • 141. 环形链表

    • 142. 环形链表 II
160、相交链表
// 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
// Set里放的是ListNode而不是ListNode.val,比较的是指针地址
// 方法一:哈希表
//    方法一:哈希表法。存的是ListNode,所以相等时代表地址相同,也就是同一个元素
//    即便val相等,在哈希判断是也不会相等,所以可以使用hash表的方法public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {if(headA==null|| headB==null){return null;}
//      1.先将某一个链表中的元素存到 set 中HashSet<ListNode> set = new HashSet<>();ListNode cur = headA;while(cur!=null){set.add(cur);cur = cur.next;}//      2.再遍历第二个链表,如果有就直接返回,如果没有继续遍历ListNode node= headB;while(node!=null){if(set.contains(node)){return node;}node = node.next;}return null;}//方法二:双指针public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA==null || headB==null){return null;}ListNode nodeA = headA;ListNode nodeB = headB;while(nodeA!=nodeB){// 退出的关键是:指向同一个指针(不是值相等),或者都指向nullnodeA = nodeA==null?headB:nodeA.next;nodeB = nodeB==null?headA:nodeB.next;}return nodeA;//如果没有相等的那么nodeA==nodeB==null}
141、环形链表
//    双指针public boolean hasCycle(ListNode head) {if(head==null ||head.next==null){return false;}ListNode slow = head;ListNode fast = head.next;while(slow!=fast){//            因为快指针在前面,所以只要判断快指针是否达到了队尾就可以if(fast==null || fast.next==null){return false;}slow = slow.next;fast = fast.next.next;}return true;}//    哈希表,耗时非常慢public boolean hasCycle(ListNode head) {if(head==null){return false;}HashSet<ListNode> set = new HashSet<>();ListNode cur = head;while(cur!=null){if(set.contains(cur)){return true;}set.add(cur);cur = cur.next;}return false;}
142、环形链表II
//给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
//    方法一是哈希表,方法二双指针public ListNode detectCycle(ListNode head) {ListNode slow = head;ListNode fast = head;
//        1.快慢指针找重合点while(fast!=null && fast.next!=null){slow = slow.next;fast = fast.next.next;
//        2.重合了,这个时候,从头来一个指针遍历if(fast==slow){ListNode cur = head;while(cur!=slow){cur = cur.next;slow = slow.next;}return slow;}}
//        3没有环,返回nullreturn null;}

一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题相关推荐

  1. 数据结构与算法--单链表相关面试题

    此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢! 一.概述 获取单链表的有效元素个数[新浪面试题1] 获取单链表倒数第k个结 ...

  2. python定义链表节点_Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】...

    本文实例讲述了Python数据结构与算法之链表定义与用法.分享给大家供大家参考,具体如下: 本文将为大家讲解: (1)从链表节点的定义开始,以类的方式,面向对象的思想进行链表的设计 (2)链表类插入和 ...

  3. 数据结构与算法之链表结构寻找p、q最近的公共祖先

    链表结构,寻找p.q最近的公共祖先 数据结构与算法之链表结构寻找p.q最近的公共祖先 链表结构,寻找p.q最近的公共祖先 问题 想法 代码 问题 设一棵二叉树的结点结构为(LLINK, INFO, R ...

  4. 数据结构与算法 内核链表实现商品购物系统项目+Makefile

    数据结构与算法 内核链表实现商品购物系统项目 第一章 项目实现思维 [1]编译介绍 [2]框架思维 第二章 Makefile编写 第三章 代码编写实现 [1]favorite.txt文件 [2]his ...

  5. 数据结构与算法 第八天常见排序+冒泡排序+快速排序+文件IO+大数据排序+文件合并

    数据结构与算法 第八天常见排序+冒泡排序+快速排序+文件IO+大数据排序+文件合并 第一章 冒泡排序 [1]Bubble_Sort.c 第二章 快速排序 [1]quick_sort.c 第三章 大数据 ...

  6. (左神)数据结构与算法 ---- 判断链表是否为回文结构的三种高效解法

    链表在数据结构与算法中可谓"北斗之尊",现在让我们通过判断链表回文的小练习进一步更深地了解链表~ 文章目录 一.链表的节点结构 二.判断一个链表是否为回文结构 (一)解法1:将链表 ...

  7. JS数据结构与算法_链表

    上一篇:JS数据结构与算法_栈&队列 下一篇:JS数据结构与算法_集合&字典 写在前面 说明:JS数据结构与算法 系列文章的代码和示例均可在此找到 上一篇博客发布以后,仅几天的时间竟然 ...

  8. java数据接口之链表_Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在'点"中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LI ...

  9. 数据结构与算法--复杂链表的复制

    复杂链表的复制 题目:实现一个函数complexListNode 复制一个复杂链表.在链表中,每个节点除了有一个next指针指向下一个节点,还有另外一个before节点,before节点指向链表中任意 ...

最新文章

  1. PYTHON编程导论群问题汇总(五)
  2. 黑苹果挂载不了分区_让黑苹果变得更完美——BCM94532HMB无线蓝牙接力完美驱动!...
  3. xtrabackup支持的engine
  4. Java中的传值与传引用
  5. HTML5清爽简洁外贸网站模板
  6. php微信个性化菜单,微信公众平台新增个性化菜单接口,实现公众号
  7. 手机支付优惠促销活动插画素材,拿来就能用,高效省时。
  8. 自动驾驶感知-车道线系列(三)——霍夫变换
  9. 《战舰世界》携手汉堡王开启“战舰堡胃战”主题活动
  10. 修复Lvgl的roller控件点击位置向上偏移的问题
  11. 薄板开孔建模计算的ansys命令流
  12. java判断生肖_Java写出生肖年判断
  13. python爬取酷狗音乐top500及歌词_爬取酷狗音乐Top500(示例代码)
  14. 利用稀疏格式矩阵求解方程组以及机器学习训练速度对比
  15. 【基于深度学习的细粒度分类笔记2】弱监督学习下商品识别:CVPR 2018细粒度识别挑战赛获胜方案简介
  16. 国产手机已经用上了 120W 快充技术,苹果还在用20W的原因一
  17. 服务器国产linux操作系统,国产linux操作系统适于做服务器系统的有哪些
  18. java swing组件_Java -- Swing 组件使用
  19. 网站收录链接分析之网站排名查询
  20. 用python爬取实时基金估值

热门文章

  1. 【校招VIP】产品设计之游戏常识
  2. metasploit利用IE漏洞XSS挂马拿内网主机
  3. iOS 审核被拒记录 Guideline 2.5.1 HealthKit; 2.5.4 UIBackgroundModes audio; 1.5 Developer Information
  4. reduce函数详解
  5. table表格溢出隐藏
  6. 分享四种换发型方法,轻易测试出适合你的发型
  7. 万网云解析设置二级域名解析到同IP不同端口
  8. 最新爱情经典语录 真爱一个人,就要尽量让他开心
  9. 马宁的Windows Phone 7.1初体验(二)——Push Notification
  10. 3D Vision 八讲:第四讲