一,203.移除链表元素
- 直接使用原来的链表来进行删除操作
移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。
所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。所以要分成头节点和非头节点来讨论,对于非头节点,通过 cur=cur.next;的形式来不断更新和遍历。
(1)分成头节点和非头节点
第一种写法:用两个指针来指向非头节点。一个指向前一节点,一个指向当前节点。双指针的思想
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null&&head.val==val)
head=head.next;
if(head==null)
return head;
ListNode pre=head;
ListNode cur=head.next;
while(cur!=null)
{
if(cur.val==val)
{
pre.next=cur.next;
}
else
{
pre=cur;
}
cur=cur.next;
}
return head;
}
}
第二种写法:用一个指针来指向非头节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null&&head.val==val)
head=head.next;
ListNode cur=head;
while(cur!=null)
{
while(cur.next!=null&&cur.next.val==val)
{
cur.next=cur.next.next;
}
cur=cur.next;
}
return head;
}
}
此时需要用while循环,而不能直接用if,否则会报错
可以改成如下代码
while(cur!=null&&cur.next!=null)
{
if(cur.next.val==val)
{
cur.next=cur.next.next;
}
else
{
cur=cur.next;
}
}
return head;
}
(2)加上一个虚节点
第一种写法:添加虚节点(dummy,虚的)只是加了一个虚节点,令pre指向虚节点,cur指向当前节点,后面的操作和第一钟写法是一模一样的。注意返回头节点是return dummy.next;不是return dummy.
ListNode dummy = new ListNode(-1, head);
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy=new ListNode(-1,head);
ListNode pre=dummy;
ListNode cur=head;
while(cur!=null)
{
if(cur.val==val)
{
pre.next=cur.next;
}
else
{
pre=cur;
}
cur=cur.next;
}
return dummy.next;
}
}
第二种写法:添加一个虚节点,但是只用一个指针
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy=new ListNode(-1,head);
ListNode cur=dummy;
while(cur.next!=null)
{
if(cur.next.val==val)
{
cur.next=cur.next.next;
}
else
{
cur=cur.next;
}
}
return dummy.next;
}
}
二, 707.设计链表(虚拟头节点)
//设置虚结点,同时采用头插法加入结点
class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val)
{
this.val=val;
}
}
class MyLinkedList {
int size;
ListNode dummy;
public MyLinkedList() {
size=0;
dummy=new ListNode(0);
}
public int get(int index) {
if(index<0||index>=size)
{return -1;}
ListNode cur=dummy;
for(int i=0;i<=index;i++)
{
cur=cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(size,val);
}
public void addAtIndex(int index, int val) {
if(index>size)
return;
if(index<0)
index=0;
size++;
ListNode pre=dummy;
for(int i=0;i<index;i++)
{
pre=pre.next;
}
ListNode addNode=new ListNode(val);
addNode.next=pre.next;
pre.next=addNode;
}
public void deleteAtIndex(int index) {
if(index>=size||index<0)
return;
size--;
if(index==0)
{
dummy=dummy.next;
return;
}
ListNode pre=dummy;
for(int i=0;i<index;i++)
{
pre=pre.next;
}
pre.next=pre.next.next;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
代码采用了头插法来实现(所以进行插入和删除操作时,要设置一个结点pre指向前一结点),以下是链表的打印函数,可以看到:用头插法建立的单链表的逻辑顺序和输入元素的顺序相反。
public void printList()
{
ListNode cur=dummy.next;
for(int i=0;i<size;i++)
{
System.out.print(cur.val+"-->");
cur=cur.next;
}
System.out.print("null");
}
其中插入操作里面的代码
if(index>size)
return;
if(index<0)
index=0;
可以改写为:
if(index>size||index<0)
return;
同时应该注意return的用法:方法是void,不满足条件直接return;
当索引是0,删除结点,只能是虚结点,删除(dummy=dummy.next;)后,直接return;不然会报错
if(index>=size||index<0)
return;
size--;
if(index==0)
{
dummy=dummy.next;
return;
}
代码也可以改成如下:
if(index>=size||index<0)
return;
注意边界条件:对于size;
public int get(int index) { //如果index非法,返回-1 if (index < 0 || index >= size) { return -1; }
public void addAtIndex(int index, int val) { if(index>size||index<0) return;
public void deleteAtIndex(int index) { if (index < 0 || index >= size) { return; }
三,206.反转链表
1.采用双指针写法:(注意先要保存下一结点再改变指针方向,注意顺序)
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
ListNode temp=null;
while(cur!=null)
{
temp=cur.next;//保存下一结点
cur.next=pre;//改变指针指向
pre=cur;//先移动pre指针
cur=temp;//再移动cur指针
}
return pre;
}
}
2.采用递归写法(在双指针的基础上改变)注意
pre=cur;
cur=temp;
对应到递归代码 是
public ListNode reverse(ListNode pre,ListNode cur)
{
return reverse(cur,temp);// pre=cur;;cur=temp;
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null,head);//给pre和cur赋初始值
}
public ListNode reverse(ListNode pre,ListNode cur)
{
if(cur==null)
return pre;//递归结束条件
ListNode temp=null;
temp=cur.next;//保存下一结点
cur.next=pre;//改变指针指向
return reverse(cur,temp);// pre=cur;//先移动pre指针;cur=temp;//再移动cur指针
}
}
3.上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。 (还没想明白,有时间再看看)
// 从后向前递归
class Solution {
ListNode reverseList(ListNode head) {
// 边缘条件判断
if(head == null) return null;
if (head.next == null) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode last = reverseList(head.next);
// 翻转头节点与第二个节点的指向
head.next.next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head.next = null;
return last;
}
}