基础部分
1.链表反转
public class T01_ReverseList {
//非递归方式反转链表
public ListNode reverse(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode last = null;
while (head != null) {
last = head.next;
head.next = pre;
pre = head;
head = last;
}
return pre;
}
//递归方式反转链表
public ListNode reverse2(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode temp = head.next;
ListNode newHead = reverse(temp);
temp.next = head;
head.next = null;
return newHead;
}
}
2.合并两个链表
public class T09_MergeList {
//不需要额外空间
public ListNode mergeList(ListNode list1, ListNode list2) {
if (list1 == null) {
return list2;
}
if (list2 == null) {
return list1;
}
if (list1.val < list2.val) {
list1.next = mergeList(list1.next, list2);
return list1;
} else {
list2.next = mergeList(list1, list2.next);
return list2;
}
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
ListNode dummyHead=new ListNode(-1);
ListNode cur=dummyHead;
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 dummyHead.next;
}
}
3.链表删除重复节点,重复节点保留
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return null;
}
ListNode cur = head;
while (cur.next != null) {
if (cur.next.val == cur.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
4.链表删除重复节点,重复节点不保留
public class T10_DeleteListNode {
public ListNode deleteNode(ListNode head) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode cur = dummyHead;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val == cur.next.next.val) {
int value = cur.next.val;
while (cur.next != null && cur.next.val == value) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
5.从尾到头打印链表
1.直接打印到控制台
import java.util.Stack;
public class T05FromTailToHead {
//递归的
public void printFromTailToHead(ListNode head) {
if (head == null) {
return;
}
printFromTailToHead(head.next);
System.out.print(head.val + " ");
}
//非递归的,使用一个stack来存储节点的值,再打印出栈的元素
public void printFromTailToHead2(ListNode head) {
Stack<Integer> stack = new Stack<>();
ListNode cur = head;
while (cur != null) {
stack.push(cur.val);
cur = cur.next;
}
while (!stack.isEmpty()) {
System.out.print(stack.pop() + " ");
}
}
}
2.结果放到一个ArrayList里
import java.util.ArrayList;
import java.util.Stack;
public class T05FromTailToHead2 {
//递归方式
ArrayList<Integer> arrayList = new ArrayList<>();
public ArrayList<Integer> printFromTailToHead(ListNode head) {
if (head != null) {
printFromTailToHead(head.next);
arrayList.add(head.val);
}
return arrayList;
}
//非递归方式
public ArrayList<Integer> printFromTailToHead2(ListNode head) {
Stack<Integer> stack = new Stack<>();
ListNode cur = head;
while (cur != null) {
stack.push(cur.val);
cur = cur.next;
}
ArrayList<Integer> list = new ArrayList<>();
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
}
6.返回链表倒数第k个节点
public class T06DaoshuKthNode {
//双指针法,一个指针先走k-1步,再一起走
public ListNode kthNode(ListNode head, int k) {
if (head == null || k <= 0) {
return null;
}
ListNode pre = head;
ListNode last = head;
while (k > 1) {
if (last.next == null) {
return null;
}
last = last.next;
k--;
}
while (last.next != null) {
pre = pre.next;
last = last.next;
}
return pre;
}
}
7.删除链表倒数第k个节点
public class T07_DeleteKthNode {
public ListNode deleteKth(ListNode head, int k) {
if (head == null || k <= 0) {
return null;
}
ListNode pre = head;
ListNode last = head;
while (k >= 1) {
last = last.next;
k--;
}
if (last == null) {//说明要删除第一个节点
head = head.next;
} else {
while (last.next != null) {
pre = pre.next;
last = last.next;
}
ListNode temp = pre.next;
pre.next = pre.next.next;
temp.next = null;
}
return head;
}
public ListNode deleteKth2(ListNode head, int k) {
if (head == null || k <= 0) {
return head;
}
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
ListNode cur = head;
ListNode last = head;
while (k > 1) {
if (last.next == null) {
return null;
}
last = last.next;
k--;
}
while (last.next != null) {
pre = cur;
cur = cur.next;
last = last.next;
}
pre.next = pre.next.next;
return dummyHead.next;
}
}
8.判断链表是否有环
public class T08_HasCircle {
//方法一:声明两个指针,一个指针走一次经过两个节点(快指针quick),另一个走一次经过一个节点(慢指针slow)
//方法说明:快指针走的比较快,若链表有环,则一定会追上慢指针,若无环,则会走到链表末端。
public boolean hasCircle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
//当快指针能够走到头表示无环
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
//方法二:用一个集合装ListNode,如果有重复元素,则有环
public boolean hasCircle2(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return true;
}
set.add(head);
head = head.next;
}
return false;
}
}
9.两个链表的第一个公共节点(无环)
public class T09_FirstInsectionNode {
//不需要额外空间,时间复杂度是m+n
public ListNode findNode(ListNode head1, ListNode head2) {
ListNode p1 = head1;
ListNode p2 = head2;
while (p1 != p2) {
p1 = p1 != null ? p1.next : head2;
p2 = p2 != null ? p2.next : head1;
}
return p1;
}
//需要额外空间,时间复杂度是m+n
public ListNode findNode2(ListNode head1, ListNode head2) {
ListNode p1 = head1;
ListNode p2 = head2;
HashSet<ListNode> hashSet = new HashSet<>();
while (p1 != null) {
hashSet.add(p1);
p1 = p1.next;
}
while (p2 != null) {
if (hashSet.contains(p2)) {
return p2;
}
p2 = p2.next;
}
return null;
}
}
10.两个单链表相加(逆序)
两个非空的链表用来表示两个非负的整数。位数是按照逆序的方式存储的,每个节点存储一位数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。返回的链表是和的逆序
public class T10_TwoListSum {
public ListNode twoListSum(ListNode l1, ListNode l2) {
ListNode res = new ListNode(-1);
ListNode cur = res;
int temp = 0;
int sum = 0;
while (l1 != null || l2 != null) {
int v1 = 0;
int v2 = 0;
if (l1 != null) {
v1 = l1.val;
l1 = l1.next;
}
if (l2 != null) {
v2 = l2.val;
l2 = l2.next;
}
sum = temp + v1 + v2;
temp = sum / 10;
cur.next = new ListNode(sum % 10);
cur = cur.next;
}
if (temp != 0) {
cur.next = new ListNode(temp);
}
return res.next;
}
}
11.两个单链表相加(正序)
public class T11_TwoListSum2 {
//将两个链表压栈,再弹栈,就可以得到逆序的节点
public ListNode listSum2(ListNode list1,ListNode list2){
ListNode l1=list1;
ListNode l2=list2;
Stack<ListNode> stack1=new Stack<>();
Stack<ListNode> stack2=new Stack<>();
while(l1!=null){
stack1.push(l1);
l1=l1.next;
}
while(l2!=null){
stack2.push(l2);
l2=l2.next;
}
int sum=0;
int flag=0;
ListNode dummyHead=new ListNode(-1);
ListNode cur=dummyHead;
while(!stack1.isEmpty()||!stack2.isEmpty()){
int v1=0;
int v2=0;
if(!stack1.isEmpty()){
v1=stack1.pop().val;
}
if(!stack2.isEmpty()){
v2=stack2.pop().val;
}
sum=flag+v1+v2;
flag=sum/10;
cur.next=new ListNode(sum%10);
cur=cur.next;
}
if(flag!=0){
cur.next=new ListNode(flag);
}
//再反转链表
return dummyHead.next;
}
public ListNode listSum(ListNode l1, ListNode l2) {
ListNode head1 = l1;
ListNode head2 = l2;
Stack<ListNode> s1 = new Stack<>();
Stack<ListNode> s2 = new Stack<>();
int length1 = 0;
int length2 = 0;
int temp = 0;//进位符
while (head1 != null) {
s1.push(head1);
head1 = head1.next;
length1++;
}
while (head2 != null) {
s2.push(head2);
head2 = head2.next;
length2++;
}
if (length1 > length2) {
while (!s1.isEmpty()) {
ListNode cur = s1.pop();
int v2 = s2.isEmpty() ? 0 : s2.pop().val;
int sum = temp + cur.val + v2;
cur.val = sum % 10;
temp = sum / 10;
}
if (temp != 0) {
ListNode newNode = new ListNode(temp);
newNode.next = l1;
return newNode;
} else {
return l1;
}
} else {
while (!s2.isEmpty()) {
ListNode cur = s2.pop();
int v1 = s1.isEmpty() ? 0 : s1.pop().val;
int sum = temp + v1 + cur.val;
cur.val = sum % 10;
temp = sum / 10;
}
if (temp != 0) {
ListNode newNode = new ListNode(temp);
newNode.next = l2;
return newNode;
} else {
return l2;
}
}
}
}
12.链表右移k个位置
public class T12_RotateKNode {
public ListNode rotate(ListNode head, int k) {
if (head == null || head.next == null) {
return head;
}
ListNode tail = head;
int length = 1;
while (tail.next != null) {
length++;
tail = tail.next;
}
tail.next = head;//注意这句话不能放在后面
int step = k % length;
ListNode p = head;
for (int i = 0; i < length - step - 1; i++) {
p = p.next;
}
ListNode newHead = p.next;
p.next = null;
return newHead;
}
}
13.返回有环链表的入口节点
public class T13_CircleNode {
//解法一:用hashset,如果有重复节点,那么重复节点就是入口节点
public ListNode circle(ListNode head) {
HashSet<ListNode> hashSet = new HashSet<>();
while (head != null) {
if (hashSet.contains(head)) {
return head;
}
hashSet.add(head);
head = head.next;
}
return null;
}
//解法二:快慢指针,快指针一次走两步,慢指针一次走一步,
// 快慢指针相遇时,将快指针指向head,一次走一步,快慢指针再次相遇的节点就是入口
public ListNode circle2(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
14.分隔链表
public class T14_PartitionNode {
//用两个链表,一个把小于x的节点穿起来,另一个把大于等于x的节点穿起来,
// 再将两个链表穿起来
public ListNode partition(ListNode head, int x) {
ListNode dummyHead1 = new ListNode(-1);
ListNode dummyHead2 = new ListNode(-1);
ListNode p1 = dummyHead1;
ListNode p2 = dummyHead2;
while (head != null) {
if (head.val < x) {
p1.next = head;
head = head.next;
p1 = p1.next;
} else {
p2.next = head;
head = head.next;
p2 = p2.next;
}
}
p1.next = null;
p2.next = null;
p1.next = dummyHead2.next;
return dummyHead1.next;
}
}
15.两两交换链表中的节点
public class T15_SwapPairs {
public ListNode swapNode(ListNode head) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
ListNode cur = head;
while (cur != null && cur.next != null) {
ListNode last = cur.next;
cur.next = cur.next.next;
last.next = cur;
pre.next = last;
pre = cur;
cur = cur.next;
}
return dummyHead.next;
}
}
16.随机链表复制
public class T16_CopyRandomList {
//需要额外空间
public Node copyRandomList(Node head) {
Node cur = head;
HashMap<Node, Node> hashMap = new HashMap<>();
while (cur != null) {
hashMap.put(cur, new Node(cur.val));
cur = cur.next;
}
cur = head;
while (cur != null) {
hashMap.get(cur).next = hashMap.get(cur.next);
hashMap.get(cur).random = hashMap.get(cur.random);
cur = cur.next;
}
return hashMap.get(head);
}
public Node copyRandomList2(Node head) {
if (head == null) {
return null;
}
Node cur = head;
Node next = null;
while (cur != null) {
next = cur.next;
cur.next = new Node(cur.val);
cur.next.next = next;
cur = next;
}
cur = head;
while (cur != null) {
cur.next.random = cur.random == null ? null : cur.random.next;
cur = cur.next.next;
}
cur = head;
Node res = head.next;
Node copyNode = null;
while (cur != null) {
copyNode = cur.next;
next = cur.next.next;
cur.next = next;
copyNode.next = next == null ? null : next.next;
cur = next;
}
return res;
}
}
17.翻转链表的m到n之间的节点
public class T17_ReverseMN {
//1->2->3->4->5->6->7 将m之前断开,n之后断开,再反转m到n的节点,最后拼接链表
// m n
//p q k h
public ListNode reverseList(ListNode head, int m, int n) {
ListNode p = null;
ListNode q = head;
while (m > 1) {
p = q;
q = q.next;
m--;
}
ListNode k = null;
ListNode h = head;
while (n > 0) {
k = h;
h = h.next;
n--;
}
k.next = null;
ListNode pre = null;
ListNode cur = q;
while (cur != null) {
ListNode last = cur.next;
cur.next = pre;
pre = cur;
cur = last;
}
if (p == null) {
q.next = h;
return pre;
} else {
p.next = pre;
q.next = h;
return head;
}
}
}
进阶部分
18.合并K个排序过的链表
public class T023 {
//归并
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
ListNode res = mergeLists(lists, 0, lists.length - 1);
return res;
}
private ListNode mergeLists(ListNode[] lists, int left, int right) {
if (left >= right) {
return lists[left];
}
int mid = left + (right - left) / 2;
ListNode LN = mergeLists(lists, left, mid);
ListNode RN = mergeLists(lists, mid + 1, right);
return merge(LN, RN);
}
private ListNode merge(ListNode n1, ListNode n2) {
if (n1 == null) {
return n2;
}
if (n2 == null) {
return n1;
}
if (n1.val <= n2.val) {
n1.next = merge(n1.next, n2);
return n1;
} else {
n2.next = merge(n1, n2.next);
return n2;
}
}
//优先队列
public ListNode mergeKLists2(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);
for (ListNode node : lists) {
if (node != null) {
pq.add(node);
}
}
ListNode dummyHead = new ListNode(-1);
ListNode cur = dummyHead;
while (!pq.isEmpty()) {
ListNode top = pq.poll();
cur.next = top;
cur = cur.next;
if (top.next != null) {
pq.add(top.next);
}
}
return dummyHead.next;
}
}
19.链表排序
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
输入: 4->2->1->3
输出: 1->2->3->4
public class T148排序链表 {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode pre=null;
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){// 取中点
pre=slow;
slow=slow.next;
fast=fast.next.next;
}
pre.next=null;// 截断 中点之前
return merge(sortList(head),sortList(slow));// slow 是中点
}
private ListNode merge(ListNode list1,ListNode list2){
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
if(list1.val<list2.val){
list1.next=merge(list1.next,list2);
return list1;
}else{
list2.next=merge(list1,list2.next);
return list2;
}
}
}
每k个节点链表反转
重排链表
插入排序