1.链表逆序(leetcode 206.Reverse Linked List)esay
题目描述:已知链表头节点指针head,将链表逆序。
思路:从链表的头节点依次遍历,每遍历一个节点就进行逆序, 使用头插法进行逆序。
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = null;
while(head != null) {
ListNode nextTemp = head.next;
head.next = newHead;
newHead = head;
head = nextTemp;
}
return newHead;
}
}
2.链表部分逆序(leedcode92.Reverse Linked List2)medium
题目描述:已知链表头节点指针head,将链表从位置m到n逆序。
思路:需要完成m到n位置的链表逆序,其中逆序算法已经清楚,只需要确定开始逆序的位置(从起始节点后移m个位置),以及需要逆序的节点个数(m-n+1)。同时,需要记录开始逆序节点的前驱(pre_start),以便逆序完可以直接链接上。逆置段尾结点的后继也需要记录。需要注意一点,讨论m是否为1,如果从第一个节点开始逆置,则逆置完的链表段头节点即为起始节点。
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
if (head == null || head.next == null) return head;
ListNode pre_start, new_start, new_end;
pre_start = new_start = new_end = null;
ListNode list = head; //记录list
int length = n-m+1; //长度
int steps = m-1;
while(head!=null && steps!=0) {
pre_start = head;
head = head.next;
steps--; //head指针移动到m对应位置
}
new_end = head;
ListNode next_head = null;
while (head!=null && length!=0) {
next_head = head.next;
head.next = new_start;
new_start = head;
head = next_head;
length--; //逆序length个节点
}
new_end.next = head;
if (m == 1) {
list = new_start;
} else {
pre_start.next = new_start; //将pre_start链接上逆置后的链表段
}
return list;
3.求两个链表的交点(leetcode160.Intersection of Two Linked Lists)easy
题目描述:已知链表A的头节点指针headA,链表B的头结点指针headB,两个链表相交,求两个链表相交的交点
思路:1.使用set集合。遍历链表A,将A中节点对应的指针(地址)存入set中。遍历链表B,将B中节点对应的指针(地址),在set中查找,发现在set中的第一个节点地址,即是两个链表的起始交点。
2.若链表A与链表B相交,则只是相交节点的前面链表段不相同。分别计算链表A与B的长度,将较长的链表起点移动到与短链表起点对齐的地方,这样一同出发,相交点即是指针相同的交点。
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode listA = headA;
ListNode listB = headB;
int lengthA = 0;
int lengthB = 0;
while(headA != null) {
lengthA++;
headA = headA.next;
} // 计算链表A的长度
while(headB != null) {
lengthB++;
headB = headB.next;
} // 计算链表B的长度
int dis = 0;
if (lengthB > lengthA) {
dis = lengthB - lengthA;
while(dis > 0){
listB = listB.next;
dis--;
}
} else {
dis = lengthA - lengthB;
while(dis > 0){
listA = listA.next;
dis--;
}
} //将较长链表的起点移动到与短链表起点对齐的地方
while (listA!=listB && listA!=null && listB!=null) {
listA = listA.next;
listB = listB.next;
}
return listA;
}
}
4.链表求环(leetcode 141.Linked List Cycle)easy
问题描述:已知链表可能有环,若有环则返回true,否则返回false
思路:因为只需要判断是否有环,不用确定起始节点,所以方法较多,列举常见的两种。
1.利用set集合,依次将节点指针插入set中,当在set中查找,第一个在set中发现的节点地址,即是链表环的起点
2.从起点开始遍历,依次判断该节点的next是否为本身,如果是,则返回true;如果不是,则将该节点的next指针指向自己,并递归遍历其后面的链表。
代码实现:方法1:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null) return false;
Set nodeSet = new HashSet();
while(head != null) {
if (nodeSet.contains(head)) {
return true;
} else {
nodeSet.add(head);
head = head.next;
}
}
return false;
}
}
方法2:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if (null == head) return false;
if (head == head.next) return true;
ListNode n = head.next;
head.next = head; // 将指针指向自己
return hasCycle(n);
}
}
5.链表求环2(leetcode 142.Linked List Cycle2)medium
问题描述:已知链表可能有环,若有环则返回环起始节点,否则返回NULL
思路:1.简单的方法是,利用set集合实现。依次将节点指针插入set中,当在set中查找,第一个在set中发现的节点地址,即是链表环的起点。
2.相对巧妙的想法,利用快慢指针实现。见下面,fast与slow指针同时从起点X出发,其中fast是slow速度的两倍,当fast指针追到slow指针时,此时相遇于Z点。对于slow指针,走了a+b的路程。那么fast指针的路程为2(a+b),同时也为 a+b+c+b,即a+2b+c=2(a+b),得a=c。这时,如果从它们的交点Z出发的话,同时有一指针head从起点X出发,必会相交于Y点,也即为环的起始点。
代码实现:方法1
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
// if (null == head) return null;
Set nodeSet = new HashSet();
while(head != null) {
if (nodeSet.contains(head)) {
return head;
} else {
nodeSet.add(head);
}
head = head.next;
}
return null;
}
}
方法2:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = fast;
while (fast!=null && fast.next!=null && fast.next.next!=null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) { // fast与slow指针相交时
while (head != slow) {
head = head.next; // head指针从起始点出发
slow = slow.next;
}
return head;
}
}
return null;
}
}
6.链表划分(leetcode86.Partition List)medium
问题描述:已知链表头指针head与数值x,将所有小于x的节点放在大于等于x的节点前,且保存这些节点的原来的相对位置。如,划分前:1->4->3->2->5->2,x=3,划分后:1->2->2->4->3->5
思路:遍历链表,将小于x的节点插入到新链表less_head中,将大于等于x的节点插入到新链表more_head中,然后将more_head.next插入在less_head的末尾,返回less_head即可。
或者直接将原链表中大于x的节点移出并链接为新链表mhead,然后将mhead链接至原链表的结尾即可。
代码实现:方法1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode lhead = new ListNode(0); // 使用两个链表less_head和more_head来分别记录
ListNode less_head = lhead;
ListNode mhead = new ListNode(0);
ListNode more_head = mhead;
while(head!=null) {
if (head.val < x) {
lhead.next = head;
lhead = lhead.next;
} else {
mhead.next = head;
mhead = mhead.next;
}
head = head.next;
}
mhead.next = null;
lhead.next = more_head.next;
return less_head.next;
}
}
方法2:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode list = new ListNode(0); // 将原链表中大于x的值连接为一个链表mhead
list.next = head;
ListNode new_head = list;
ListNode more_head = new ListNode(0);
ListNode mhead = more_head;
while(list.next!=null) {
if (list.next.val < x) {
list = list.next;
} else {
more_head.next = list.next;
more_head = more_head.next;
list.next = list.next.next;
}
}
list.next = mhead.next;
more_head.next = null;
return new_head.next;
}
}
7.链表的深度拷贝(leetcode138.Copy List With Random Pointer)medium
问题描述:已知一个复杂的链表,链表节点除了有一个指向下一个节点的next指针,还有一个random指针,指向本链表的任意某个节点(也可能指向自己),求这个链表的深度拷贝(构造生成一个完全新的链表,即使原链表毁坏,新链表也可独立使用)
思路:本问题中,主要是一个random指针也需要拷贝下来。利用map来实现,其中map的key记录原链表的各个节点,value来保存新建的节点(val值为key中节点的值),对于新建的节点的next和random指向的节点位置,由对应的key获得。即,map.get(head).next = map.get(head.next)、map.get(head).random = map.get(head.random)。
代码实现:
/*
// Definition for a Node.
class Node {
public int val;
public Node next;
public Node random;
public Node() {}
public Node(int _val,Node _next,Node _random) {
val = _val;
next = _next;
random = _random;
}
};
*/
class Solution {
public Node copyRandomList(Node head) {
if (head == null) return null;
Map map = new HashMap<>();
Node iter = head;
while(iter != null) {
map.put(iter, new Node(iter.val));
iter = iter.next;
} // 将链表的各个节点指针存为map的key,value为该节点的拷贝。
iter = head;
while(iter != null) {
map.get(iter).next = map.get(iter.next);
map.get(iter).random = map.get(iter.random);
iter = iter.next;
}
return map.get(head);
}
}
8.排序链表的合并(2个)(leetcode21.Merge Two Sorted Lists)easy
问题描述:已知两个已排序链表头结点l1和l2,将这两个链表合并,合并之后仍是有序的,返回合并后链表头节点。
思路:本题相对简单,依次比较l1和l2的头结点,谁小就将谁插入新链表l中。最后返回l链表即可。
代码实现:方法1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode l = new ListNode(0);
ListNode head = l;
while(l1!=null && l2!=null) {
if (l1.val < l2.val) {
head.next = l1;
head = head.next;
l1 = l1.next;
} else {
head.next = l2;
head = head.next;
l2 = l2.next;
}
}
if (l1 != null) head.next = l1; // 如果l1还有剩余节点,直接链接到l后面
else head.next = l2; // l2还有剩余
return l.next;
}
}
方法2(递归实现):
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
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;
}
}
}
9.排序链表的合并(多个)(leetcode23.Merge K Sorted Lists)hard
问题描述:已知k个已排序链表头结点l1和l2,将这k个链表合并,合并之后仍是有序的,返回合并后链表头节点。
思路:本题相当于上一题难度变大,主要是如何分配这k个链表,让其分别两两合并,最后合并为一个整体。如果采用暴力破解,从第一个开始,合并第二个,然后再合并第三个。。。,共k个链表,平均每个链表有n个节点,则时间复杂度为:(n+n)+(2n+n)+(3n+n)+…+((k-1)n+n) = (1+2+3+…+k-1)n+(k-1)n = (k^2+k-1)/2*n = O(k^2 *n).
另一个思路,采用分治法,即两两进行合并。第一轮,进行k/2次,每次处理2n个;第二轮,进行k/4次,每次处理4n个,…,最后一次,进行k/(2logk)次,每次处理2logk*n个值。时间复杂度为:k/2 *2n+k/4 *4n+…+k/(2^logk) *(2^logk *n) = nk+nk+…+nk = O(klogk * n)。
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return mergeKLists(lists, 0, lists.length-1);
}
public ListNode mergeKLists(ListNode[] lists, int first, int end) {
if (first == end) return lists[first];
if (first > end) return null;
if (end-first == 1) return mergeTwoLists(lists[first], lists[end]);
int mid = (first + end)/2;
ListNode l1 = mergeKLists(lists, first, mid); // 递归合并前半部分
ListNode l2 = mergeKLists(lists, mid+1, end); // 递归合并后半部分
return mergeTwoLists(l1, l2);
}
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;
}
}
}
利用数组实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0) return null;
if (lists.length == 1) return lists[0];
if (lists.length == 2) return mergeTwoLists(lists[0], lists[1]);
int mid = lists.length/2;
ListNode[] lists1 = new ListNode[mid];
for (int i=0; i