1. 反转单链表
public ListNode ReverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
ListNode nex=null;
while (cur!=null){
nex=cur.next;
cur.next=pre;
pre=cur;
cur=nex;
}
return pre;
}
2. 链表内指定区间反转
将一个节点数为 size 的链表 m 位置到 n 位置之间的区间反转
public ListNode reverseBetween (ListNode head, int m, int n) {
if(head ==null || head.next==null || m==n){
return head;
}
ListNode pre=null;
ListNode cur=head;
ListNode top=head;
ListNode nex=null;
ListNode p1=null;
int i=1;
while (cur!=null && i<m){
p1=cur;
cur=cur.next;
i++;
}
while (cur!=null && i<=n){
nex=cur.next;
cur.next=pre;
pre=cur;
cur=nex;
i++;
}
if(m==1 && nex==null){
return pre;
}else if(m==1){
top.next=cur;
return pre;
}else if(nex==null){
p1.next=pre;
return top;
}
p1.next=pre;
while (pre.next!=null){
pre=pre.next;
}
pre.next=nex;
return top;
}
public ListNode reverseBetween1 (ListNode head, int m, int n) {
if(head ==null || head.next==null || m==n){
return head;
}
//万一起始翻转点是表头,那么需要一个前置节点top
ListNode top=new ListNode(0);
top.next=head;
ListNode pre = top;
ListNode cur=head;
//遍历找到翻转的起始位置
for(int i = 1; i < m; i++){
pre=cur;
cur=cur.next;
}
//翻转,此处翻转的方式与上面不一样,相当于每次把nex换到pre位置后面
ListNode nex=null;
for(int i = m; i < n; i++){
nex = cur.next;
cur.next = nex.next;
nex.next = pre.next;
pre.next = nex;
}
return top.next;
}
![](https://img-blog.csdnimg.cn/af018c084bb441b6b367c5e3fadd389a.jpeg#pic_center)
3. 链表中的节点每k个一组翻转
BM3 链表中的节点每k个一组翻转
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序
public ListNode reverseKGroup(ListNode head, int k) {
ListNode end = head;
ListNode start = head;
int i = 1;
for (; i < k && end!=null; i++) {
if (end.next == null) {break;}
end = end.next;
}
//截止到这一步,如果链表长度超过K,此时start是第一段需要反转的第一个,end是第一段需要翻转的最后一个,
//长度不超过K直接返回
if (head==null || i < k || k == 1 ) {return head;}
//top是整个链表的起始点,cur是开始当前需要被翻转的节点,pre是前一个,nex是后一个
ListNode top = end;
ListNode pre = null;
ListNode cur = start;
ListNode nex=null;
while (true) {
int k1 = k;
while (k1 > 0) {
nex = cur.next;
cur.next = pre;
pre = cur;
cur = nex;
k1--;
if (end != null) {end = end.next;}
}
//到这一步,如果当前被翻转的这一组节点还剩下的节点数大于K,那么end指向下一组待翻转节点的尾部
//cur应该指向下一组待翻转节点的头部,start在翻转前指向这一组头部,现在是尾部
//所以此时的变换应该是这一组的start指向下一组的尾部end,然后start更新为下一组节点的头部
if (end != null) {start.next = end;start=cur;}
//如果下一组节点数不足K个,那么直接不需要再做翻转
else {start.next = cur;break;}
}
return top;
}
递归法:
public ListNode reverseKGroup (ListNode head, int k) {
//找到每次翻转的尾部
ListNode tail = head;
//遍历k次到尾部
for(int i = 0; i < k; i++){
//如果不足k到了链表尾,直接返回,不翻转
if(tail == null)
return head;
tail = tail.next;
}
//翻转时需要的前序和当前节点
ListNode pre = null;
ListNode cur = head;
//在到达当前段尾节点前
while(cur != tail){
//翻转
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
//当前尾指向下一段要翻转的链表
head.next = reverseKGroup(tail, k);
return pre;
}
4、删除有序链表中重复出现的元素-II
public ListNode deleteDuplicates (ListNode head) {
if(head==null){return null;}
ListNode top=new ListNode(0);
top.next=head;
ListNode pre=top;
ListNode cur=head;
boolean b=false;
while (cur!=null){
ListNode nex=cur.next;
while (nex!=null && cur.val== nex.val){
cur=nex; nex=nex.next;
b=true;
}
if(b){//遇到了重复的
if (nex==null){//重复节点持续到链表最后一个
pre.next=null;
return top.next;
}
pre.next=nex; cur=nex;
b=false;
}else {//没有遇到重复的
pre=cur;cur=nex;
}
}
return top.next;
}
public ListNode deleteDuplicates (ListNode head) {
if(head==null){return null;}
ListNode top=new ListNode(0);
top.next=head;
ListNode cur=top;
while(cur.next != null && cur.next.next != null){
//遇到相邻两个节点值相同
if(cur.next.val == cur.next.next.val){
int temp = cur.next.val;
//将所有相同的都跳过
while (cur.next != null && cur.next.val == temp)
cur.next = cur.next.next;
}
else
cur = cur.next;
}
return top.next;
}
5、合并两段有序链表
//合并两段有序链表
ListNode merge(ListNode pHead1, ListNode pHead2) {
//一个已经为空了,直接返回另一个
if(pHead1 == null)
return pHead2;
if(pHead2 == null)
return pHead1;
//加一个表头
ListNode head = new ListNode(0);
ListNode cur = head;
//两个链表都要不为空
while(pHead1 != null && pHead2 != null){
//取较小值的节点
if(pHead1.val <= pHead2.val){
cur.next = pHead1;
//只移动取值的指针
pHead1 = pHead1.next;
}else{
cur.next = pHead2;
//只移动取值的指针
pHead2 = pHead2.next;
}
//指针后移
cur = cur.next;
}
//哪个链表还有剩,直接连在后面
if(pHead1 != null)
cur.next = pHead1;
else
cur.next = pHead2;
//返回值去掉表头
return head.next;
}
6、单链表升序排序
方法一:归并排序
知识点1:分治,分治即“分而治之”,“分”指的是将一个大而复杂的问题划分成多个性质相同但是规模更小的子问题,子问题继续按照这样划分,直到问题可以被轻易解决;整个分治过程经常用递归来实现。
只需要找到中间个元素的前一个节点,将其断开,就可以将链表分成两个子链表,然后继续划分,直到最小,然后比较合并成一个链表,然后往上依次合并。
public class Solution {
//合并两段有序链表
ListNode merge(ListNode pHead1, ListNode pHead2) {
//一个已经为空了,直接返回另一个
if(pHead1 == null)
return pHead2;
if(pHead2 == null)
return pHead1;
//加一个表头
ListNode head = new ListNode(0);
ListNode cur = head;
//两个链表都要不为空
while(pHead1 != null && pHead2 != null){
//取较小值的节点
if(pHead1.val <= pHead2.val){
cur.next = pHead1;
//只移动取值的指针
pHead1 = pHead1.next;
}else{
cur.next = pHead2;
//只移动取值的指针
pHead2 = pHead2.next;
}
//指针后移
cur = cur.next;
}
//哪个链表还有剩,直接连在后面
if(pHead1 != null)
cur.next = pHead1;
else
cur.next = pHead2;
//返回值去掉表头
return head.next;
}
public ListNode sortInList (ListNode head) {
//链表为空或者只有一个元素,直接就是有序的
if(head == null || head.next == null)
return head;
ListNode left = head;
ListNode mid = head.next;
ListNode right = head.next.next;
//右边的指针到达末尾时,中间的指针指向该段链表的中间
while(right != null && right.next != null){
left = left.next;
mid = mid.next;
right = right.next.next;
}
//左边指针指向左段的左右一个节点,从这里断开
left.next = null;
//分成两段排序,合并排好序的两段
return merge(sortInList(head), sortInList(mid));
}
}
方法二:转化为数组排序
public ListNode sortInList (ListNode head) {
ArrayList<Integer> nums = new ArrayList();
ListNode p = head;
//遍历链表,将节点值加入数组
while(p != null){
nums.add(p.val);
p = p.next;
}
p = head;
//对数组元素排序
Collections.sort(nums);
//遍历数组
for(int i = 0; i < nums.size(); i++){
//将数组元素依次加入链表
p.val = nums.get(i);
p = p.next;
}
return head;
}
7、两两交换链表中的节点
LC24两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
- 法一:递归
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
}
- 法二:迭代
public ListNode swapPairs(ListNode head) {
if(head==null || head.next==null){
return head;
}
ListNode top= new ListNode(0,head.next);
ListNode next;
ListNode pre=top;
while (head!=null && head.next!=null){
pre.next=head.next;
next=head.next.next;
head.next.next=head;
head.next=next;
pre=head;
head=next;
}
return top.next;
}
8、双指针
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
- 双指针
- 如果有交点,从ha开头->ha结尾->hb开头->hb结尾
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA= pA == null ? headB : pA.next;
pB= pB == null ? headA : pB.next;
}
return pA;
}