编程导航算法通关村第一关 | 青铜:链表的增删改查

目录

单链表

变量传递

链表插入

链表删除

双向链表


目录

变量传递

链表插入

链表删除

双向链表


使用java操作链表,节点的定义是一个Node类,类的属性有val (存储数据) 和next (Node类,用于指向节点 )。链表结构和增删操作如下:

package com.yugutou.charpter1_linklist.level1;

/**
 * 构造链表,使用静态内部类定表示结点,实现增加和删除元素的功能
 *
 */
public class BasicLinkList {
    static class Node {
        final int data;
        Node next;
        public Node(int data) {
            this.data = data;
        }
    }

    public static void main(String[] args) {
        // 头部添加节点1
        Node head = new Node(1);
        System.out.println("头部添加节点1:" + BasicLinkList.toString(head));

        System.out.println("链表长度为:" + getLength(head));
        // 尾部添加节点2
        Node node = new Node(2);
        head = BasicLinkList.insertNode(head, node, 2);
        System.out.println("尾部添加节点2:" + BasicLinkList.toString(head));
        System.out.println("链表长度为:" + getLength(head));
        // 中间添加节点3
        node = new Node(3);
        head = BasicLinkList.insertNode(head, node, 2);
        System.out.println("中间添加节点3:" + BasicLinkList.toString(head));

        // 删除中间节点2
        head = BasicLinkList.deleteNode(head, 2);
        System.out.println("删除中间节点3:" + BasicLinkList.toString(head));

        // 删除头部节点1
        head = BasicLinkList.deleteNode(head, 1);
        System.out.println("删除头部节点1:" + BasicLinkList.toString(head));
    }

    /**
     * 获取链表长度
     *
     * @param head 链表头节点
     * @return 链表长度
     */
    public static int getLength(Node head) {
        int length = 0;
        Node node = head;
        while (node != null) {
            length++;
            node = node.next;
        }
        return length;
    }

    /**
     * 链表插入
     *
     * @param head       链表头节点
     * @param nodeInsert 待插入节点
     * @param position   待插入位置,取值从2开始
     * @return 插入后得到的链表头节点
     */
    public static Node insertNode(Node head, Node nodeInsert, int position) {
        // 需要判空,否则后面可能会有空指针异常
        if (head == null) {
            return nodeInsert;
        }
        //越界判断
        int size = getLength(head);
        if (position > size + 1 || position < 1) {
            System.out.println("位置参数越界");
            return head;
        }

        //在链表开头插入
        if (position == 1) {
            nodeInsert.next = head;
//            return nodeInsert;
            //上面return还可以这么写:
            head = nodeInsert;
            return head;
        }

        Node pNode = head;
        int count = 1;
        while (count < position - 1) {
            pNode = pNode.next;
            count++;
        }
        nodeInsert.next = pNode.next;
        pNode.next = nodeInsert;

        return head;
    }

    /**
     * 删除节点
     *
     * @param head     链表头节点
     * @param position 删除节点位置,取值从1开始
     * @return 删除后的链表头节点
     */
    public static Node deleteNode(Node head, int position) {
        if (head == null) {
            return null;
        }
        int size = getLength(head);
        //思考一下,这里为什么是size,而不是size+1
        if (position > size || position <1) {
            System.out.println("输入的参数有误");
            return head;
        }
        if (position == 1) {
            //curNode就是链表的新head
            return head.next;
        } else {
            Node cur = head;
            int count = 1;
            while (count < position - 1) {
                cur = cur.next;
                count++;
            }
            Node curNode = cur.next;
            cur.next = curNode.next;
        }
        return head;
    }

    /**
     * 输出链表
     *
     * @param head 头节点
     */
    public static String toString(Node head) {
        Node current = head;
        StringBuilder sb = new StringBuilder();
        while (current != null) {
            sb.append(current.data).append("\t");
            current = current.next;
        }
        return sb.toString();
    }

}

单链表

目录

单链表

变量传递

链表插入

链表删除

双向链表


变量传递

java中的方法变量都是存在栈中的,而node节点实例是存储在堆中,每调用一个方法就会开一个新的栈空间,方法之间的相互调用参数传递都是形参指向,互不干扰。

如main方法里调用 insertNode(head, node, 2) 方法,在 insertNode 方法里的head变量无论怎么改变,都不会影响main方法里的head变量。所以在设计上,对链表插入或删除操作(考虑到表头改变了的话),都会返回一个新的表头给main方法里的head变量。

链表插入

表头插入的话,插入的位置是1:直接将InsertNode的next指向原来的head节点,返回InsertNode节点

 插入的位置是中间或表尾:逻辑都是一样的:拿到插入位置的前一个节点(preNode),将要插入的节点(InsertNode)指向preNode的next节点,然后将preNode的next指向InsertNode就好了。

