今日任务:
- 链表理论基础
- 203.移除链表元素
- 707.设计链表
- 206.反转链表
链表理论基础
链接:链表理论基础
思路:通常包含①节点存储的元素val②指向下一个节点的指针next③节点的构造函数
public class ListNode {
// 结点的值
int val;
// 下一个结点
ListNode next;
// 节点的构造函数(无参)
public ListNode() {
}
// 节点的构造函数(有一个参数)
public ListNode(int val) {
this.val = val;
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
LeetCode203 移除链表元素
题目链接:203.移除链表元素
思路:
(1)有虚拟头结点,有pre和cur指针
● 需要注意的点:①空链表的特殊情况②及时更新指针pre和cur③存在虚拟头结点情况下,dummy.next才是新的头结点
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head == null){
return head;
}
//有dummy,有pre版(有指针cur,即current缩写)
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;
}
}
(2)无虚拟头结点,有pre和cur指针
● 需要注意的点:①先寻找到不是val的新头结点,再判断新的头结点是不是空
②不存在虚拟头结点情况下,直接返回head
class Solution {
public ListNode removeElements(ListNode head, int val) {
//无dummy,有pre和cur
//需要单独考虑头结点的情况,头结点后移
if(head == null){
return head;
}
while(head != null && head.val ==val){
head = head.next;
}
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;
}
}
(3)无虚拟头结点,无pre只剩cur
● xx.val、xx.next一定要有xx不为空的判断条件
class Solution{
public ListNode removeElements(ListNode head, int val) {
if(head == null){
return head;
}
while(head!=null && head.val==val){
head = head.next;
}
ListNode curr = head;
while(curr!=null){
if(curr.next!=null && curr.next.val == val){
curr.next = curr.next.next; //需要注意的就是xx.next,xx绝对不能为空
}
curr = curr.next;
}
return head;
}
}
LeetCode707 设计链表
题目链接:707.设计链表
● 初始化的节点即虚拟节点,设置pre和cur更加方便;
● 记得加节点的时候size++;
// 首先"class"声明链表节点
class ListNode {
int val;
ListNode next;
ListNode() {
};
public ListNode(int val) {
this.val = val;
}
}
//链表包括节点头部head和节点个数size
class MyLinkedList {
int size;
ListNode head; //虚拟头结点
//初始化head和size,这里的head是虚拟头结点所以val=0
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if(index<0 || index>size){
return -1;
}
ListNode cur = head;
//i=0时,cur指向真正的第一个节点,即index=0的结点
for(int i=0;i<=index;i++){
cur = cur.next; //因此i=index即指向序号为index的结点
}
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) {
// ListNode ToAdd = new ListNode(val);
// if(index=0){
// ToAdd.next = head.next;
// }
// if(index=size){
// cur = head;
// for(i=0;i<=size;i++){
// cur = cur.next;
// }
// cur.next =ToAdd;
// } 用了虚拟头结点插入结点不需要分情况
if (index < 0){
index = 0;
}
if(index>size){
return;
}
ListNode pre = head;
for(int i=0;i<index;i++){
pre =pre.next;
}
ListNode ToAdd = new ListNode(val);
ToAdd.next = pre.next;
pre.next = ToAdd;
size++;
}
public void deleteAtIndex(int index) {
if(index<0 || index>size){
return ; //直接结束
}
ListNode pre = head;
for(int i=0;i<index;i++){
pre =pre.next;
}
ListNode cur = pre.next;
pre.next = cur.next;
size--;
}
}
LeetCode206 反转链表
题目链接:206. 反转链表
(1)双指针法(pre、cur)
● 设pre为null,再翻转
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode tmp;
while(cur!=null){
tmp = cur.next; //用tmp保存下一个节点
cur.next = pre; //反转链表的操作
//移动pre和cur,准备进行下一轮操作
pre = cur;
cur = tmp;
}
return pre;
}
}
(2)递归法
● 实际上就是双指针的基础上层层递归,可以从注释里看出两种方法的对比
class Solution {
public ListNode reverseList(ListNode head) {
// ListNode pre = null;
// ListNode cur = head;
// ListNode tmp;
return Reverse(head, null);
}
ListNode Reverse(ListNode cur, ListNode pre) {
// while(cur!=null){
// tmp = cur.next;
// cur.next = pre;
// pre = cur;
// cur = tmp;
// }
// return pre;
if (cur == null) {
return pre;
}
ListNode tmp = cur.next;
cur.next = pre;
return Reverse(tmp, cur);
}
}