目录
1.反转链表
给定一个单链表的头结点pHead,长度为n,反转该链表后,返回新链表的表头。如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
解析:关键在改变next指向
public class Solution {
public ListNode ReverseList(ListNode head) { //反转链表核心:改变next指向
// if(head == null) { //若为空链表,则返回null
// return null;
// }
// if(head.next == null) { //若链表只有一个节点,则无需翻转,翻转也为自身,返回head即可
// return head;
// }
ListNode prev = null;
ListNode cur = head;
while(cur != null) {
ListNode curNext = cur.next; //先保存反转前,当前cur的下一个值
cur.next = prev; //改变指向 ,此时完成第一个节点的指向
prev = cur; //继续向后移动改变后面节点的指向
cur = curNext;
}
return prev;
}
}
2.(m,n) 区间链表反转
给出的链表:1→2→3→4→5→NULL, m=2,n=4;
返回:1→4→3→2→5→NULL.
数据范围: 链表长度 :0<size≤1000,0<m≤n≤size,链表中每个节点的值满足∣val∣≤1000
时间复杂度 O(n),空间复杂度 O(1)。
解析:范围外节点指向保持不变,范围内的完成节点反转
public ListNode reverseBetween (ListNode head, int m, int n) {
ListNode dummyNode = new ListNode(-1); //定义一个虚拟头结点
dummyNode.next = head;
ListNode prev = dummyNode; // 定义一个前驱,指向虚拟头节点
for(int i=0; i<m-1; i++) {
prev = prev.next; //进入循环,prev始终指向m的前一个位置
}
ListNode cur = prev.next; //cur始终是prev的后面一个
ListNode curNext; //定义一个变量curNext 用于后面保存cur的下一个位置
for(int i=0; i < n-m; i++){ //当进入区间范围内,开始反转
curNext = cur.next;
cur.next = curNext.next; //cur的下一个节点指向 curNext下一个节点(因为curNext已经移走)
curNext.next = prev.next; //curNext移动到cur前面,也即是prev后面---》到此完成一个数的反转
prev.next = curNext; //prev继续向后移动,也即是当前的curNext位置
}
return dummyNode.next;
}
3.链表中的节点每 k 个一组翻转
链表中的节点每 k 个一组翻转,返回翻转后的链表,如果链表中的节点数不是 k 的倍数,将剩下的节点保持原样
数据范围: 0≤n≤2000 ,1≤k≤2000 ,链表中每个元素都满足:0≤val≤1000
空间复杂度 O(1),时间复杂度 O(n)如:给定的链表:1→2→3→4→5
对于 k = 2k=2 ,返回 :2→1→4→3→5
对于 k = 3k=3 , 返回:3→2→1→4→5
解析:取出k个节点进行翻转,因此首先是找到k个结点的集合,然后对每个结点集合进行翻转操作
public ListNode reverseKGroup (ListNode head, int k) {
if(head == null || head.next == null) {
return head; //若为空或只有一个节点,不需反转,返回头结点即可
}
ListNode end = head; //定义一个下标end,初始位置在head处
for(int i=0; i<k; i++) {
if(end == null) {
return head;
}
end = end.next;
}
ListNode newHead = reverse(head,end); //开始反转
head.next = reverseKGroup(end,k); //
return newHead;
}
private ListNode reverse(ListNode head,ListNode end) {
ListNode prev = null;
ListNode cur = head;
while(cur != end) { //当进入组内,执行反转
ListNode curNext = cur.next; //先记录下cur的下一个位置
cur.next = prev; //改变指向,也及时完成反转
prev = cur; //然后继续完成组内其余节点的反转
cur = curNext;
}
return prev;
}
4.合并两个递增的链表
两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序。
数据范围:0≤n≤1000,−1000≤节点值≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6}
解析:合并两个单链表,返回两个单链表头结点值小的那个节点(递归方法)
public class Solution {
public 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(list2.next,list1);
return list2;
}
}
}
新建一个数组用于接收两个链表,再对数组中元素排序,最后再将数组转化为链表
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) { //递归实现
if(list1==null) return list2; // list1 list2为空的情况
if(list2==null) return list1;
if(list1 == null && list2 == null){
return null;
}
//将两个两个链表存放在list中
ArrayList<Integer> list = new ArrayList<>(); //定义一个数组用于存放合并的链表
// 遍历存储list1
while(list1 !=null) {
list.add(list1.val);
list1 = list1.next;
}
// 遍历存储list2
while(list2 !=null){
list.add(list2.val);
list2 = list2.next;
}
Collections.sort(list); //对list中所有元素排序Collections.sorts(list)
// 将list转换为 链表
ListNode newHead = new ListNode(list.get(0));
ListNode cur = newHead;
for(int i=1;i<list.size();i++){
cur.next = new ListNode(list.get(i));
cur = cur.next;
}
// 输出合并链表
return newHead;
}
}
5.合并 k 个升序的链表
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
数据范围:节点总数满足 0≤n≤10^5; 链表个数满足1≤k≤10^5 ;
每个链表的长度满足1≤len≤200 ; 每个节点的值满足∣val∣<=1000
要求:时间复杂度 O(nlogk)
如输入:[{1,2},{1,4,5},{6}]------->{1,1,2,4,5,6}
解析:使用两两合并的方法
public class Solution {
public ListNode mergeKLists(ArrayList<ListNode> lists) {
if(lists == null || lists.size() == 0) return null;
return mergeSort(lists,0,lists.size()-1);
}
//将链表数组二分,left,right知道的是下标
public ListNode mergeSort(ArrayList<ListNode> lists,int left,int right) {
if(left >= right) {
return lists.get(left);
}
int mid = left + ((right-left) >> 1);
ListNode list1 = mergeSort(lists,left,mid);
ListNode list2 = mergeSort(lists,mid+1,right);
return merge(list1,list2);
}
public 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(list2.next,list1);
return list2;
}
}
}
6. 判断链表中是否有环
判断给定的链表中是否有环。有环则返回true,否则false。
数据范围:链表长度0≤n≤10000,链表中任意节点的值满足∣val∣<=100000
要求:空间复杂度 O(1),时间复杂度 O(n)
解析:定义快慢指针,从head开始出发,当二者相遇,则说明fast走完2圈,slow走完1圈
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null) return false;
ListNode fast = head; //定义一个快慢指针,开始都位于head处
ListNode slow = head;
while(fast !=null && fast.next !=null) { //循环条件:至少2各节点才能构成环
fast = fast.next.next;
slow = slow.next; //快慢指针分别走2步和1步
if(fast == slow) { //2个指针相遇
return true;
}
}
return false;
}
}
7.找出链表中环的入口结点
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:n≤10000,1<=结点值<=10000
空间复杂度 O(1),时间复杂度 O(n)
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode fast,slow;
fast = pHead;
slow = pHead;
while(fast != null && fast.next != null) { //先找到快慢指针相遇的位置
fast = fast.next.next;
slow = slow.next;
if(fast == slow) //此时说明有环存在
break;
}
if(fast == null || fast.next == null) return null; //若fast指向空,则不存在环
fast = pHead; //指针指向链表头部
while(fast != slow) {
fast = fast.next; //与第一次相遇的节点 相同速度出发,相遇节点为入口节点
slow = slow.next;
}
return fast;
}
8.找出链表中倒数最后k个结点
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围: 0≤n≤10^5,0≤ai≤10^9,0≤k≤10^9
空间复杂度 O(n),时间复杂度 O(n)
解析:用一个数组存放链表中所有元素,然后再根据下标寻找倒数第k个节点
public ListNode FindKthToTail (ListNode pHead, int k) {
//=====================方法1:================================//
ArrayList<ListNode> list = new ArrayList<>(); //创建一个列表用于存放链表元素
while(pHead != null) {
list.add(pHead);
pHead = pHead.next;
}
if(k > list.size() || k == 0)
return null;
return list.get(list.size()- k); //根据下标找到倒数第k个节点
}
解析:快指针先出发走K个节点,然后快慢指针在此基础上同时出发,当fast=null时,表明走完了链表,此时,slow所在的位置也就是倒数第几个位置
public ListNode FindKthToTail (ListNode pHead, int k) {
if(pHead == null) return pHead;
ListNode fast = pHead;
ListNode slow = pHead;
while(k != 0) { //快指针先走k步
if(fast == null) return null; //若fast为空返回null,不为空则继续向后移动
fast = fast.next;
k--;
}
while(fast != null) { //快慢指针一起移动
fast = fast.next;
slow = slow.next;
}
return slow;
}
9.删除链表的倒数第n个节点
给定一个链表,删除链表的倒数第 n 个节点,返回链表的头指针
给出的链表为:1→2→3→4→5, n= 2
删除了链表的倒数第 n个节点后:1→2→3→5.空间复杂度 O(1),时间复杂度O(n)
解析:同上,用快慢指针,考虑head为null的情况,和(n为链表)的情况
public ListNode removeNthFromEnd (ListNode head, int n) {
if(head == null) return null;
ListNode fast = head;
ListNode slow = head;
for(int i=0; i<n; i++) { //快指针先走n步
fast = fast.next;
}
//fast走到了最后一个null处
if(fast == null) { //fast == null说明fast以及走完了链表,也即是删除倒数第n(n是链表的长度)
return head.next; //如果删除的是第一个数,则需要返回head.next
}
while(fast.next != null) { //快慢指针一起向后走
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next; //删除倒数第n个节点 slow.next,此时更新 slow.next
return head;
}
10.两个链表的第一个公共结点
输入两个无环的单向链表,找出它们的第一个公共结点,若无则返回空。
数据范围:n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
解析方法1:用集合完成,将链表1放入set里,然后看集合set是否包含链表2中的元素,若包含则是二者的公共节点,不包含则返回null
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
HashSet set = new HashSet<>(); //创建一个set集合
while(pHead1 != null) {
set.add(pHead1); //将链表1中的元素放入set集合
pHead1 = pHead1.next;
}
while(pHead2 != null) {
if(set.contains(pHead2)) { //查看集合set里是否包含链表2里的元素
return pHead2; //包含则返回钙元素,也即是两个链表的公共节点
}
pHead2 = pHead2.next;
}
return null;
}
解析方法2:快慢指针:分别从两个链表同时遍历,走完自己再走另一个链表,当二者相遇时,此时节点即是公共节点
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode list1 = pHead1;
ListNode list2 = pHead2;
while(list1 != list2) {
list1 = (list1 == null)? pHead2: list1.next;
list2 = (list2 == null)? pHead1: list2.next;
}
return list1;
}
11.判断一个链表是否为回文结构
回文是指该字符串正序逆序完全一致。
数据范围: 链表节点数0≤n≤10^5,链表中每个节点的值满 ∣val∣≤10^7
如:
输入:{1,2,2,1} ------> 1->2->2->1 返回值:true
输入:{2,1} ------> 2->1 返回值:false
解析:创建一个列表list存放链表中的元素,通过比较链表和列表 从0下标位置和最后一个位置是否相等(链表头头往后,列表从后往前)比较对应元素是否相等,若都等则返回true
public boolean isPail (ListNode head) {
List<Integer> list = new ArrayList<>(); //创建一个list列表用于存放链表中的元素
if(head == null || head.next == null) return true;
while(head != null) { //把链表转为list列表
list.add(head.val);
head = head.next;
}
int j = list.size()-1;
for(int i =0; i<j; i++) {
if(!list.get(i).equals(list.get(j))) {
return false;
}
j--;
}
return true;
}