203.移除链表元素
题目链接:力扣
思路
一般方法:对于一般删除链表元素的方法而言,我们需要分情况进行处理,分为被删除节点是头节点和被删除节点不是头节点的情况。如果是头节点,就将下一个节点赋值给头节点;如果非头节点,就进行常规删除
虚拟头节点:其实就是给头节点前面放一个虚拟节点,这样如果删除头节点的话就可以使用常规删除方法进行删除了
移除链表元素
一般方法
第一步:判断删除头节点的情况(注意:这里使用的是while关键字,这里是一个连续排查的过程,有前几个节点都是目标值的情况)
第二步:创建指针指向头节点(这里是头节点已经被删除干净的头节点)
第三步:进行判断,被删除的这个节点会被绕过,所以要使用cur.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) {
while ( head!= null && head.val == val) {
head = head.next;
}
//创建一个指针
ListNode cur = head;
while ( cur != null && cur.next != null ) {
if ( cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
虚拟头节点
第一步:创建虚拟节点
第二步:按照上个方法的第二步和第三步进行操作
第三步:注意返回的是虚拟节点的next 才是真正的head
class Solution {
public ListNode removeElements(ListNode head, int val) {
//创建虚拟头节点
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode cur = dummy;
while ( cur != null && cur.next != null ) {
if ( cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummy.next;
}
}
707.设计链表
题目链接:力扣
思路
这个题目包含了对链表元素的增删改查,其中get(int index)是查;addAtHead(int val)、addAtTail(int val)、addAtIndex(int index, int val)是增;deleteAtIndex(int index)是删
设计链表
单向链表实现:
第一步:定义单向链表的节点
第二步:定义链表的属性和链表
第三步:get(int index)
1、对非法下标进行判断
2、创建指针来取值
3、循环下标的次数,将指针指向要取的节点
4、返回获取的值
第四步:addAtIndex(int index, int val) 这个方法实现了,其他两个添加节点的方法也就实现了
1、对非法下标进行判断
2、创建要添加的节点、,创建临时指针
3、循环到指定的下标下,添加节点
4、链表长度增加
第五步:deleteAtIndex(int index)
这里和203 移除链表元素差不多
// 定义单向链表节点
class ListNode {
int val;
ListNode next;
ListNode () {}
ListNode (int val) {
this.val = val;
}
}
class MyLinkedList {
// size存储链表元素的个数
int size;
// 头节点
ListNode head;
// 链表的构造方式
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
// 获取第index个节点的数值,下标从0开始
public int get(int index) {
// index小于0或者大于size-1的情况都是无效的索引
if ( index < 0 || index > size - 1 ) {
return -1;
}
// 创建一个指针来取值
ListNode cur = head;
// 这里cur指向的是虚拟头节点
// 这样当索引为0的时候,下面的循环执行一次,cur刚好指向真正的头节点
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;
}
// 创建要添加的指针
ListNode add = new ListNode(val);
// 对链表元素进行添加
ListNode tmp = head;
// 将tmp指针移动到
for (int i = 0 ; i < index ; i++) {
tmp = tmp.next;
}
add.next = tmp.next;
tmp.next = add;
// 对链表长度进行增加
size++;
}
public void deleteAtIndex(int index) {
// 对非法下标进行判断
if (index < 0 || index > size - 1) {
return;
}
// 如果是头节点,将头节点向前移一位
if (index == 0) {
head = head.next;
return;
}
ListNode cur = head;
for (int i = 0 ; i < index ; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
// 链表长度减一
size--;
}
}
双向链表实现:
class ListNode{
int val;
ListNode next,prev;
ListNode () {}
ListNode (int val) {
this.val = val;
}
}
class MyLinkedList {
// 记录链表中的元素
int size;
// 记录链表的虚拟头节点和尾节点
ListNode head;
ListNode tail;
public MyLinkedList() {
// 初始化操作
this.size = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
head.next = tail;
tail.prev = head;
}
public int get(int index) {
// 判断下标的合法性
if (index < 0 || index >= size) {
return -1;
}
// 创建一个指针
ListNode cur = this.head;
// 判断哪一边的遍历时间更短
if (index >= size / 2) {
// 从tail开始遍历
cur = tail;
for (int i = 0 ; i < size - index ; i++) {
cur = cur.prev;
}
} else {
for (int i = 0 ; i <= index ; i++) {
cur = cur.next;
}
}
return cur.val;
}
public void addAtHead(int val) {
//等价于在第0个元素前添加
addAtIndex(0,val);
}
public void addAtTail(int val) {
//等价于在最后一个元素(null)前添加
addAtIndex(size,val);
}
public void addAtIndex(int index, int val) {
// 判断下标的合法性
if(index>size){
return;
}
//index小于0
if(index<0){
index = 0;
}
ListNode pre = this.head;
for(int i=0; i<index; i++){
pre = pre.next;
}
//新建结点
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next.prev = newNode;
newNode.prev = pre;
pre.next = newNode;
// 长度增加
size++;
}
public void deleteAtIndex(int index) {
// 判断下标的合法性
if (index < 0 || index >= size) {
return;
}
// 删除操作
ListNode pre = this.head;
for (int i = 0 ; i < index ; i++) {
pre = pre.next;
}
pre.next.next.prev = pre;
pre.next = pre.next.next;
// 长度减少
size--;
}
}
206.反转链表
题目链接:力扣
思路
双指针:其实可以称为三指针了,cur负责指向pre,tmp负责临时保存
递归法:是双指针的优化
反转链表
双指针法
第一步:定义双指针
cur指向头节点,pre指向null(反转时后节点指向前节点的方向)
第二步:在cur指向null的时候循环结束
要创建一个临时节点保存cur反转后后面的节点,防止丢失
注意:在反转成功后,在移动指针的时候,要先移动pre,再移动cur
/**
* 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;
while (cur != null) {
// 利用临时指针保存cur后的下一个节点,防止找不到
ListNode tmp = cur.next;
cur.next = pre;
// 注意这里先移动pre 再移动cur
pre = cur;
cur = tmp;
}
return pre;
}
}
递归法
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null , head);
}
private ListNode reverse(ListNode prev , ListNode cur) {
if (cur == null) {
return prev;
}
ListNode tmp = null;
tmp = cur.next; // 保存下一个节点
cur.next = prev; // 反转指针
return reverse(cur,tmp);
}
}