Leetcode链表总结
由于最近开始面临保研找实习,所以打算刷波leetcode,之前一直想做这件事,巩固一下自己的基础,但总是被各种事情耽搁,由此写下这篇博客作为督促。在这期间偶然间看到同学的博客,他也打算刷leetcode,但刚做了一题就拿到了滴滴的offer,而且那一题跟我做的第一道题一样,让人感叹的巧合。希望我也能像他一样得到满意的结果。
本篇文章不会包含代码的讲解,只是做题目的一点思路心得,相关答案可以访问leetcode
2. Add Two Numbers
比较简单的链表相加问题,除了要考虑到链表不一样长的情况,还有最高位进位为1。思考到细节。
另外,在solution中看到了为链表加空表头的方式,解决了插入判断一致性问题,也是一个比较基础的操作。
19. Remove Nth Node From End of List
删除倒数第n个节点,双指针问题,第一个指针走n步,第二个指针与第一个指针一起移动,需考虑删除节点为头节点的情况。也可通过添加一空头,消除头节点的特殊判断。
24. Swap Nodes in Pairs
交换相邻节点,添加空头消除头节点特殊情况,考虑链表长度为奇数的特殊情况。
82. Remove Duplicates from Sorted List II
有序表删除重复元素,按值找出需删除的一段链表,修改指针,添加空头消除头节点特殊判断。
92. Reverse Linked List II
在206题反转链表的基础上,在链表上加入某范围的反转,反转链表使用三个指针,从头到尾依次把后一块移动到前一块。由于反转的是链表的部分,头部有可能改变也可能不改变,因此加入空头消除特殊性,small和big指针分别记录需反转链表的前一块和后一块的地址,之后使用206题方法对链表进行反转即可。
看了讨论区,发现也不用计算链表末尾值,根据n-m即可知道移动块的次数,少了一次需反转链表的遍历。
109. Convert Sorted List to Binary Search Tree
将有序链表转换为平衡BST树,采用递归的方式,链表中部元素作为树根,左右子树依次对两端链表进行同种操作,寻找中部元素使用两个指针,一个一次走一步,另一个一次走两步,即可找到中部元素,代码如下
public TreeNode sortedListToBST(ListNode head) {
if(head==null) return null;
ListNode fast=head;
ListNode slow=head;
ListNode pre=head;
while(fast!=null&&fast.next!=null) { //快指针走到尾部时
pre=slow;
slow=slow.next;
fast=fast.next.next;
}
pre.next=null; //将链表断开
TreeNode treehead = new TreeNode(slow.val); //new一根节点
if(head==slow) return treehead; //只剩一个节点不进行递归
else {
treehead.left=sortedListToBST(head);
treehead.right=sortedListToBST(slow.next);
}
return treehead;
}
看到讨论区中有不将链表断开,另写一个函数传入tail,将null判断改为是否等于tail判断,少了一个指针。
138. Copy List with Random Pointer
random指针链表深拷贝,一直在考虑如何找到重复节点,其实也是一个索引问题,使用hashmap,被拷贝链表作为key,拷贝节点为value,从头开始一一连接指针即可。
public RandomListNode copyRandomList(RandomListNode head) {
Map<RandomListNode,RandomListNode> map = new HashMap<>();
//第一次遍历做映射
RandomListNode tmp = head;
while(tmp!=null) {
map.put(tmp,new RandomListNode(tmp.label));
tmp=tmp.next;
}
//第二次遍历做连接
tmp=head;
while(tmp!=null) {
map.get(tmp).next=map.get(tmp.next);
map.get(tmp).random=map.get(tmp.random);
tmp=tmp.next;
}
return map.get(head);
}
142. Linked List Cycle II
在141判断是否有环形链表的基础上,返回入环的第一个节点。在不使用另外存储空间的情况下。判断环形链表使用双指针,一个一次走两步,另一个一次走一步,如果链表中有环,那么指针会相遇。返回入环的第一个节点需要数学推导,即当两指针相遇后,将一个指针退回head,之后各自走一步,再次相遇时便是入环的第一个节点。
public ListNode detectCycle(ListNode head) {
ListNode first = head;
ListNode second = head;
boolean flag=false;
while(second!=null&&second.next!=null) { //无环为null返回
first=first.next;
second=second.next.next;
if(first==second) {
flag=true;
break;
}
}
if(flag==false)
return null;
else {
first=head;
while(first!=second) {
first=first.next;
second=second.next;
}
return first;
}
}
143. Reorder List
重排链表,这道题给我一种“妙啊”的感觉,后一半反排插入一开始不知道怎么办,甚至想到了hash,讨论区的做法是,先把后一半反转,再逐个插入。
public void reorderList(ListNode head) {
if(head==null||head.next==null||head.next.next==null) return;
ListNode fast=head;
ListNode slow=head;
//找到一半
while(fast!=null&&fast.next!=null) {
fast=fast.next.next;
slow=slow.next;
}
//反转
ListNode first = slow.next;
ListNode second = first.next;
slow.next=null;
first.next=null;
while(second!=null) {
ListNode third = second.next;
second.next=first;
first=second;
second=third;
}
//逐个插入
ListNode tmp=head;
ListNode next=null;
while(first!=null) {
next=first.next;
first.next=tmp.next;
tmp.next=first;
tmp=tmp.next.next;
first=next;
}
}
148. Sort List
在O(nlogn)时间内排序,归并排序加递归,将链表分为两半,递归排序,再将这两半排好序的链表合并在一起。
public ListNode sortList(ListNode head) {
if(head==null||head.next==null) return head;
ListNode fast = head;
ListNode slow = head;
//链表分半
while(fast.next!=null&&fast.next.next!=null) {
fast=fast.next.next;
slow=slow.next;
}
//递归排序
ListNode tmp=slow.next;
slow.next=null;
ListNode left=sortList(head);
ListNode right=sortList(tmp);
//合并
ListNode blank=new ListNode(0);
tmp=blank;
while(left!=null&&right!=null) {
if(left.val<right.val) {
tmp.next=left;
left=left.next;
tmp=tmp.next;
}else {
tmp.next=right;
right=right.next;
tmp=tmp.next;
}
}
if(left!=null)
tmp.next=left;
if(right!=null)
tmp.next=right;
return blank.next;
}
总结:
在链表头部添加空头以消除头部特殊判断比较常见,双指针问题可用于倒数k个或将链表元素分半。递归和hashmap也比较常见。
上一篇: 官方详解UOS与Deepin OS区别:UOS是商业版
下一篇: SpringBoot项目发布