- 反转链表
NC78
ListNode prev = null;
while(curr!=null){
ListNode temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
// 递归反转连链表
// reverse(head.next) 返回反转之后的头结点
ListNode successor = null
if(head ==null || head.next ==null) return head
// 递归
ListNode last = reverseN(head.next)
// 链表反转
head.next.next = head
// 反转之后 原始的head头结点 变为尾节点 指向null
head.next = null
return last
- 反转链表的前k个节点
// 后继节点
ListNode successor = null
if(head ==null || head.next ==null) return head
if(k==1){
// 记录第k+1个节点
successor = head.next
return head
}
// 递归
// 以head.next 为起点
ListNode last = reverseN(head.next,k-1)
head.next.next = head
// 反转之后的head 与后面的节点连接
head.next = successor
return last
- 链表在指定区间内反转[m,n]
- m=n时,区间只有一个节点
- m=1时,反转链表的前n个节点
if(m==1){
return reverseN(head,n)}
head.next = reverseBetween(head.next,m-1,n-1)
return head
- 判断链表是否有环
NC4
快慢指针
while(fast!=null || fast.next !=null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true
}
}
- 链表相加
NC40
输入[9,3,7], [6,3] 输出{1,0,0,0}
解题思路:1)利用栈的先进先出,栈1存放head1元素,栈2存放head2元素
2)反转链表,再按位相加
Stack<Integer> s1 = new Stack<>()
Stack<Integer> s2 = new Stack<>()
// head1 元素依次入栈
while(head1 !=null){
s1.push(head1.val)
head1 = head1.next
}
//head2 元素依次入栈
while(head2 !=null){
s2.push(head2.val)
head2 = head2.next
}
ListNode res =null
int cnt = 0 // 进位
while(!s1.isEmpty() || !s2.isEmpty()){
int x1 = s1.isEmpty()?0:s1.pop()
int x2 = s2.isEmpty()?0:s2.pop()
int sum = x1+x2+cnt // 当前这一位的总和
cnt = sum/10 // 查看当前是否有进位
int val = sum%10 // 个位数
// 进行当前节点的插入
ListNode tempNode = new ListNode(val)
tempNode.next = res
res = tempNode
}
// 最后一位有进位
if(cnt>0){
ListNode tempNode = new ListNode(cnt);
tempNode.next = res
res = tempNode
}
return res
- 链表中环的入口节点
NC3
// 快指针的路程是慢指针的两倍
// when fast==slow 判断有环
if(fast==slow){
// 将快指针指向头结点 慢指针指向相遇节点
// 同速度移动
fast = head
if(fast !=slow){
fast = fast.next
slow = slow.next
}
return fast
}
- 链表中的节点每K个一组翻转
- 栈 K个入栈-出栈(先进后出);判断是否为K个,已反转和未反转的拼接
Stack<ListNode> stack = new Stack<>()
ListNode res = new ListNode(0)
// p指针
ListNode p = res
while(true){
ListNode temp = head
while(temp !=null &&count <k){
stack.push(temp)
temp =temp.next
count++
}
if(count != k){
p.next = head
break
}
while(!stack.isEmpty()){
p.next(stack.pop())
p = p.next
}
p.next =temp
head =temp
}
return res.next
- 删除有序链表中的重复元素
NC24
- 输入:1-2-3-3-4-4-5 输出1-2-5
ListNode curr = head
while(curr != null || curr.next != null){
// 继续寻找同元素的最大范围
if(curr.val == curr.next.val){
ListNode temp = curr.next
while(temp !=null || temp.val = curr.val){
temp = temp.next
}
prev.next = temp
curr = temp
}
}
- 输入:1-2-3-3-4-4-5 输出:1-2-3-4-5
ListNode temp = head;
while(temp.next != null){
if(temp.val == temp.next.val){
temp.next = temp.next.next
}else{
temp =temp.next
}
}
- 链表的倒数第k个节点
- 双指针
// 双指针:快慢指针 快指针先移动K步,然后快慢指针同时同速移动
for(int i=0;i<k;i++){
fast = fast.next
}
// 表示该目标节点为头结点
if(fast == null){
return head.next
}
if(fast.next !=null){
fast = fast.next
slow = slow.next
}
// 此时的slow不是倒数第k个节点,是倒数第k+1个节点
- 删除链表的倒数第n个节点
- 非递归
// 求链表的长度
ListNode prev= head;
int last = length(head)-n
if(last ==0){
return head.next}
// 求要删除节点的前节点
for(int i=0;i<last-1;i++){
prev= prev.next
}
prev.next =prev.next.next
return head
- 双指针
// 同上
- 合并有序链表
ListNode head = new ListNode(0)
ListNode res = head;
while(l1 !=null && l2!=null){
if(l1.val >= l2.val){
res.next = l2
l2 = l2.next
}else {
res.next = l1
l1 = l1.next
}
res = res.next
}
if(l1 !=null){
res.next = l1
}
if(l2 != null){
res.next = l2
}
return head.next
- 合并K个有序链表
leetcode 23
- 优先队列(最小堆)
详细解释
- 链表排序
归并排序 :先利用快慢指针找出链表的中点,然后分为两个链表,一直分,知道无法分为止,然后自底而上排序归并
时间O(NlogN) 空间O(logN)
ListNode fast =head.next
ListNode slow =head
while(fast!=null || fast.next !=null){
fast =fast.next.next
slow =slow.next
}
// 分割为两个链表
ListNode newList = slow.next
slow.next =null
ListNode left = sortInList(head)
ListNode right= sortInList(newList)
ListNode lhead = new ListNode(-1)
ListNode res = lhead
// 归并排序
while(left!=null &&right !=null){
if(left.val > right.val){
lhead.next = right
right =right.next
}else{
lhead.next = left
left = left.next
}
lhead = lhead.next
}
// 判断左右链表是否为空
lhead.next = left!=null?left:right
return res.next
- 判断链表是否为回文结构
- 寻找回文串的核心:从中心向两端扩展
- 单链表不能逆序遍历,将原始链表反转存入一条新的链表,然后比较
- 双指针(快慢指针):先找到中间节点,反转后半部分链表,然后比较节点值是否相等
- 栈
// 入栈
while(temp!=null){
stack.push(temp.val)
temp =temp.next
}
// 出栈(后半部分即可)
len>>=1
while(len-- >0){
if(head.val != stack.pop()){
return false
}
head =head.next
}
- 两个链表的第一个公共节点
输入:{1,2,3}{4,5}{6,7} 输出{6,7}
如果有公共节点,那么两个链表指针同时走到末尾,则
若两个链表长度不等,则head1+head2 = head2+head1
ListNode p1 =head1
ListNode p2 =head2
while(p1!=p2){
p1 = p1.next
p2 = p2.next
if(p1!=p2){
if(p1 ==null){
p1 = head2
}
if(p2 ==null){p2=head1}
}
return p1
}
双指针使用技巧
1. 快慢指针
快慢指针一般初始化为链表的头节点head,前进时快指针fast在前,慢指针slow在后
1) 链表中是否有环
2)链表中点(作用归并排序)
3)链表环的入口节点
4)寻找单链表中倒数第K个节点
2. 左右指针
左右指针一般运用在数组中,求两个索引值,初始化为left=0,right=len(arr)-1
1)二分搜索
2)反转数组
3)两数之和
4)滑动 窗口算法(字符串匹配)