目录
1.移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
方法1:
创建一个头结点,遍历一遍所给链表,将不等于val的节点增加到新的链表上,返回头结点的下一个节点的引用
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode Nhead=new ListNode(0);
ListNode cur1=head;
ListNode cur2=Nhead;
while(cur1!=null){
if(cur1.val!=val){
cur2.next=cur1;
cur2=cur2.next;
}
cur1=cur1.next;
}
cur2.next=null;
return Nhead.next;
}
}
方法2:
创建一个头结点(哨兵位),让哨兵位的下一个节点指向原头结点的引用,创建一个cur引用指向哨兵位,每次判断cur的下一个节点的值是否为val,如果是就将cur.next指向cur.next.next,否则cur=cur.next
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode Nhead=new ListNode(0);
Nhead.next=head;
ListNode cur=Nhead;
while(cur.next!=null){
if(cur.next.val==val){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
return Nhead.next;
}
}
方法3:
使用递归,如果head为null就返回null,否则head的下一个引用再调用removeElements方法,如果head.val==val,就返回head.next,否则返回head。
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null){
return null;
}
head.next=removeElements(head.next,val);
if(head.val==val){
return head.next;
}else{
return head;
}
}
}
2.反转一个单链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
方法1:
使用三个指针,分别指向连续的三个节点,将中间节点的下一个引用指向上一个节点,将上一个指针指向中间节点,中间指针指向下一个节点,下一个指针指向下一个节点的下一个引用
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode cur=head;
ListNode next=head.next;
ListNode prv=null;
while(next!=null){
cur.next=prv;
prv=cur;
cur=next;
next=next.next;
}
cur.next=prv;
return cur;
}
}
3.链表的中间节点
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
方法1:
快慢指针,快慢指针开始均指向头结点,快指针每次走两步,慢指针每次走一步,当快指针为空或者快指针的下一个节点为空时,返回慢指针
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
return slow;
}
}
4.返回倒数第K个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值
方法1:
先创建一个指针,让他先走k步,再创建一个指针指向头结点,两个指针同时走,当快指针为空时,慢指针指向的节点就为倒数第K个节点
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode cur=head;
while(k>0){
cur=cur.next;
k--;
}
ListNode prv=head;
while(cur!=null){
cur=cur.next;
prv=prv.next;
}
return prv.val;
}
}
5.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
方法1:
如果list1或者list2中有一个节点为空,就返回另外一个节点,没有就将小的一个节点的下一个节点使用递归调用mergeTwoLists方法,找到剩下的较小的节点返回
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
if(list1.val<list2.val){
list1.next=mergeTwoLists(list1.next,list2);
return list1;
}else{
list2.next=mergeTwoLists(list1,list2.next);
return list2;
}
6.链表分割
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
方法1:
分别创建两个头结点,一个用来存储小于的,一个用来存储大于的,然后两个链表拼接,返回存储小于的头结点的下一个节点。
注意:当存储大于等于的头节点的下一个节点不为空时,需要将最后一个节点的下一个节点置为空,因为原链表的最后一个节点的值可能是小于目标值的
如果第二个链表除头结点外,没有其他节点,那么直接返回原链表的头结点
public class Partition {
public ListNode partition(ListNode pHead, int x) {
if(pHead==null){
return pHead;
}
ListNode NHead1=new ListNode(-1);
ListNode NHead2=new ListNode(-2);
ListNode cur1=NHead1;
ListNode cur2=NHead2;
ListNode cur=pHead;
while(cur!=null){
if(cur.val<x){
cur1.next=cur;
cur1=cur1.next;
}else{
cur2.next=cur;
cur2=cur2.next;
}
cur=cur.next;
}
cur1.next=NHead2.next;
if(NHead2.next!=null){
cur2.next=null;
}
if(NHead1.next==null){
return pHead;
}
return NHead1.next;
}
}
7.链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
方法1:
判断链表是否为空,利用快慢指针找到中间节点,将后面节点的指向反转,创建一个引用指向头结点,与为节点同时开始走,如果两者的值不同,就返回false,当cur1指向的下一个节点是slow指向的节点并且cur1与slow指向的节点值相同时,返回true,当cur1==slow时也返回true
public class PalindromeList {
public boolean chkPalindrome(ListNode A) {
ListNode fast=A;
ListNode slow=A;
if(A==null){
return true;
}
while(fast.next!=null&&fast.next.next!=null){
fast=fast.next.next;
slow=slow.next;
}
ListNode cur=slow;
slow=slow.next;
while(slow.next!=null){
ListNode curN=slow.next;
slow.next=cur;
cur=slow;
slow=curN;
curN=curN.next;
}
slow.next=cur;
ListNode cur1=A;
while(slow!=cur1){
if(slow.val!=cur1.val){
return false;
}
if(cur1.next==slow&&cur1.next.val==slow.val){
return true;
}
cur1=cur1.next;
slow=slow.next;
}
return true;
}
}
8.相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
方法1:
两个链表各循环一遍,记录链表长度,让长的链表先走长度的差值,然后同时走,两个指针的指向同一个节点时,那么该节点就是链表相交的节点
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int count1=0;
int count2=0;
ListNode cur1=headA;
ListNode cur2=headB;
while(cur1!=null){
cur1=cur1.next;
count1++;
}
while(cur2!=null){
cur2=cur2.next;
count2++;
}
if(count1>count2){
int tmp=count1-count2;
cur1=headA;
cur2=headB;
while(tmp>0){
cur1=cur1.next;
tmp--;
}
}else{
int tmp=count2-count1;
cur2=headB;
cur1=headA;
while(tmp>0){
cur2=cur2.next;
tmp--;
}
}
while(cur1!=cur2&&cur1!=null&&cur2!=null){
cur1=cur1.next;
cur2=cur2.next;
}
return cur1;
}
}
9.环形链表1
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
方法:
利用快慢指针,快指针一次走两步 ,慢指针一次走一步,当快慢指针相遇,说明是环形链表,当快指针走到空,说明不是环形链表
public class Solution {
public boolean hasCycle(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;
}
}
10.环形链表2
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
方法
快慢指针找到第一次相遇的节点,将慢指针指向头结点,快慢指针同时一步一步走,直到相遇,该相遇点就是环入口点
公式:2(L+X)=L+NC+X;
L为未进环的长度,X为在环里走的长度,C为一圈环的长度,N为快指针走的圈数
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
slow=head;
while(true){
if(slow==fast){
return slow;
}
slow=slow.next;
fast=fast.next;
}
}
}
return null;
}
}