反转链表: 输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
假设链表为 1→2→3→∅,反转后为 ∅←1←2←3。
在遍历链表时,将当前节点的next指针改为指向前一个节点。
由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。
在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode curr=head;
while (curr!=null){
ListNode next=curr.next;
curr.next=pre;//让当前节点指向上一个节点
pre=curr;//将当前节点保存到上一个当中
curr=next;
}
return pre;
}
NowCoder
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null && l2 == null) return null;
ListNode head=new ListNode(-1);
ListNode temp=head;
while (l1!=null && l2 !=null){
if(l1.val > l2.val){
temp.next=l2;
l2=l2.next;
}else {
temp.next=l1;
l1=l1.next;
}
temp=temp.next;
}
temp.next= l1==null? l2:l1;
return head.next;
}
https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/
public ListNode deleteNode(ListNode head, int val) {
if(head == null) return null;
if(head.val==val) return head.next;
//初始化双指针
ListNode pre=head;
ListNode cur=head.next;
//根据题意绝对找得到 所以无需判断是否为空
while (cur.val != val){
pre=pre.next;
cur=cur.next;
}
pre.next = cur.next;
return head;
}
NowCoder
public ListNode deleteDuplication(ListNode pHead){
if(pHead == null || pHead.next == null) return pHead;
// 自己构建辅助头结点
ListNode head = new ListNode(Integer.MIN_VALUE);
head.next = pHead;
ListNode pre = head;
ListNode cur = head.next;
while(cur!=null){
if(cur.next != null && cur.next.val == cur.val){
// 相同结点一直前进
while(cur.next != null && cur.next.val == cur.val)
cur = cur.next;
// 退出循环时,cur 指向重复值,也需要删除,而 cur.next 指向第一个不重复的值
cur = cur.next;
// pre 连接新结点
pre.next = cur;
}else{
pre = cur;
cur = cur.next;
}
}
return head.next;
}
链表的中间节点
NowCoder
使用双指针,一个快指针 fast 每次移动两个节点,一个慢指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。查找有环链表中环的入口结点。当快慢指针相遇时,我们可以判断到链表中有环,这时重新设定一个新指针指向链表的起点,且步长与慢指针一样为1,则慢指针与“新”指针相遇的地方就是环的入口。
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
ListNode slow=head;
ListNode fast=head;
boolean flag=false;
while (fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast == slow){
flag=true;
break;
}
}
if(!flag) return null;//遍历完了都没找到说明为null
//这个时候定义一个新节点从开始遍历
ListNode cur=head;
while (cur!=slow){
cur=cur.next;
slow=slow.next;
}
return cur;
}
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
设链表的长度为 N。设置两个指针 fast和 slow,先让 fast移动 K 个节点,则还有 N - K 个节点可以移动。此时让 fast和 slow同时移动,可以知道当 fast移动到链表结尾时,slow移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。
public ListNode getKthFromEnd(ListNode head, int k) {
if(head == null) return null;
ListNode slow=head;
ListNode fast=head;
for (int i = 0; i < k; i++)
fast=fast.next;
while (fast !=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
力扣地址:判断一个链表是否是回文结构
方法一:通过栈来把所有的数字压进去 然后在遍历一遍依次比较
方法二:快慢指针 + 反转链表。通过快慢指针先让慢指针走到中点 然后反转剩下的部分。反转完成之后 然后再定义一个变量从头开始依次和反转那部分的链表进行比较是否相等
public boolean isPalindrome(ListNode head) {
if(head == null) return false;
if(head.next==null) return true;
ListNode slow=head;
ListNode fast=head;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
//遍历完成之后,slow指向中间节点
ListNode pre=null;
ListNode cur=slow;
while(cur!=null){
ListNode next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
//反转完成之后 pre就为1->2 然后依次遍历2个链表相互比较
ListNode p2=pre;
ListNode p1=head;
while(p2!=null){
if(p1.val != p2.val) return false;//只要有一个不相等则返回false
p1=p1.next;
p2=p2.next;
}
return true;
}
相交链表: 找到两个单链表相交的起始节点。
思想:先统计两个链表的长度,然后相减这就是他们的步长差,然后让长的先走步长差的长度,然后两个链表再一起移动,相交则为相交节点
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB==null) return null;
ListNode l1=headA;
ListNode l2=headB;
int countA=0,countB=0;
while(l1!=null){
countA++;
l1=l1.next;
}
while(l2!=null){
countB++;
l2=l2.next;
}
ListNode a=headA;
ListNode b=headB;
int skip=Math.abs(countA-countB);
if(countA>countB)
while(skip>0) {
a=a.next;
skip--;
}
else if(countA<countB)
while(skip>0) {
b=b.next;
skip--;
}
while(a!=null && b!=null){
if(a==b) return a;
a=a.next;
b=b.next;
}
return null;
}
力扣地址
思路:先统计有多少个数,然后判断需要移动几个位置,让他变成循环链表,然后移动指定的步长,然后破除循环
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head==null)
return null;
else {
ListNode node=head;
ListNode listNode=new ListNode(0);
int count=0;//总共多少数
while (node.next!=null){
node=node.next;
count++;
}
count++;
int flag=count - k % count+1;//移动几个位置
node.next=head;//变成循环链表
ListNode temp=head;
for (int i = 1; i < flag; i++)
temp=temp.next;
listNode.next=temp;//让新链表头结点指向当前的头结点
//破除循环链表
ListNode cur=listNode;
for (int i = 0; i < count; i++)
cur=cur.next;
cur.next=null;
return listNode.next;
}
}
}
将单向链表按某值划分成左边小、中间相等、右边大的形式
思路:定义6个空的链表指针,分别为左中右,然后比较大小放在对应的链表位置,最终串起来就行。
这是一道简单的分隔链表:对链表进行分隔,使得所有 小于 x
的节点都出现在 大于或等于 x
的节点之前。
public ListNode partition(ListNode head, int x) {
if(head == null) return null;
ListNode left=new ListNode(-1);
ListNode lp=left;
ListNode right=new ListNode(-1);
ListNode rp=right;
while(head!=null){
if(head.val < x){
lp.next=head;
lp=lp.next;
}else {
rp.next=head;
rp=rp.next;
}
head=head.next;
}
rp.next=null;//让大的一方下一个指向null 免得造成了循环链表
//遍历分组完成 然后判断连个组有不有空的,然后串起来
if(left.next == null) return right.next;//如果左边分组为空 则直接返回右边的
if(right.next == null) return left.next;//如果右边分组为空 则直接返回左边的
lp.next=right.next;//否则就串起来
return left.next;
}