庖丁解牛:设计链表-单链表

庖丁解牛:设计链表 - 单链表

一、题目分析

01 题目描述

这是LeetCode中的一道题目,题目描述如下:

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,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 的节点。

本次我们选择单链表的实现,所以把题目描述拆分出来:

设计并实现自己的单链表。

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,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;
    }
}

你可能感兴趣的:(庖丁解牛-代码解构,链表,算法,数据结构)