摘要:拿了小米和美团的,要被延期,失效,工作重新找。把准备过程纪录下来,共勉。注意,有环的链表,此种方法失效。已知两个单链表和各自有序,把它们合并成一个链表依然有序
写在最前面
导师贪腐出逃美国,两年未归,可怜了我。拿了小米和美团的offer,要被延期,offer失效,工作重新找。把准备过程纪录下来,共勉。
链表是最常考察的数据结构
// 链表定义 public class Node{ int val;//数据域 Node next;// 指针域 public Node(int val){ this.val = val; } }
基础题
求单链表的长度
考察链表遍历的方法,时间复杂度为o(n)
//求单链表长度,起手判断链表是否为空,非常必要 public static int getListLength(Node head){ //如果链表为空,长度为0 if(null == head){ return 0; } // 开始着手办正事了 int len = 0; Node cur = head; //如果循环判断条件不确定,可以先写逻辑,回头再定条件 while(null != cur.next){ len++; cur = cur.next; } return len; }
结点相对位置关系,结点与链表的相对位置关系
翻转链表
链表中经典解法1,引入两个指针,然后确定其相对位置关系
迭代法
解题思路:前后两个指针,遍历链表,每遍历一个结点,前面的指针将指向后面的指针,时间复杂度o(n)
public static Node getReverseList(Node head){ if(null == head || null == head.next){ return null; } Node cur = head; Node newHead = null; while(null != cur){ Node preCur = cur; cur = cur.next; preCur.next = newHead; newHead = preCur; } return newHead; }
返回单链表倒数第k个结点
两个指针,前面的指针超前后面指针k个位置
public static Node findLastKthNode(Node head,int k){ if(k == 0 || null == head){ return null; } Node p = head; Node q = head; while(k > 0 || p != null){ p = p.next; k--; } if(k > 0 || p == null){ return null; } while(p != null){ q = q.next; p = p.next; } return q; }
查找中间结点
分析:两个指针可以解决,位置相对关系是:p指针向前移动一步,q指针向前移动两步
public static Node getMiddleNode(Node head){ if(null == head || head.next == null){ return head; } Node p = head; Node q = head; while(p != null){ p = p.next; q = q.next; if(p != null){ p = p.next; } } return q; }
从尾到头打印单链表
public static void printList(Node head){ Stacks = new Stack<>(); Node cur = head; while(cur != null){ s.push(cur); cur = cur.next; } while(!s.empty()){ cur = s.pop(); System.out.print(p.val + " "); } System.out.println(); }
有环问题
判断一个单链表是否有环
思路:快慢两个指针,如果相遇,则有环
public static boolean hasCycle(Node head){ Node fast = head; Node slow = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(fast == slow){ return true; } } return false; }
- **取出有环链表中,环的长度**
思路:找到相遇的结点a后,让b结点从a位置向下遍历,回来后得到的结点数,即为环长度
//方法:有环链表中,获取环的长度。参数node代表的是相遇的那个结点 public int getCycleLength(Node node) { if(node == null){ return 0; } int length = 0; Node cur = node; while(cur != null){ cur = cur.next; length++; if(cur != node){ return length; } } return length; }
判断两个链表相交
思路:如果两链接的尾结点相同,则有环。注意,有环的链表,此种方法失效。
public static boolean isIntersect(Node head1, Node head2) { Node tail1 = head1; Node tail2 = head2; while(tail1 != null){ tail1 = tail1.next; } while(tail2 != null){ tail2 = tail2.next; } if(tail1 == tail2){ return true; } return false; }
单链表中,取出环的起始点
public Node getCycleStart(Node head, int cycleLength) { Node first = head; Node second = head; for(int i = 0; i < cycleLength; i++){ second = second.next; } while(first != null && second != null){ if(first == second){ return first; } first = first.next; second = second.next; } return null; }
求两个单链表相交的第一个节点
思路:分别求出a链表、b链表的长度len1、len2,如果a链表比b链表长,则a提前移动len1-len2距离,然后a、b一起向前移动,直到引用相同。
public static Node getFirstCommonNode(Node head1, Node head2) { Node tail1 = head1; Node tail2 = head2; int len1 = 1; while(tail1 != null){ tail1 = tail1.next; len1++; } int len2 = 1; while(tail2 != null){ tail2 = tail2.next; len2++; } if(tail1 != tail2){ return null; } Node n1 = head1; Node n2 = head2; if(len1 > len2){ int k = len1 - len2; while(k != 0){ n1 = n1.next; k--; } } if(len1 < len2){ int k = len1 - len2; while(k != 0){ n2 = n2.next; k--; } } while(n1 != n2){ n1 = n1.next; n2 = n2.next; } return n1; }
已知两个单链表head1和head2各自有序,把它们合并成一个链表依然有序
public static Node mergeSortedList(Node head1, Node head2){ if(head1 == null){ return head2; } if(head2 == null){ return head1; } Node mergeHead = null; if(head1.val < head2.val){ mergeHead = head1; head1 = head1.next; mergeHead.next = null; }else{ mergeHead = head2; head2 = head2.next; mergeHead.next = null; } Node mergeCur = mergeHead; while(head1 != null && head2 != null){ if(head1.val < head2.val){ mergeCur.next = head1; head1 = head1.next; mergeCur = mergeCur.next; mergeCur.next = null; }else{ mergeCur.next = head2; head2 = head2.next; mergeCur = mergeCur.next; mergeCur.next = null; } } if(head1 != null){ mergeCur.next = head1; } if(head2 != null){ mergeCur.next = head2; } return mergeHead; }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70116.html
摘要:底层实现是对象数组,优点是时间为,缺点是和时间为,需要留意的是扩容的过程以及的算法本节参考源码中放最新的源码为,组成链表或红黑树定义从整体上看,底层的存储结构是基于数组和链表实现的。实现了所谓的线程安全,在很多方法上都加上了。 ArrayList ArrayList底层实现是对象数组,优点是set、get时间为O(1),缺点是add和remove时间为O(n),需要留意的是扩容的过程以...
摘要:算法名称描述优点缺点标记清除算法暂停除了线程以外的所有线程算法分为标记和清除两个阶段首1 回顾我的时间线 在本文的开头,先分享一下自己的春招经历吧: 各位掘友大家好,我是练习时长快一年的Android小蔡鸡,喜欢看源码,逛掘金,写技术文章...... 好了好,不开玩笑了OWO,今年春招投了许多简历的,但是被捞的只有阿里,头条和美团,一路下来挺不容易的. 个人认为在春招中运气>性格>三观>技术...
摘要:和三个方法的时间复杂度必须为两种解法,解法一,将最小值存入自有的数据结构中,如下所示原本的值最小值解法二,用两个栈 堆栈和队列统称线性表 简单的线性结构 数组和链表可以实现这两种数据结构 堆栈 基本理解 DFS 深度优先---按深度遍历 递归转非递归 队列 基本理解 BFS 广度优先---按层序遍历 出入栈的合法性模拟出入栈的过程,不是入栈,就是...
摘要:由遍历结果反求树分析递归分治,第一层需要找到相应的遍历结果,对数组来说,问题转化为找下标问题对前序中序遍历结果来说前序左右中序左右因此,中序中的下标可求,为对每一层来说,左子树的长度为,右子树的长度为,左子树前序为至,中序为至,可以使 由遍历结果反求树 分析:递归分治,第一层需要找到相应的遍历结果,对数组来说,问题转化为找下标问题 对前序、中序遍历结果来说 前序:[root,[左...
阅读 2879·2023-04-25 21:23
阅读 2978·2021-09-22 15:24
阅读 829·2019-08-30 12:55
阅读 2042·2019-08-29 18:42
阅读 2562·2019-08-29 16:27
阅读 876·2019-08-26 17:40
阅读 2079·2019-08-26 13:29
阅读 2568·2019-08-26 11:45