文章目录
反转链表
非递归——add重载
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
ListNode tmp = listNode;
while(tmp!=null){
list.add(0,tmp.val);
tmp = tmp.next;
}
return list;
}
}//O(N) O(N)
递归
ArrayList<Integer> list = new ArrayList<>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
直接反转链表
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
while (listNode != null){
list.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(list);
return list;
}
使用栈
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
while (listNode != null){
stack.push(listNode.val);
listNode = listNode.next;
}
while (!stack.empty()){
list.add(stack.pop());
}
return list;
}
先反转链表,再遍历存入list中
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
ListNode pre = null;
ListNode cur = listNode;
ListNode temp = cur;
while (cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
while (pre != null){
list.add(pre.val);
pre = pre.next;
}
return list;
}
合并两个排序的链表
递归解法
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1 == null){
return list2;
}else if(list2 == null){
return list1;
}
if(list1.val>list2.val){
list2.next = Merge(list1,list2.next);
return list2;
}else {
list1.next = Merge(list1.next,list2);
return list1;
}
}
两个链表的第一个公共结点
给定两个单链表
A,B
,返回第一个公共结点的指针。若没有,返回null
栈解法
使用双端队列Deque的实现类ArrayDeque:
- addFirst(): 向队头插入元素,如果元素为空,则发生NPE
- addLast(): 向队尾插入元素,如果为空,则发生NPE
- offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false
- offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false
- removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException
- removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException
- pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null
- pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null
- getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException
- getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException
- peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null
- peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Deque<ListNode> deque1 = new ArrayDeque<>(),deque2 = new ArrayDeque<>();
while(pHead1 != null){
deque1.add(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null){
deque2.add(pHead2);
pHead2 = pHead2.next;
}
ListNode ans = null;
//peek是获取元素但不会移除类似于get,poll是获取元素并会移除类似于remove
//不用get和remove的原因是如果该元素是*null*,则发生*NoSuchElementException*
//而另外两个会返回*null*
while(!deque1.isEmpty()&&!deque2.isEmpty()&&deque1.peekLast()==deque2.peekLast()){
ans = deque1.pollLast();
deque2.pollLast();
}
return ans;
}
利用set特性
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Set<ListNode> set = new HashSet<>();
while (pHead1 != null) {
set.add(pHead1);
pHead1 = pHead1.next;
}
while(pHead2 != null && !set.contains(pHead2)){
pHead2 = pHead2.next;
}
return pHead2;
}
双指针法
假设链表1是a+c;链表2是b+c,那么c就是其公共部分。
a+c+b=b+c+a,此时就找到了公共结点。
这种方法还挺美妙的。
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode pa = pHead1,pb = pHead2;
while (pb != pa){
pa = pa == null?pHead2:pa.next;
pb = pb == null?pHead1:pb.next;
}
return pa;
}
我一开始没想清楚,以为有bug:如果两个链表没有交点呢?但其实结果还是正确的,没有交点的时候一定能够保证二者同时为null跳出循环,举个栗子:
//有公共结点时:a:[1,2,3,4,6,7];b:[5,6,7]
l1:1 2 3 4 6 7 null 5 6
l2:5 6 7 null 1 2 3 4 6
找到了公共结点6
//无公共结点时:a:[1,2,3,4,6,7];b:[8,9,5]
l1:1 2 3 4 6 7 null 8 9 5 null
l2:8 9 5 null 1 2 3 4 6 7 null
二者同时为null跳出循环并返回结果null
链表中环的入口结点
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
Set方法
public ListNode EntryNodeOfLoop(ListNode pHead) {
Set<ListNode> set = new HashSet<>();
while(pHead!=null&&set.add(pHead)){
pHead = pHead.next;
}
return pHead;
}
快慢指针法
我们已经掌握了如何使用快慢指针判断是否有环:慢指针一次走一步,快指针一次走两步,如果相遇就说明存在环。
那么如何找到环的入口结点呢:我们经过推导可以得知:两个指针相遇后,再使用两个指针,一个从链表起始点开始,一个从相遇点开始,每次他们都走一步,直到再次相遇,那么这个相遇点就是环的入口。
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode slow = pHead;//快指针
ListNode fast = pHead;//慢指针
while (fast != null && fast.next != null) {
//快慢指针,快指针每次走两步,慢指针每次走一步
fast = fast.next.next;
slow = slow.next;
//先判断是否有环,
if (slow == fast) {
//确定有环之后才能找环的入口
while (pHead != slow) {
//两指针,一个从头结点开始,
//一个从相遇点开始每次走一步,直到
//再次相遇为止
pHead = pHead.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
链表中倒数最后K个结点
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。如果该链表长度小于k,请返回一个长度为 0 的链表。
用栈解决
public ListNode FindKthToTail (ListNode pHead, int k) {
Stack<ListNode> stack = new Stack<>();
while(pHead != null){
stack.push(pHead);
pHead = pHead.next;
}
ListNode ans = null;
for(int i = k;;i--){
if(stack.isEmpty()){
return null;
}else if(i==1){
ans = stack.pop();
return ans;
}else {
stack.pop();
}
}
}
差值法
我们可以先对链表进行一次完整遍历,拿到总长度 cntcnt,最后由 cnt - kcnt−k 即是倒数第 kk 个节点距离 headhea**d 节点的距离。
public ListNode FindKthToTail (ListNode pHead, int k) {
int count = 0;
ListNode temp = pHead;
while(temp!=null){
count++;
temp = temp.next;
}
count = count - k;
if(count<0){
return null;
}else {
while (count--!=0){
pHead = pHead.next;
}
return pHead;
}
}
快慢指针法
第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可。如果第一个指针还没走k步的时候链表就为空了,我们直接返回null即可。
public ListNode FindKthToTail (ListNode pHead, int k) {
if (pHead == null){
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while (k-->0){
if (fast == null){
return null;
}
fast = fast.next;
}
while (fast != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
今日推歌
-----《年轮》
荒草丛生的青春
倒也过的安稳
代替你陪着我的
是年轮
数着一圈圈年轮
我认真
将心事都封存
密密麻麻是我的自尊