1、删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
思路:
要删除链表中等于给定值的所有节点,我们可以定义一个结果链表,再结合尾插的方法来实现。
定义一个last引用,指向结果链表的最后一个节点,cur引用指向给定链表的第一个节点。遍历链表,如果节点的值与给定值val不相等,就把它尾插到结果链表。尾插的时候要考虑到两种情况,结果链表为空和不为空,这个在之前链表的那篇博客里我有说过,这里就不再罗嗦,需要注意的是,插入一个新节点后要记得更新结果链表的最后一个节点,即last引用的指向。
//定义一个节点(后边所有问题都会用到!)
public static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
//删除链表里所有值为val的结点
public ListNode removeElements(ListNode head, int val) {
ListNode result = null; //结果链表,没有结点
ListNode last = null; //记录结果链表的最后一个结点
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
if (cur.val != val) {
//把cur结点尾插到result链表上
cur.next = null;
if (result == null) {
result = cur;
} else {
last.next = cur;
}
//更新结果链表的最后一个结点
last = cur;
}
cur = next;
}
return result;
}
其实还有一种方法可以实现,我们直接在给定的链表上修改。依然是遍历整个链表,定义一个cur引用指向链表的第一个节点,如果当前节点的next引用保存的值,即下一个节点的值与给定值相等,我们就可以把当前节点的next引用指向它的下下一个节点,也就是next.next,如果不相等的话,指向下一个节点就可以了。
可是这样写的话会有一个问题,我们并没有判断第一个节点的值是否与指定值相等,其实很简单,我们可以再加一个判断,如果相等,返回头节点的下一个节点,不相等的话就返回头节点。
public ListNode removeElements2(ListNode head,int val){
if (head==null){
return null;
}
ListNode cur = head;
while (cur.next!=null){
if(cur.next.val!=val){
cur = cur.next;
}else {
cur.next = cur.next.next;
}
}
if (head.val==val){
return head.next;
}else {
return head;
}
}
2、反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路:
反转一个单链表依然可以借助定义一个结果链表和头插的方法来实现。
定义一个cur引用指向当前链表的头节点。遍历链表,取出一个节点,头插到结果链表中,链表的头插这里就不多说了,直接看代码。
//反转链表
public ListNode reverseList(ListNode head) {
ListNode result = null;
ListNode cur = head;
while(cur!=null){
ListNode next = cur.next;
cur.next = result;
result = cur;
cur = next;
}
return result;
}
还有一种方法也可以实现链表的反转。
利用三引用遍历的方法,就是再定义一个节点prev来保存当前节点的前驱,有了这个前驱结点,要逆置链表我们只需要将cur的引用指向prev就行,也就是图中橙色箭头部分。
这样操作之后,原来的链表就变成了下面这样:
然而很明显出现了一个问题,原来cur指向的D节点就找不到了,怎么办呢,我们可以再定义一个next节点来保存D节点,这就是三引用遍历的方法。
最后,三个引用依次往后走,就实现了链表的反转,如下图:
但是需要考虑一个问题,如果cur指向的是第一个节点,那么它的前驱就应该是空。
代码如下:
public ListNode reverseList2(ListNode head){
if (head==null){
return null;
}
ListNode prev = null;
ListNode cur = head;
while (cur!=null){
ListNode next = cur.next;
cur.next=prev;
prev=cur;
cur=next;
}
return prev;
}
3、合并两条链表
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路:
找两个引用cur1,cur2分别指向原来两条链表的第一个节点,定义一个结果链表result,再用一个last引用记录结果链表的最后一个节点。
当cur1和cur2都不为空的时候,遍历原来的两条链表,比较cur1和cur2的值,哪个小就把它尾插到结果链表中。尾插这里不用多讲,注意链表为空和不为空两种情况。
一旦cur1或者cur2中有一个变为null,就意味着有一条链表遍历结束,那么直接将另一条链表中的剩余数据插入到结果链表中即可。
考虑一种特殊情况,就是如果传进来的两条链表其中一条为空,那么结果链表直接返回另一条链表即可。
代码如下:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
ListNode cur1 = l1;
ListNode cur2 = l2;
ListNode result = null; //结果链表的第一个结点
ListNode last = null; //记录最后一个结点
while(cur1!=null && cur2!=null){
if(cur1.val <= cur2.val){
ListNode next = cur1.next;
//把cur1尾插到result上
cur1.next = null;
if(result==null){
result = cur1;
}else{
last.next = cur1;
}
last = cur1;
cur1 = next;
}else{
ListNode next = cur2.next;
//把cur2尾插到result上
cur2.next = null;
if(result==null){
result = cur2;
}else{
last.next = cur2;
}
last = cur2;
cur2 = next;
}
}
if(cur1!=null){
last.next = cur1;
}
if(cur2!=null){
last.next = cur2;
}
return result;
}