代码随想录算法训练营day03 | 203.移除链表元素,707.设计链表,206.反转链表
链表基础
定义单向链表:
class ListNode{
// 下一节点和当前节点值
ListNode next;
int value;
// 三种构造函数
public ListNode(){}
public ListNode(int val){
this.value = val;
}
public ListNode(int val,ListNode next){
this.value = val;
this.next = next;
}
}
203.移除链表元素
教程视频:https://www.bilibili.com/video/BV18B4y1s7R9/?vd_source=ddffd51aa532d23e6feac69924e20891
解法一:直接删除(区分头节点和中间节点)
链表中删除头节点操作和中间节点的操作不同。
因此本题要先确定新的头节点,之后再遍历删除中间节点。
头节点删除是将head后移,中间节点是将 next 指向 next.next 。
注意: 单向链表不能找前一节点,因此循环判定条件是下一节点不为空。
public ListNode removeElements(ListNode head, int val) {
//确定新的头节点
while(head!=null && head.val==val){
head=head.next;
}
// 设置虚拟指针 currentNode
ListNode currentNode = head;
while(currentNode!=null && currentNode.next!=null){
if(currentNode.next.val==val){
//删除currentNode.next
currentNode.next=currentNode.next.next;
}else{
currentNode=currentNode.next;
}
}
return head;
}
解法二:虚拟头节点
加入虚拟头节点,能够统一对单向链表头节点和中间节点的增删操作,使代码更简洁。
public ListNode removeElements(ListNode head, int val) {
// 创建虚拟头节点,让其位于head前
ListNode dummyhead = new ListNode();
dummyhead.next = head;
//指针
ListNode curr = dummyhead;
//遍历删除无用节点
while(curr.next!=null){
if(curr.next.val==val){
curr.next = curr.next.next;
}else{
curr = curr.next;
}
}
return dummyhead.next;
}
707.设计链表
教程视频:https://www.bilibili.com/video/BV1FU4y1X7WD/?vd_source=ddffd51aa532d23e6feac69924e20891
解法一:不使用虚拟头节点(输出有误,还没改)
class MyLinkedList {
int val;
MyLinkedList next;
public MyLinkedList() {
}
public int get(int index) {
int num = 0;
while(this!=null){
if(num==index){
return this.val;
}else if(this.next!=null){
this = this.next;
num++;
}
}
return -1;
}
public void addAtHead(int val) {
MyLinkedList head = new MyLinkedList();
head.val = val;
head.next = this;
}
public void addAtTail(int val) {
MyLinkedList newNode = new MyLinkedList();
head.val = val;
if(this==null){
this = newNode;
return;
}
while(this.next!=null){
this = this.next;
}
this.next = newNode;
}
public void addAtIndex(int index, int val) {
MyLinkedList node = new MyLinkedList();
node.val = val;
int num = 0;
while(this!=null){
if(num==index){
node.next = this.next;
this.next = node;
}else if(this.next!=null){
this = this.next;
num++;
}
}
}
public void deleteAtIndex(int index) {
// 删除头节点
if(index==0){
this = this.next;
}
// 删除中间节点
int num = 1;
while(this.next!=null){
if(num==index){
this.next = this.next.next;
}
}
}
}
解法二:虚拟头节点
class ListNode {
int val;
ListNode next;
public ListNode (){}
public ListNode (int value){
this.val = value;
}
}
class MyLinkedList {
//size存储链表元素的个数
int size;
//虚拟头结点
ListNode head;
public MyLinkedList() {
size = 0;
head = new ListNode ();
}
//获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
public int get(int index) {
//判断index有效
if(index<0 || index>size-1){
return -1;
}
//遍历链表
ListNode currentNode = head.next;//指针
while(index>0){ //带入index=0 来确定判断条件
currentNode = currentNode.next;
index--;
}
return currentNode.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
// ListNode newHead = new ListNode (val);
// newHead.next = head.next;
// head.next = newHead;
// size+=1;
}
public void addAtTail(int val) {
addAtIndex(size,val);
// ListNode newTail = new ListNode (val);
// ListNode currentNode = head;
// while(currentNode.next!=null){
// currentNode = currentNode.next;
// }
// currentNode.next = newTail;
// size+=1;
}
public void addAtIndex(int index, int val) {
//判断index有效
if(index<0 || index>size){ //这里index等于size的情况也可插入节点
return;
}
//创建要插入的结点
ListNode newNode = new ListNode (val);
//遍历指针
ListNode currentNode = head;
while(index>0){
currentNode = currentNode.next;
index--;
}
newNode.next = currentNode.next;
currentNode.next = newNode;
size+=1;
}
public void deleteAtIndex(int index) {
//判断index有效
if(index<0 || index>size-1){
return;
}
//指针
ListNode currentNode = head;
while(index>0){
currentNode = currentNode.next;
index--;
}
currentNode.next = currentNode.next.next;
size-=1;
}
}
206.反转链表
教程视频:https://www.bilibili.com/video/BV1nB4y1i7eL/?spm_id_from=333.788&vd_source=ddffd51aa532d23e6feac69924e20891
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
解法一:双指针
public ListNode reverseList(ListNode head) {
//快慢指针
ListNode currentNode = new ListNode();
currentNode = head;
ListNode preNode = null;
while(currentNode!=null){
ListNode TempNode = new ListNode();
TempNode = currentNode.next;
currentNode.next = preNode;
preNode = currentNode;
currentNode = TempNode;
}
return preNode;
}
解法二:递归
public ListNode reverse(ListNode cur, ListNode pre){
if(cur==null){return pre;}
ListNode temp = new ListNode();
temp = cur.next;
cur.next = pre;
// pre = cur;
// cur = temp;
return reverse(temp, cur);//调用递归完成上两行的交换
}
public ListNode reverseList(ListNode head) {
return reverse(head, null);
}
总结
1、虚拟头节点可以将单向链表的操作统一起来,减少由于头节点操作不一致导致的分类讨论;
2、单向链表操作时一定要考虑清楚,当前指针需要指向哪个节点;
3、反转链表直接将链表next指向修改即可,不需要多次遍历链表。