编程导航算法通关村第一关 | 初识链表

定义

链表是一种数据结构, 它由一个个节点组成, 每个节点包含数据和指向下一个节点的指针, 这里的指针指向的其实就是下一个节点的引用。
示例

分类

根据节点的指针数量, 链表可以分为单向链表、双向链表和循环链表。单向链表每个节点只有一个指针, 指向下一个节点; 双向链表每个节点有两个指针, 分别指向前一个节点和后一个节点; 循环链表是一种特殊的链表, 它的最后一个节点的指针指向头节点。

与数组的比较

相同点

  1. 都是线性存储结构, 可以依次遍历其中所有的元素。
  2. 元素在内存中的存储顺序都是连续的

区别

  1. 数组的大小是固定的, 在创建后无法动态扩展, 而链表的大小可以根据需要动态增减。
  2. 数组的访问速度比较快, 可以通过下标直接访问元素; 而链表的访问速度较慢, 需要遍历整个链表才能访问其中的某个元素。
  3. 数组的内存分配是连续的, 如果需要插入或删除数组中的元素, 需要移动大量的元素; 而链表只需要改变指针的方向即可。

链表的基本操作

  1. 单向链表的创建
public class Node {
	// 数据
    public int value;
    // 指针
    public Node next;

    public Node(int value) {
        this.value = value;
    }

    public static void main(String[] args) {
        Node listnode=new Node(1);
    }
}

  1. 遍历链表

迭代方式:

public void traverseLinkedList(Node head) {
    Node pointer = head;
    while (pointer != null) {
        // 指针指向下一个节点
        pointer = pointer.next;
    }
}

递归方式:

public void traverseLinkedList(Node head) {
    if (head == null) {
        return;
    } else {
        // 递归遍历下一个节点
        traverseLinkedList(head.next);
    }
}
  1. 链表插入

    单链表的插入需要考虑三种情况: 头部、中部和尾部

    (1) 头部插入

    头部插入比较简单, 只需要将新节点的指针指向头部, 然后将新节点作为头节点。
    在这里插入图片描述

(2) 在链表中间插入

需要先遍历链表找到要插入的位置, 然后将当前位置接入到前驱节点和后驱节点之间。

需要注意的是, 在遍历链表时, 必须要在目标节点的前一个位置停下来, 不然将无法获取到前驱节点

在这里插入图片描述

(3) 在单链表的结尾插入

尾部插入比较简单, 将只需要将尾部节点指向新节点即可。
在这里插入图片描述
综上, 我们写出链表插入的方法如下:

 /**
     * 链表插入
     * @param head 头节点
     * @param newNode 新节点
     * @param position 插入的位置, 从 1 开始
     * @return 插入后得到的链表头节点
     */
    public Node insertNode(Node head, Node newNode, int position) {
        if (head == null) {
            // 插入的节点即为头节点
            return newNode;
        }
        // 判断索引是否越界
        int size = getLength(head);
        if (position < 1 || position > size + 1) {
            throw new RuntimeException("索引参数越界");
        }
        // 表头插入
        if (position == 1) {
            newNode.next = head;
            head = newNode;
            return head;
        }
        // 中间插入 (包括尾部)
        Node curNode = head;
        int index = 1;
        while (index < position - 1) {
            curNode = curNode.next;
            index++;
        }
        newNode.next = curNode.next;
        curNode.next = newNode;
        
        return head;
    }
    
    public int getLength(Node head) {
        int length = 0;
        Node node = head;
        while (node != null) {
            length++;
            node = node.next;
        }
        return length;
    }
  1. 链表删除

    删除同样分为在删除头部元素,删除中间元素和删除尾部元素。

    (1)删除表头节点

    将头节点重新指向头节点的下一个节点即可。
    在这里插入图片描述
    (2)删除最后一个节点

    将尾节点的前一个节点指向 null 即可。
    在这里插入图片描述
    (3)删除中间节点

    找到需要删除的节点, 将该节点的前一个节点重新指向该节点的下一个节点即可。
    在这里插入图片描述
    删除节点的完整实现:

/**
     * 删除节点
     * @param head 头节点
     * @param position 删除节点位置, 取值从 1 开始
     * @return 删除后的链表头节点
     */
    public Node deleteNode(Node head, int position) {
        if (head == null) {
            return head;
        }
        
        // 判断索引是否越界
        int size = getLength(head);
        if (position < 1 || position > size) {
            throw new RuntimeException("索引参数越界");
        }
        
        if (position == 1) {
            // 删除的为头节点
            return head.next;
        }else {
            // 删除中间节点 (包括尾节点)
            Node curNode = head;
            int index = 1;
            while (index < position - 1) {
                curNode = curNode.next;
                index++;
            }
            curNode.next = curNode.next.next;
        }
        
        return head;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值