203.移除链表元素
- 题目链接:203.移除链表元素
- 解题思路:从头到尾遍历链表,遇到需要删除的元素时将其删除即可
- 主要难点:
- 设置一个虚拟头节点:使得整个链表中的每一个节点都可以用统一的方式进行遍历和删除,说白了就是给原来的头节点也设置一个前驱,这样每个节点就都有前驱了,头节点不用单独拿出来作为一种特殊情况
- 最后返回值的设置: 题目要求最后返回新的链表的头节点,在最后返回的时候并不是直接返回cur,而是返回的虚拟头节点的后继节点fake.next。
可能是因为cur有可能为null(?)
- 解题步骤:
- 定义虚拟头节点fake,值设为-1,next节点设为原链表的头节点
- 定义pre和cur两个用来指示当前位置状态的节点,最开始的时候cur从原链表头节点head开始,则pre从定义的虚拟头节点fake开始
- 开始循环遍历,终止条件为当前节点cur为null,在遍历过程中若发现了当前节点对应值与要删除的目标值相同,则执行链表删除操作pre.next = cur.next,否则令pre=cur,并将cur向后移一位,直到循环结束
- 最终返回虚拟头节点的后继节点
- 解题时间:10min+12min(博客)
- 代码:
/**
* 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) {
if(head == null) {
return head;//链表本身为空
}
ListNode fake = new ListNode(-1,head);//统一设置一个虚拟头节点,值为-1,后继节点原来的头节点
ListNode pre = fake;
ListNode cur = head;
while(cur != null) {
if(cur.val == val){
//需要删除当前节点
pre.next = cur.next;
}else{
pre = cur;
}
cur = cur.next;
}
return fake.next;
}
}
707.设计链表
- 题目链接:707.设计链表
- 解题思路:不涉及太多算法,就是自己实现一个链表的增删获取操作,需要自己定义链表结构
- 主要难点:也设置一个虚拟头节点,理由同上,用起来方便
- 解题步骤:
- 获取:判断合法性后,从虚拟头节点开始后移index位,取得当前对应值返回
- 任意位置插入:几种情况要都考虑到,大于index不插直接return,小于index插头部,等价于index=0,其他情况正常插,从虚拟头节点移动到要插入位置的前一个位置上,新建节点进行插入,记得size++
- 头插入、尾插入:是任意位置插入的特殊情况,直接调用
- 删除:判断合法性后,从虚拟头节点开始后移index位,删除节点
- 解题时间:35min+6min(博客)
- 代码:
class MyLinkedList {
int size;
ListNode head;//设置虚拟头节点
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
//获取第index个值
//首先判断是否合法
if(index<0||index>=size){
return -1;
}
ListNode cur = head;//从虚拟节点开始,向后移动
//因为设置了虚拟头节点,所以实际上找的应该是index+1
for(int i = 0;i < index+1;i++){
cur=cur.next;//后移
}
return cur.val;//返回当前节点对应的值
}
public void addAtHead(int val) {
//可以复用addAtIndex方法实现
addAtIndex(0,val);
}
public void addAtTail(int val) {
//可以复用addAtIndex方法实现
addAtIndex(size,val);
}
public void addAtIndex(int index, int val) {
//index大于链表长度时不插入
if(index>size){
return;//链表
}
//index小于0在头部插
if(index<0){
index=0;
}
//index等于链表长度时在末尾加,这部分不用写代码了
size++;//插完长度加1,先更新长度
//先移动到index插入位置去
ListNode pre = head;//从虚拟头节点开始
for(int i=0;i<index;i++){
pre = pre.next;//后移,最后移动到要插入的前一个位置上,即在pre的后面插add
}
ListNode add = new ListNode(val);//定义新插入的节点
//把节点放在pre后面
add.next=pre.next;
pre.next = add;
}
public void deleteAtIndex(int index) {
//先判断索引是否有效
if(index<0||index>=size){
return;//啥也不干
}else{
//删除节点
size--;//删完长度减1,先减
ListNode pre = head;//从虚拟头节点开始
for(int i=0;i<index;i++){
pre = pre.next;//后移,删除pre后面的元素
}
pre.next=pre.next.next;//删除pre后面的元素
}
}
}
class ListNode {
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}
/**
* 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);
*/
206.反转链表
- 题目链接:206.反转链表
- 解题思路:
- 方法1:双指针法,逆置每一个指向next的方向,这种方法理解起来比较简单,需要用到两个指针和一个用来保存后项的临时变量,两两一对逆置即可
- 方法2:递归,
- 主要难点:双指针,递归
- 解题步骤:(双指针为例)
- 新建两个指针,pre指针指向头部前一个null,cur指针指向当前头部head,再新建一个temp用来存放后项
- 循环执行:先保存cur的下一位到temp,然后将cur的后项逆置转为前项pre,接着cur和pre向右移动,重复上述过程
- 当cur为空时,即走完整个链表停止
- 解题时间:10min(双指针)+10min(递归)+5(博客)
- 代码:
双指针:
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;
}
}
递归:
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
private ListNode reverse(ListNode prev, ListNode cur) {
if (cur == null) {
return prev;
}
ListNode temp = null;
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
}
总结
今天三道题都是链表题,主要复习链表的定义,一些增删获取基础操作的手动实现,还有就是链表元素的操作。用到比较多的方法是虚拟头节点和双指针法。其中虚拟头节点是链表类题常用的技巧,自己定义一个虚拟的头节点就可以使得整个链表的操作一致,避免出现特殊情况。