庖丁解牛:设计链表 - 单链表
一、题目分析
01 题目描述
这是LeetCode中的一道题目,题目描述如下:
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
本次我们选择单链表的实现,所以把题目描述拆分出来:
设计并实现自己的单链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
02 任务分解
LeetCode提供了函数签名,我们把它写在下面:
class MyLinkedList {
public MyLinkedList() {}
public int get(int index) {}
public void addAtHead(int val) {}
public void addAtTail(int val) {}
public void addAtIndex(int index, int val) {}
public void deleteAtIndex(int index) {}
}
我们的任务:
- 初始化函数:
MyLinkedList()
- 取值函数:
get()
- 插入函数:
addAtHead
… - 删除函数:delete
03 正式代码前
到这里准备工作完成了,但是可以想象,这段代码写出来是很难调试的,于是我们自己建立一个java
工程进行调试
大概分为三个class,链表主类、节点类、测试类
节点类是最简单的:
public class ListNode {
int val;
ListNode next;
public ListNode(){
this.val = 0;
}
public ListNode(int val){
this.val = val;
}
public ListNode(int val,ListNode next){
this.val = val;
this.next = next;
}
}
然后是链表主类,先编写基本的初始化函数和一些需要的属性:
public class MySingleLinkedList {
ListNode head;
int length;
public MySingleLinkedList() {
head = new ListNode();
// 笔者编写时犯了一个错误:
// ListNode head = new ListNode();
// 这里的错误!!!
length = 1;
}
}
04 额外的测试函数
除了力扣提供的函数,为了方便直观地调试,我们额外编写两个函数:print
函数打印链表、manual
手动建表
(1) 打印函数
public void print(){
ListNode cur = head;
while(cur != null){
if(cur != head){
System.out.print("->");
}
System.out.print("【"+ cur.val +"】");
cur = cur.next;
}
System.out.println();
}
(2) 手动建表
public void manualCreatList(){
// 手动建表
ListNode node1 = new ListNode(2);
ListNode node2 = new ListNode(4);
ListNode node3 = new ListNode(6);
head.val = 0;
head.next = node1;
node1.next = node2;
node2.next = node3;
length = 4;
}
(3) 测试函数
然后我们可以开始写第一个要求的函数:取值函数。但是取值函数测试是否正确?我们还要写个测试函数:
public class TestMain {
public static void main(String[] args) {
MySingleLinkedList list = new MySingleLinkedList();
list.manualCreatList();
list.print();
int num = list.get(2);
System.out.println(num);
}
}
二、题解代码
01 取值函数
public int get(int index) {
if(index > length-1 || index < 0){
//索引有效性检查
return -1;
}else{
ListNode cur = head;
// ListNode cur = new ListNode();
// 需要对cur节点进行初始化
for(int i=0 ; i<index ; i++){
cur = cur.next;
}
return cur.val;
}
}
02 添加函数
(1) 头插法
public void addAtHead(int val) {
if(length == 0){
head = new ListNode(val);
}else{
int hVal = head.val;
ListNode node = new ListNode(hVal);
head.val = val;
node.next = head.next;
head.next = node;
}
length++;
}
(2) 尾插法
public void addAtTail(int val) {
if(length == 0){
head = new ListNode(val);
}else{
ListNode node = new ListNode(val);
ListNode cur = head;
while(cur.next != null){
cur = cur.next;
}
cur.next = node;
}
length++;
}
(3) 按索引插入
public void addAtIndex(int index, int val) {
boolean lenValid = length >= 0;
boolean idxValid = index <= length && index >= 0;
if(lenValid && idxValid){
if(length == 0){
head = new ListNode(val);
}else{
if(index == 0){
addAtHead(val);
return;
}else if(index == length){
addAtTail(val);
return;
}else if(index < length){
ListNode node = new ListNode(val);
ListNode cur = head;
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
node.next = cur.next;
cur.next = node;
}
}
length++;
}
}
03 删除函数
public void deleteAtIndex(int index) {
boolean lenValid = length > 0;
boolean idxValid = index < length && index >= 0;
ListNode cur = head;
if(lenValid && idxValid){
if(index == 0 || index == length-1){
// 删除首尾节点
if(index == 0) head = head.next;
if(index == length-1){
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
cur.next = null;
}
}else{
// 删除中间节点
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
cur.next = cur.next.next;
}
length--;
}
}
三、完整源代码
MySingleLinkedList.java
public class MyLinkedList {
ListNode head;
int length;
public MyLinkedList () {
head = null;
length = 0;
}
public int get(int index) {
boolean lenValid = length > 0;
boolean idxValid = index < length && index >= 0;
int result;
if(head == null) return -1;
if(!lenValid || !idxValid){
result = -1;
}else{
ListNode cur = head;
for(int i=0 ; i<index ; i++){
cur = cur.next;
}
result = cur.val;
}
return result;
}
public void addAtHead(int val) {
if(length == 0){
head = new ListNode(val);
}else{
int hVal = head.val;
ListNode node = new ListNode(hVal);
head.val = val;
node.next = head.next;
head.next = node;
}
length++;
}
public void addAtTail(int val) {
if(length == 0){
head = new ListNode(val);
}else{
ListNode node = new ListNode(val);
ListNode cur = head;
while(cur.next != null){
cur = cur.next;
}
cur.next = node;
}
length++;
}
public void addAtIndex(int index, int val) {
boolean lenValid = length >= 0;
boolean idxValid = index <= length && index >= 0;
if(lenValid && idxValid){
if(length == 0){
head = new ListNode(val);
}else{
if(index == 0){
addAtHead(val);
return;
}else if(index == length){
addAtTail(val);
return;
}else if(index < length){
ListNode node = new ListNode(val);
ListNode cur = head;
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
node.next = cur.next;
cur.next = node;
}
}
length++;
}
}
public void deleteAtIndex(int index) {
boolean lenValid = length > 0;
boolean idxValid = index < length && index >= 0;
ListNode cur = head;
if(lenValid && idxValid){
if(index == 0 || index == length-1){
// 删除首尾节点
if(index == 0) head = head.next;
if(index == length-1){
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
cur.next = null;
}
}else{
// 删除中间节点
for(int i=0 ; i<index-1 ; i++){
cur = cur.next;
}
cur.next = cur.next.next;
}
length--;
}
}
public void print(){
ListNode cur = head;
while(cur != null){
if(cur != head){
System.out.print("->");
}
System.out.print("【"+ cur.val +"】");
cur = cur.next;
}
System.out.println();
}
public void manualCreatList(){
// 手动建表
ListNode node1 = new ListNode(2);
ListNode node2 = new ListNode(4);
ListNode node3 = new ListNode(6);
head.val = 0;
head.next = node1;
node1.next = node2;
node2.next = node3;
length = 4;
}
}