目录
一、反转链表
leetcode(206题)
方法一:头插法
我们根据最基本的想法,将链表的每个节点按顺序头插在新的节点上。看代码
public static ListNode reverse1(ListNode head){
ListNode prev=null;
ListNode cur=head;
while(cur!=null){
ListNode next=cur.next;
cur.next=prev;
prev=cur;
cur=next;
}
return prev;
}
其实我们常用的用三个指针(引用),遍历整个链表来反转的方法,其本质上就是头插法。
方法二:递归法
先看代码
public static ListNode reverse3(ListNode head){
//返回结束的条件
if(head==null||head.next==null){
return head;
}
//递归的部分
ListNode newhead=reverse3(head.next);
head.next.next=head;
head.next=null;
return newhead;
}
递归的思想就是将原链表的下一个节点,再次馈送到递归函数中。例如1=>2=>3=>4=>5=>null这个链表,经过 ListNode newhead=reverse3(head.next);之后变成了null<=2<=3<=4<=5,同时head也指向1,1指向2。然后我们需要将2指向1,然后将1指向null,然后返回新的头就行了。也就是通过head.next.next=head;head.next=null;return newhead;完成链表的反转。
二、删除指定元素的所有节点
leetcode(203题)
方法一:尾插法
思想就是遍历整个链表,如果该节点的值和给定值不同,就将该值尾插到一个新的链表之中。主要是尾插部分的实现。
该尾插方法的不同之处在于,为了保证下次遍历,需要将每次插入的元素的指向改为null。
public ListNode removeElements(ListNode head, int val){
ListNode fakehead=null;
ListNode cur=head;
while(cur!=null){
ListNode next=cur.next;
if(cur.val!=val){
fakehead=lastAdd(fakehead,cur);
}
cur=next;
}
return fakehead;
}
private static ListNode lastAdd(ListNode fakehead, ListNode cur) {
if(fakehead==null){
fakehead=cur;
fakehead.next=null;
}else{
ListNode c=fakehead;
while (c.next!=null){
c=c.next;
}
c.next=cur;
cur.next=null;
}
return fakehead;
}
小技巧:假头法
使用假头法改进这个尾插法
public ListNode removeElements(ListNode head, int val){
ListNode fake=new ListNode(937854328);
ListNode c=head;
ListNode newlast=fake;
while(c!=null){
ListNode n=c.next;
if(c.val!=val){
newlast.next=c;
c.next=null;
newlast=c;
}
c=n;
}
return fake.next;
}
方法二:遍历修改
遍历每一个元素,如果该节点的值等于给定值,将该节点的前一个节点指向下一个节点,如果是头节点则将头节点后移。该方法需要考虑的情况比较多,相对比较复杂容易弄混。
public ListNode removeElements(ListNode head, int val) {
ListNode newHead=head;
ListNode prev=null;
ListNode cur=head;
while (cur!=null){
if(cur.val==val){
if(prev==null){
newHead=newHead.next;
}else{
prev.next=cur.next;
}
}else{
prev=cur;
}
cur=cur.next;
}
return newHead;
}
小技巧:假头法
使用一个假头,这样链表的每个节点都有一个前置节点,并且就算链表为空加上假头后也不为空了。使用假头法来改进方法二,代码如下:
public ListNode removeElements2(ListNode head, int val){
ListNode fakehead=new ListNode(937854328);
fakehead.next=head;
ListNode prev=fakehead;
ListNode cur=head;
while(cur!=null){
if(cur.val==val){
prev.next=cur.next;
}else{
prev=cur;
}
cur=cur.next;
}
return fakehead.next;
}
我们对于假头法的使用需要分清楚情况,像这种情况下使用假头法就可以有效的简化问题。
三、升序链表合并
leetcode(21题)
方法 :首先我们需要想清楚,怎么合并两个升序的链表。我们可以遍历两个链表,将小节点的放到新链表上,然后将小的节点的链表向后移再进行比较,再将小的节点放在新链表的末尾然后再后移。直到一个链表为空,将不为空的链表接在新链表的尾节点上。同时我们需要考虑特殊情况。代码如下:
public ListNode mergeTwoLists(ListNode list1, ListNode list2){
ListNode newhead=null;//设定新链表的头
ListNode c1=list1;
ListNode c2=list2;
ListNode newlast=null;//由于链表末尾元素是尾插进去的,我们可以用一个节点来记录末尾,这样就不用遍历找末尾
if(c1==null){
return c2;
}
if(c2==null){
return c1;
}
while(c1!=null && c2!=null){
if(c1.val<c2.val){
if(newhead==null){
newhead=c1;
newlast=c1;//此时只有一个元素,头就是尾
}else{
newlast.next=c1;
newlast=c1;
}
c1=c1.next;
}else{
if(newhead==null){
newhead=c2;
newlast=c2;//此时只有一个元素,头就是尾
}else{
newlast.next=c2;
newlast=c2;
}
c2=c2.next;
}
}
if(c1==null){
newlast.next=c2;
}
if(c2==null){
newlast.next=c1;
}
return newhead;
假头法改进
public ListNode mergeTwoLists2(ListNode list1, ListNode list2){
ListNode newhead = new ListNode(937854328);
ListNode c1 = list1;
ListNode c2 = list2;
ListNode newlast = newhead;
while (c1 != null && c2 != null) {
if (c1.val < c2.val) {
newlast.next=c1;
c1 = c1.next;
} else {
newlast.next=c2;
c2 = c2.next;
}
newlast=newlast.next;
}
if (c1 == null) {
newlast.next = c2;
} else {
newlast.next = c1;
}
return newhead.next;
}
四、指定数值分割链表
题目链接
https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?
方法:遍历整个链表,如果当前的值小于给定值则存入链表1,否则存入链表2,然后将两个链表连接在一起就行了。代码如下:
public ListNode partition(ListNode pHead, int x) {
ListNode fakehead1=new ListNode(937854328);
ListNode last1= fakehead1;
ListNode fakehead2=new ListNode(1357820039);
ListNode last2=fakehead2;
ListNode c=pHead;
while(c!=null){
if(c.val<x){
last1.next=c;
last1=c;
}else{
last2.next=c;
last2=c;
}
c=c.next;
}
last1.next=null;
last2.next=null;
last1.next=fakehead2.next;
return fakehead1.next;
}
五、删除有序链表的重复节点
题目链接
https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?
方法一:遍历法
首先我们知道这是一个有序的链表,也就是说,这个链表的重复节点都是连在一块的。我们可以用三个指针来遍历整个链表,如果该节点的值不等于下个节点的值,我们就直接向下走,如果相等的话,我们就将next指针逐个往后移,一直移到不等于该节点的节点处,并将prev指针的指向改为该节点。同时需要考虑特殊情况,这样我们就实现了重复节点的删除。代码如下:
public ListNode deleteDuplication(ListNode pHead) {
if(pHead==null){
return null;
}
ListNode fakehead=new ListNode(937854328);
fakehead.next=pHead;
ListNode p=fakehead;
ListNode c=pHead;
ListNode n=c.next;
while(c.next!=null){
if(c.val!= n.val){
p=c;
c=n;
n=n.next;
}else{
while(c.val==n.val){
n=n.next;
if(n==null){
p.next=null;
return fakehead.next;
}
}
p.next=n;
c=n;
n=n.next;
}
}
return fakehead.next;
}
方法二:递归法
递归法的代码较为简洁,但是递归法难度更高。我们需要知道,函数的功能需要删除重复的节点,于是当节点为null或者节点个数为1个的时候哦我们可以直接返回。当当前节点与下一个节点的值不同时,我们可以保留该节点,并且将该节点指向去重后的以下个节点为头的链表。当当前的节点的值与下个节点的值相同的时候,我们需要一直跳到下个不相等的节点或者为null的节点,我们返回去重后的以不相等的节点为头的链表。实际还是在节点为空或者一个的时候返回。
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
// 递归出口:当「输入节点为空」或者「不存在下一节点」,直接返回
if (pHead == null || pHead.next == null) return pHead;
if (pHead.val != pHead.next.val) {
// 若「当前节点」与「下一节点」值不同,则当前节点可以被保留
pHead.next = deleteDuplication(pHead.next);
return pHead;
} else {
// 若「当前节点」与「下一节点」相同,需要跳过「值相同的连续一段」
ListNode tmp = pHead;
while (tmp != null && tmp.val == pHead.val) tmp = tmp.next;
return deleteDuplication(tmp);
}
}
}