链表理论基础
题目建议: 了解一下链接基础,以及链表和数组的区别
文章链接
代码随想录知识点:
- 链接的入口节点称为链表的头结点也就是head。单链表如图所示:
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;
}
}
203.移除链表元素
题目建议:本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接
分析链接
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
思路:因为单链表的特殊性,只能指向下一个节点,那么如果删除的是头结点又该怎么办呢?有两种方式:
- 方法一:直接使用原来的链表来进行删除操作。
- 方法二:设置一个虚拟头结点在进行删除操作(建议使用)。
方法一:写代码的时候单独写一段逻辑来处理移除头结点的情况
class Solution {
public ListNode removeElements(ListNode head, int val) {
while (head != null && head.val == val) {
head = head.next;
}
// 已经为null,提前退出
if (head == null) {
return head;
}
// 已确定当前head.val != val
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;
}
}
重点:重点在这一块儿,细细品吧,自己写各种逻辑上的报错。
while (head != null && head.val == val) {
head = head.next;
}
// 已经为null,提前退出
if (head == null) {
return head;
}
// 已确定当前head.val != val
方法二:可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了
/**
* 添加虚节点方式
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(-1,head);
ListNode pre = dummy;
ListNode cur = head;
if(head == null){
return head;
}
while(cur!=null){
if(cur.val == val){
pre.next = cur.next;
}else{
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
}
重点:创建虚拟节点的语句
ListNode dummy = new ListNode(-1, head);
707.设计链表
题目建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接
思路链接
设计链表的一系列增删改查的函数
单链表:
?class ListNode{
int val;
ListNode next;
public ListNode(){}
public ListNode(int val){
this.val=val;
}
}
class MyLinkedList {
int size;
ListNode dummy;
public MyLinkedList() {
size=0;
dummy=new ListNode(0);
}
//get add delete方法,就是记住,最先判断两个边界条件
//if index<0和index>=size的情况
public int get(int index) {
//index相当于数组下标,size相当于nums.length,index从0开始
if(index<0 || index>size-1){
return -1;
}
ListNode cur=dummy;
for(int i=0;i<=index;i++){
cur=cur.next;
}
return cur.val;
}
public void addAtIndex(int index, int val) {
if(index>size){
//这里只能return空,不能return -1;因为是public void
return;
}
if(index<0){
index = 0;
}
//不要忘记size!!!
size++;
ListNode cur=dummy;
ListNode add = new ListNode(val);
for(int i=0;i<index;i++){
cur = cur.next;
}
add.next = cur.next;
cur.next = add;
}
public void deleteAtIndex(int index) {
if(index<0 || index>size-1){
return;
}
//不要忘记size!!!
size--;
ListNode cur=dummy;
for(int i=0;i<index;i++){
cur = cur.next;
}
cur.next=cur.next.next;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
}
-
get?add?delete方法,就是记住,最先判断两个边界条件? if?index<0和index>=size的情况 -
不要忘记处理size!!! -
index相当于数组下标,size相当于nums.length,index从0开始 -
有些越界的地方return空,有些return?-1是因为返回值不同,public?void和public int的区别
双链表:
class ListNode{
int val;
ListNode next;
ListNode prev;
public ListNode(){}
public ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
int size;
ListNode dumhead,dumtail;
public MyLinkedList() {
size = 0;
dumhead = new ListNode(0);
dumtail = new ListNode(0);
//这一步非常关键,否则在加入头结点的操作中会出现null.next的错误!!!
dumhead.next = dumtail;
dumtail.prev = dumhead;
}
public int get(int index) {
if(index<0 || index>=size){
return -1;
}
ListNode cur = dumhead;
for(int i=0;i<=index;i++){
cur = cur.next;
}
return cur.val;
}
public void addAtIndex(int index, int val) {
if(index<0){
index=0;
}
if(index>size){
return;
}
//不要忘记处理size
size++;
ListNode cur = dumhead;
ListNode add = new ListNode(val);
for(int i =0;i<index;i++){
cur = cur.next;
}
add.next = cur.next;
cur.next.prev = add;
cur.next = add;
add.prev = cur;
}
public void deleteAtIndex(int index) {
if(index<0 || index>=size){
return;
}
size--;
ListNode cur = dumhead;
for(int i=0;i<index;i++){
cur = cur.next;
}
cur.next.next.prev = cur;
cur.next = cur.next.next;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
}
- 记得处理size!!!
- 双链表的情况,记得除了定义虚拟的初始头结点dumhead,还有虚拟尾节点dumtail也要定义
- 双链表情况,初始化的时候,记得多出来了一步,dumhead和dumtail的互指!否则头结点会出现null.next的错误!!!
206.反转链表
题目建议: 建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接
思路链接
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
方法一:(双指针+temp)
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
return null;
}
ListNode cur = head;
ListNode pre =null;
ListNode temp=null;
while(cur!=null){
temp =cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
方法二:递归法
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null,head);
}
public ListNode reverse(ListNode pre,ListNode cur){
if(cur == null){
//这里自己一开始写的是return null;是错的
return pre;
}
ListNode temp =null;
temp = cur.next;
cur.next = pre;
//这里原来写的是 reverse(cur,temp);return pre;是不行的
return reverse(cur,temp);
}
}
知识点总结
1. 链表很陌生,每道题都需要再练
|