一个人的朝圣 — LeetCode打卡第3天
知识总结
今天是链表专题, 链表也是一种非常重要的数据结构, 相关的基础知识先复习一下.
数组的内存空间是连续的, 而链表的内存空间不连续.
一道小小的Java面试题, ArrayList 和 LinkedList 有什么区别:
Leetcode 203. 移除链表元素
题目说明
移除链表中所有数值等于val的元素. 这个题目也有很多的解法, 我这里采用了两种, 方法都大同小异. 主要是遍历一下链表, 然后把符合条件的ListNode 删除
代码说明
方法一, 使用dummy节点, 高级一点叫做虚拟头结点, 这样对应后面删除操作能够达到统一. 然后不使用pre, cur双指针, 用cur.next = cur.next.next 将下一个满足条件的节点删去.
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return dummy.next;
}
}
方法二, 不使用dummy节点, 对于头结点和非头结点分开处理.
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 删除满足条件的头结点
while(head!= null && head.val == val){
head = head.next;
}
if (head == null){
return head;
}
ListNode cur = head.next;
ListNode pre = head;
while(cur != null){
if (cur.val == val){
pre.next = cur.next;
}else{
pre = cur;
}
cur = cur.next;
}
return head;
}
}
Leetcode 707. 设计链表
题目说明
这题非常的灵活, 要求自己设计一个链表并且实现相关的操作. 设计一个LinkedList 首先要自己定义一个ListNode的类, 然后写出两个构造方法.
在实现LinkedList的时候,我们使用了单链表的设计思路, LinkedList 维持了一个虚拟头结点和长度两个成员变量. 要注意在构造函数时将其初始化.
在插入和删除的时候, index的可取值范围不一样. 插入index 可以等于len. 而删除不行. 这点也很重要
代码说明
class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
int len;
ListNode head;
public MyLinkedList() {
// 初始化变量, 记得使用this
this.len = 0;
this.head = new ListNode(0);
}
public int get(int index) {
if(index >= len || index < 0){
return -1;
}
ListNode cur = head;
// 因为有虚拟头结点, 所有是<= index
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(len, val);
}
public void addAtIndex(int index, int val) {
// 注意这里index = len 是可以的, 相当于插到末尾
if(index < 0 || index > len){
return;
}
len++;
ListNode cur = head;
for(int i =0; i<index; i++){
cur = cur.next;
}
ListNode newNode = new ListNode(val);
ListNode temp = cur.next;
cur.next = newNode;
newNode.next = temp;
}
public void deleteAtIndex(int index) {
// index = len 不行
if(index < 0 || index >= len){
return;
}
len--;
ListNode cur = head;
for(int i =0; i<index; i++){
cur = cur.next;
}
cur.next = cur.next.next;
}
}
Leetcode 206. 反转链表
题目说明
经典题目, 翻转一个链表. 这里提供迭代和递归的方法. 递归的方法会比较难理解一点. 注意这里将pre的初始值定义成null就行, 不要用到dummyNode.
代码说明
迭代法: 需要一个临时变量来记录下个节点
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
ListNode temp;
while(cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
递归法: 确定好每次的head和p 值是什么. p 每次都会指向最后一个节点(即翻转之后的头结点). 而head在弹栈的时候会从后一直往前直到最前的头结点;
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
}