算法通关村第一关-链表青铜挑战笔记

1、认识链表

链表是一种常见的数据结构,用于存储和组织数据。它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。每个节点都是独立的对象,通过指针连接在一起形成一个链式结构。
链表的特点是灵活的插入和删除操作,相对于数组来说,链表的大小可以动态变化。由于节点之间通过指针连接,因此在内存中可以非连续地存储。
链表可以分为以下几种类型:
单向链表(Singly Linked List):每个节点包含数据和指向下一个节点的指针。
单向链表

双向链表(Doubly Linked List):每个节点包含数据、指向下一个节点的指针和指向前一个节点的指针。
在这里插入图片描述

循环链表(Circular Linked List):最后一个节点指向第一个节点或者其他形成循环的节点。
在这里插入图片描述

双向循环链表(Doubly Circular Linked List):既是双向链表又是循环链表,即最后一个节点指向第一个节点,同时第一个节点也指向最后一个节点。
在这里插入图片描述

2、链表的创建

2.1 创建单向链表

public class BasicLinked {
    public static void main(String[] args) {
        int[] array = {1, 2, 3, 88, 99, 100};
        //创建链表
        Node head = initLinkedList(array);
        System.out.println(BasicLinked.toString(head));
    }
    /**
     * 初始化链表
     * @param array
     * @return
     */
    private static Node initLinkedList(int[] array) {
        //记录头节点,防止链表丢失
        Node head = null;
        //记录当前节点
        Node cur = null;
        for (int i = 0; i < array.length; i++) {
            Node newNode = new Node(array[i]);
            if (i == 0) {
                head = newNode;
            } else {
                cur.next = newNode;
            }
            cur = newNode;
        }
        return head;
    }
    /**
     * 输出链表
     * @param head 头节点
     * @return
     */
    public static String toString(Node head) {
        Node currNode = head;
        StringBuilder sb = new StringBuilder();
        while (currNode != null) {
            sb.append(currNode.val).append("\t");
            currNode = currNode.next;
        }
        return sb.toString();
    }
    static class Node {
        public int val;
        public Node next;
        Node(int x) {
            val = x;
            next = null;
        }
    }
}

得到链表效果如下:
在这里插入图片描述

3、链表的插入与删除

3.1 单向链表

3.1.1 单向链表的插入

单链表的插入操作需要考虑三种情况:首部、中部和尾部。
当在链表的表头插入时注意head需要重新指向表头。创建一个新的结点newNode,执行newNode.next = head就添加成功了,这时head需要重新指向newNode让head = newNode即可,或者是直接返回一个newNode就行。
在链表中间插入时,必须先遍历找到插入的位置,获取到目标结点的前一个位置的结点(避免丢失前面结点的信息)。
在尾部插入时只需要将尾结点指向新结点就行了。

	/**
     * 链表增加节点
     *
     * @param head       链表头节点
     * @param insertNode 增加的节点
     * @param position   增加节点的位置--从1开始
     * @return
     */
    public static Node insertNode(Node head, Node insertNode, int position) {
        //做安全性校验
        if (head == null) { //不做插入处理
            return insertNode;
        }
        int likedListLength = getLinkedListLength(head);
        if (position > likedListLength + 1 || position < 1) {
            System.out.println("位置参数输入有误");
            return head;
        }
        //当从表头插入
        if (1 == position) {
            insertNode.next = head;
            //return insertNode; 当前的insertNode已经是符合条件的链表了
            head = insertNode;
            return head;
        }
        //当从其他位置插入
        Node currNode = head; //当前节点--一步一步往后移(用来存被替换位置节点的上一个节点)
        int currIndex = 1;
        while (currIndex < position - 1) { //遍历链表,在遍历到链表目标位置的上两个位置时最后一次进入循环
            currNode = currNode.next; //最后一次循环获取到目标位置的上一个节点
            currIndex++;
        }
        insertNode.next = currNode.next; //先把被挤掉位置的节点接入到新加节点的next上,防止丢失,然后再让上一个节点指向新加的节点(顺序不能颠倒)
        currNode.next = insertNode;
        return head;
    }

	/**
     * 遍历链表获取到链表的长度
     *
     * @param head 头节点
     * @return
     */
    public static int getLinkedListLength(Node head) {
        int length = 0;
        Node curr = head;
        while (curr != null) {
            length++;
            curr = curr.next;
        }
        return length;
    }
	/**
     * 如果链表是单调的,链表增加节点保证链表仍保持调单调
     *
     * @param head       链表头节点
     * @param insertNode 增加的节点
     * @return
     */
    public static Node insertNodeKeepAsc(Node head, Node insertNode) {
        //做安全性校验
        if (head == null) { //不做插入处理
            System.out.println("链表为空,请检查!");
            return null;
        }
        int insertVal = insertNode.val;
        Node currNode = head;
        //当增加的节点值小于头节点值时
        if (insertVal <= currNode.val) {
            insertNode.next = head;
            return insertNode;
        }
        //找到符合条件的上一个位置
        while (currNode.next != null && insertVal > currNode.next.val) {
            currNode = currNode.next;
        }
        insertNode.next = currNode.next; //先把被挤掉位置节点的地址存起来,防止丢失
        currNode.next = insertNode;
        return head;
    }

3.1.2 单向链表的删除

删除同样分为删除头部、中间和尾部元素。
删除头部元素一般只要执行head=head.next就行。
删除中间节点时需要找到被删除结点的上一个结点curr,然后让被删除节点的上一个结点指向被删除结点的下一个结点就行,即curr.next = curr.next.next。
删除最后一个结点时也是需要找到要删除的结点的前驱结点curr,然后让curr.next = null即可。

/**
     * 根据给定位置删除链表节点
     *
     * @param head     链表头节点
     * @param position 删除节点位置,取值从1开始
     * @return 删除后的链表头节点
     */
    public static Node deleteNode(Node head, int position) {
        //安全性校验
        if (head == null) {
            return null;
        }
        int linkedListLength = getLinkedListLength(head);
        if (position > linkedListLength || position < 1) {
            System.out.println("位置参数输入错误,请检查");
            return head;
        }
        if (position == 1) { //删除头节点
            return head.next;
        } else { //删除其他节点
            Node currNode = head; //当前节点
            int count = 1;
            while (count < position - 1) { //获取到被删除节点的上一个位置节点
                currNode = currNode.next;
                count++;
            }
            Node delNode = currNode.next; //得到被删除节点的信息
            currNode.next = delNode.next; //把被删除节点排除在链表之外

        }
        return head;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值