链表删除

删除表头,删除的位置是1:直接将head指向head的next,返回head节点。原来的head节点不可到达,会被JVM自动回收

删除中间元素和表尾元素:找到删除位置的前一个节点(preNode),然后将preNode的next指向preNode的next的next,中间的节点无法到达,会被JVM回收

双向链表

在节点类Node里多了一个last (Node) 变量,指向前一个节点。单向链表只能从前往后遍历,而双向链表能双向遍历,从表上任意一个节点开始找,能找到任意一个节点。插入和删除基本逻辑和单链表差不多,但要同时改变节点 next 和 last 的指向。

package com.yugutou.charpter1_linklist.level1;

/**
 * 构造链表,使用静态内部类定表示结点,实现增加和删除元素的功能
 *
 */
public class BasicLinkList {
    static class Node {
        final int data;
        Node next;
        Node last;
        public Node(int data) {
            this.data = data;
        }
    }

    public static void main(String[] args) {
        // 头部添加节点1
        Node head = new Node(1);
        System.out.println("头部添加节点1:" + BasicLinkList.toString(head));

        System.out.println("链表长度为:" + getLength(head));
        // 尾部添加节点2
        Node node = new Node(2);
        head = BasicLinkList.insertNode(head, node, 2);
        System.out.println("尾部添加节点2:" + BasicLinkList.toString(head));
        System.out.println("链表长度为:" + getLength(head));
        // 中间添加节点3
        node = new Node(3);
        head = BasicLinkList.insertNode(head, node, 2);
        System.out.println("中间添加节点3:" + BasicLinkList.toString(head));

        // 删除中间节点2
        head = BasicLinkList.deleteNode(head, 2);
        System.out.println("删除中间节点3:" + BasicLinkList.toString(head));

        // 删除头部节点1
        head = BasicLinkList.deleteNode(head, 1);
        System.out.println("删除头部节点1:" + BasicLinkList.toString(head));
    }

    /**
     * 获取链表长度
     *
     * @param head 链表头节点
     * @return 链表长度
     */
    public static int getLength(Node head) {
        int length = 0;
        Node node = head;
        while (node != null) {
            length++;
            node = node.next;
        }
        return length;
    }

    /**
     * 链表插入
     *
     * @param head       链表头节点
     * @param nodeInsert 待插入节点
     * @param position   待插入位置,取值从2开始
     * @return 插入后得到的链表头节点
     */
    public static Node insertNode(Node head, Node nodeInsert, int position) {
        // 需要判空,否则后面可能会有空指针异常
        if (head == null) {
            return nodeInsert;
        }
        //越界判断
        int size = getLength(head);
        if (position > size + 1 || position < 1) {
            System.out.println("位置参数越界");
            return head;
        }

        //在链表开头插入
        if (position == 1) {
            nodeInsert.next = head;
            head.last = nodeInsert;
//            return nodeInsert;
            //上面return还可以这么写:
            head = nodeInsert;
            return head;
        }

        Node pNode = head;
        int count = 1;
        while (count < position - 1) {
            pNode = pNode.next;
            count++;
        }
        nodeInsert.next = pNode.next;
        pNode.next = nodeInsert;


        //如果插入的不是尾部,要把下一个节点的last指向InsertNode
        if (nodeInsert.next != null){
            nodeInsert.next.last = nodeInsert;
        }

        nodeInsert.last = pNode;
        return head;
    }

    /**
     * 删除节点
     *
     * @param head     链表头节点
     * @param position 删除节点位置,取值从1开始
     * @return 删除后的链表头节点
     */
    public static Node deleteNode(Node head, int position) {
        if (head == null) {
            return null;
        }
        int size = getLength(head);
        //思考一下,这里为什么是size,而不是size+1
        if (position > size || position <1) {
            System.out.println("输入的参数有误");
            return head;
        }
        if (position == 1) {
            //curNode就是链表的新head
            head.next.last = null;
            Node newHead = head.next;
            head.next = null;
            return newHead;
        } else {
            Node cur = head;
            int count = 1;
            while (count < position - 1) {
                cur = cur.next;
                count++;
            }

            Node curNode = cur.next;
            cur.next = curNode.next;

            curNode.next.last = curNode.last;
            curNode.next = null;
            curNode.last = null;
        }
        return head;
    }

    /**
     * 输出链表
     *
     * @param head 头节点
     */
    public static String toString(Node head) {
        Node current = head;
        StringBuilder sb = new StringBuilder();
        //检查next指针
//        while (current != null) {
//            sb.append(current.data).append("\t");
//            current = current.next;
//        }

        //检查last指针,从尾部遍历
        while (current.next != null) {
            current = current.next;
        }
        while (current != null){
            sb.append(current.data).append("\t");
            current = current.last;
        }
        return sb.toString();
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值