反转链表
Leetcode - 206 Reverse Linked List (Easy)
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
解法一:递归
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
解法二:遍历
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
判断单链表是否有环
Leetcode - 141 Linked List Cycle (Medium)
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
return true;
}
}
return false;
}
成对交换节点
Leetcode - 24 Swap Nodes in Pair (Medium)
题目描述:不能更改链表本身的值。
Given 1->2->3->4, you should return the list as 2->1->4->3.
public ListNode swapPairs(ListNode head) {
if ((head == null) || (head.next == null))
return head;
ListNode n = head.next;
head.next = swapPairs(head.next.next);
n.next = head;
return n;
}
奇偶链表
Leetcode - 328 Odd Even Linked List (Medium)
题目描述:将所有的奇数序号节点都放到偶数序号节点前面。
Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL
public ListNode oddEvenList(ListNode head) {
if(head == null) return null;
ListNode odd = head, even = head.next, evenHead = even;
while(even != null && even.next != null){
odd.next = odd.next.next;
even.next = even.next.next;
odd = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
反转链表 II
Leetcode - 92 Reverse Linked List II (Medium)
题目描述:反转 m 到 n,只通过一次遍历完成。
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
public ListNode reverseBetween(ListNode head, int m, int n) {
if(head == null) return null;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
for(int i = 0; i<m-1; i++) pre = pre.next;
ListNode start = pre.next;
ListNode then = start.next;
for(int i=0; i<n-m; i++){
start.next = then.next;
then.next = pre.next;
pre.next = then;
then = start.next;
}
return dummy.next;
}
删除链表的节点
Leetcode - 237 Delete Node in a Linked List (Easy)
题目描述:只给出需要删除的节点引用。
Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
删除链表的倒数第 n 个节点
Leetcode - 19 Remove Nth Node From End of List (Medium)
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
解题思路:可能会删除第 0 个节点,所以要使用尾节点。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
ListNode first = cur;
while(n-- >= 0){
first = first.next;
}
while(first != null){
first = first.next;
cur = cur.next;
}
cur.next = cur.next.next;
return dummyHead.next;
}
删除有序链表的重复节点
Leetcode - 83 Remove Duplicates from Sorted List (Easy)
题目描述:保留一个重复的节点。
Input: 1->1->2->3->3
Output: 1->2->3
public ListNode deleteDuplicates(ListNode head) {
ListNode current = head;
while (current != null && current.next != null) {
if (current.next.val == current.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
删除链表中的元素
Leetcode - 203 Remove Linked List Elements (Easy)
题目描述:删除链表中值为 val 的节点。
Input: 1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = head, prev = dummyHead;
while(cur != null){
if(cur.val == val){
prev.next = cur.next;
}else{
prev = prev.next;
}
cur = cur.next;
}
return dummyHead.next;
}
删除有序链表的重复节点 II
Leetcode - 82 Remove Duplicates from Sorted List II (Medium)
题目描述:删除所有的重复节点。
Input: 1->2->3->3->4->4->5
Output: 1->2->5
解法一:递归
public ListNode deleteDuplicates(ListNode head) {
if (head == null) return null;
if (head.next != null && head.val == head.next.val) {
while (head.next != null && head.val == head.next.val) {
head = head.next;
}
return deleteDuplicates(head.next);
} else {
head.next = deleteDuplicates(head.next);
}
return head;
}
解题二:迭代
public ListNode deleteDuplicates(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = head, prev = dummyHead;
while(cur != null){
ListNode next = cur.next;
while(next != null && next.val == cur.val){
cur = cur.next;
next = next.next;
}
if(prev.next != cur){
prev.next = next;
}else{
prev = cur;
}
cur = next;
}
return dummyHead.next;
}
两数相加
Leetcode - 2 Add Two Numbers (Medium)
题目描述:给定两个链表,每个链表表示一个整数,链表的每个节点表示一位数,将两个链表表示的整数相加得到新的链表。
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
解题思路:同时遍历两个链表,将相同位置的节点相加,注意加法的进位。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode cur = dummyHead;
int carry = 0;
while(l1 != null || l2 != null){
int x = (l1 != null) ? l1.val : 0;
int y = (l2 != null) ? l2.val : 0;
int sum = carry + x + y;
carry = sum / 10;
cur.next = new ListNode(sum % 10);
cur = cur.next;
if(l1 != null) l1 = l1.next;
if(l2 != null) l2 = l2.next;
}
if(carry > 0){
cur.next = new ListNode(carry);
}
return dummyHead.next;
}
两个链表的交点
Leetcode - 160 Intersection of Two Linked Lists (Easy)
题目描述:给定两个链表,求出两个链表相交的节点。
Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
Output: Reference of the node with value = 8
解题思路:两个指针分别在两个链表上向前移动,当其中一个指针移动到了链表的末尾,将该指针指向另一个链表的头节点,另一个指针同理,当两个指针指向同一个节点时,该节点即为链表的相交节点。
我在这里绕了一会,其实很简单,交换指针指向的链表就是为了计算出两个链表私有链的长度,比如上图中,当指针 a 从 链表 A 的头节点移动到了最后,将其指向链表 B 的头节点,此时指针 b 指向链表 B 的节点 5,再向前走一步,a 指针就指向了链表 B 的 节点 0,b 指针指向了链表 A 的头节点 4,最后两个指针同时向后移动,就能同时到达相交节点 8。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode a = headA;
ListNode b = headB;
while(a != b){
a = a == null? headB : a.next;
b = b == null? headA : b.next;
}
return a;
}
合并两个有序链表
Leetcode - 21 Merge Two Sorted Lists (Easy)
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
解法一:迭代
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode cur = dummyHead;
while(l1 != null && l2 != null){
if(l1.val > l2.val){
cur.next = l2;
l2 = l2.next;
}else{
cur.next = l1;
l1 = l1.next;
}
cur = cur.next;
}
if(l1 != null){
cur.next = l1;
}
if(l2 != null){
cur.next = l2;
}
return dummyHead.next;
}
解法二:递归
public ListNode mergeTwoLists(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;
}
}
回文链表
Leetcode - 234 Palindrome Linked List (Easy)
题目描述:判断链表表示的内容是否是回文数,时间复杂度 O(n),空间复杂度 O(1)。
Input: 1->2->2->1
Output: true
解题思路:使用快慢指针找到链表的中点,将中点后面的链反转,之后比较前面的链和反转后的链是否相同。
public boolean isPalindrome(ListNode head) {
ListNode slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
// 奇数
if(fast != null){
slow = slow.next;
}
slow = reverse(slow);
while(slow != null){
if(slow.val != head.val){
return false;
}
slow = slow.next;
head = head.next;
}
return true;
}
// 反转链表
public ListNode reverse(ListNode head){
ListNode pre = null;
while(head != null){
ListNode next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
重排序链表
Leetcode - 143 Reorder List (Medium)
题目描述:将链表 L0 -> L1 -> ... -> Ln-1 -> Ln
重排序成 L0 -> Ln -> L1 -> Ln-1 -> L2 -> Ln-2 -> ...
。
Given 1->2->3->4, reorder it to 1->4->2->3.
public void reorderList(ListNode head) {
if(head == null || head.next == null) return;
// 找到链表的中间节点
ListNode p1 = head;
ListNode p2 = head;
while(p2.next != null && p2.next.next != null){
p1 = p1.next;
p2 = p2.next.next;
}
// 反转后半部分链表
ListNode preMiddle = p1;
ListNode preCurrent = p1.next;
while(preCurrent.next != null){
ListNode cur = preCurrent.next;
preCurrent.next = cur.next;
cur.next = preMiddle.next;
preMiddle.next = cur;
}
// 重排序
p1=head;
p2=preMiddle.next;
while(p1!=preMiddle){
preMiddle.next=p2.next;
p2.next=p1.next;
p1.next=p2;
p1=p2.next;
p2=preMiddle.next;
}
}
找到链表环的第一个节点
Leetcode - 142 Linked List Cycle II (Medium)
解题思路:假设链表头是 X,环的第一个节点是 Y,slow 和 fast 第一次的交点是 Z。各段的长度分别是 a,b,c。slow 每次向前走一个节点,fast 每次向前走两个结点,第一次相遇时 slow 走过的距离为 a+b,fast 走过的距离为 a+b+c+b,因为 fast 的速度是 slow 的两倍,所以 fast 走的距离是 slow 的两倍,由 2(a+b) = a+b+c+b 可得 a = c。这时,让两个指针分别从 X 和 Z 开始走,每次都走一步,那么正好会在 Y 相遇,也就是环的第一个节点。
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null) return null;
ListNode slow = head.next;
ListNode fast = head.next.next;
while(slow != fast){
if(fast == null || fast.next == null || fast.next.next == null) return null;
slow = slow.next;
fast = fast.next.next;
}
slow = head;
while(slow != fast){
fast = fast.next;
slow = slow.next;
}
return slow;
}
链表排序
Leetcode - 148 Sort List (Medium)
题目描述:对给定的链表进行排序,时间复杂度 O(longn),并且空间复杂度为常数。
Input: 4->2->1->3
Output: 1->2->3->4
解题思路:归并排序。
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode pre = head, slow = head, fast = head;
while(fast != null && fast.next != null){
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
pre.next = null;
ListNode l1 = sortList(head);
ListNode l2 = sortList(slow);
return merge(l1, l2);
}
public ListNode merge(ListNode l1, ListNode l2){
ListNode head = new ListNode(0), cur = head;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
if(l1 != null){
cur.next = l1;
}
if(l2 != null){
cur.next = l2;
}
return head.next;
}
每 K 个一组反转链表
Leetcode - 25 Reverse Nodes in k-Group (Hard)
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null || head.next == null) return head;
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode pre = dummyHead, cur = head;
while(cur != null){
ListNode temp = pre;
for(int i = 0; i < k; i++){
if(temp.next == null){
return dummyHead.next;
}
temp = temp.next;
}
for(int i = 0; i < k - 1; i++){
temp = pre.next;
pre.next = cur.next;
cur.next = pre.next.next;
pre.next.next = temp;
}
pre = cur;
cur = cur.next;
}
return dummyHead.next;
}
旋转链表
Leetcode - 61 Rotate List (Medium)
nput: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
public ListNode rotateRight(ListNode head, int n) {
if (head==null||head.next==null) return head;
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode fast=dummy,slow=dummy;
int i;
for (i=0;fast.next!=null;i++)// 获取链表长度
fast=fast.next;
for (int j=i-n%i;j>0;j--) // 找到第 i-n%i 个节点
slow=slow.next;
fast.next=dummy.next; // 进行旋转
dummy.next=slow.next;
slow.next=null;
return dummy.next;
}
划分链表
Leetcode - 86 Partition List (Medium)
题目描述:将链表中小于给定值的节点都移动到前面,大于等于该值的节点顺序不变。
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
解题思路:将小于给定值的节点组成一个新的链表,大于等于给定值的节点也组成一个新的链表,之后将两个链表拼接。
public ListNode partition(ListNode head, int x) {
ListNode dummyHead1 = new ListNode(0);
ListNode dummyHead2 = new ListNode(0);
ListNode cur1 = dummyHead1, cur2 = dummyHead2;
while(head != null){
if(head.val < x){
cur1.next = head;
cur1 = cur1.next;
}else{
cur2.next = head;
cur2 = cur2.next;
}
head = head.next;
}
cur2.next = null; // 不能省略,否则可能会形成环
cur1.next = dummyHead2.next;
return dummyHead1.next;
}
合并 k 个有序链表
Leetcode - 23 Merge k Sorted Lists (Hard)
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
解题思路:这道题类似之前的合并两个有序链表,可以逐个两两合并,也可以每次取出链表头部的最小节点,我也是首先想到这种方法,但是效率有点低。能够通过优先级队列和分治进行优化。
解法一:优先级队列
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> queue= new PriorityQueue<ListNode>(lists.length,new Comparator<ListNode>(){
@Override
public int compare(ListNode o1,ListNode o2){
if (o1.val<o2.val)
return -1;
else if (o1.val==o2.val)
return 0;
else
return 1;
}
});
ListNode dummy = new ListNode(0);
ListNode tail=dummy;
for (ListNode node:lists)
if (node!=null)
queue.add(node);
while (!queue.isEmpty()){
tail.next=queue.poll();
tail=tail.next;
if (tail.next!=null)
queue.add(tail.next);
}
return dummy.next;
}
解法二:分治
public static ListNode mergeKLists(ListNode[] lists){
return partion(lists,0,lists.length-1);
}
public static ListNode partion(ListNode[] lists,int s,int e){
if(s==e) return lists[s];
if(s<e){
int q=(s+e)/2;
ListNode l1=partion(lists,s,q);
ListNode l2=partion(lists,q+1,e);
return merge(l1,l2);
}else
return null;
}
// 合并两个链表
public static ListNode merge(ListNode l1,ListNode l2){
if(l1==null) return l2;
if(l2==null) return l1;
if(l1.val<l2.val){
l1.next=merge(l1.next,l2);
return l1;
}else{
l2.next=merge(l1,l2.next);
return l2;
}
}
链表插入排序
Leetcode - 147 Insertion Sort List (Medium)
题目描述:将链表按照插入排序的方式进行排序。
Input: -1->5->3->4->0
Output: -1->0->3->4->5
public ListNode insertionSortList(ListNode head) {
if( head == null ) return null;
ListNode dummyHead = new ListNode(0);
ListNode cur = head;
ListNode pre = dummyHead;
ListNode next = null;
while( cur != null ){
next = cur.next;
while( pre.next != null && pre.next.val < cur.val ){
pre = pre.next;
}
cur.next = pre.next;
pre.next = cur;
pre = dummyHead;
cur = next;
}
return dummyHead.next;
}