单链表及常见算法题

本文介绍了链表的基础知识,包括链表的存储结构和特性,并详细讲解了单链表的增加、按顺序插入、删除和更新结点等基本操作。此外,还讨论了单链表的面试题,如查找倒数第K个元素、反转链表、逆序打印链表和合并两个有序链表的解决方案。
摘要由CSDN通过智能技术生成

一、链表概述

1.1 链表介绍

链表( Linked List ),别名链式存储结构或单链表,是一种常见的基础数据结构,用于存储逻辑关系为 “一对一” 的数据。链表是线性表的一种,但是它并不像顺序表一样是连续存储在内存中的。链表的各个结点散布在各个内存区域,在每一个结点中都存放下一个结点的地址。

单链表在内存中是如下存储的:

在这里插入图片描述

其中:data 域存放的是本结点的数据,next 域存放的是下一个结点的地址

通过上面的图,我们可以得到链表的几个特性:

  1. 链表是以结点的方式来存储的,是链式存储;

  2. 每一个结点包含 data 域、next 域。其中 next 域存放的是下一个结点的地址;

  3. 链表各个结点并不一定是连续(所谓连续指的是地址连续)存储的;

  4. 链表分为带头结点的链表和没有头结点的链表。

1.2 代码描述结点

从上面的定义可以知道,链表中的每个结点中存放的是本结点的数据以及下一个结点的地址。所以链表中的结点可以定义如下:

public ListNode{
   
    public int age;				// 本结点的信息	
    public String name;
    public ListNode next; 		// 下一个结点的地址
}

二、单链表的基本操作

为了便于下面的演示,定义一个结点 HeroNode 如下,该结点可以理解为存储的是一个水浒英雄的信息:

class HeroNode {
   
    private int no;             // 本结点数据
    private String name;
    private String nickName;
    public HeroNode next;       // 指向下一个结点

    public HeroNode(int no, String name, String nickName) {
   
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

	// getter and setter and toString ...... 
}

假设我现在有个需求,要求使用链表实现一个水浒英雄榜管理系统。这个系统的具体功能如下:

  1. 可以增加水浒英雄(直接在链表末尾追加)
  2. 可以根据水浒英雄的编号顺序来插入英雄
  3. 可以根据水浒英雄的编号删除英雄
  4. 可以根据编号更新水浒英雄的信息

根据以上的 4 个需求,我们下面将分 4 步来逐一解决。

2.1 增加结点

增加结点,对应的是需求 1,也就是直接在链表的末尾追加一个结点。

思路:

  1. 首先我们需要创建一个头结点,该结点的作用就是表示单链表的头,如果没有头结点,我们是无法知道链表的首个结点是谁、在哪;

  2. 单链表是单向的,所以我们需要从头结点开始遍历整个链表直到末尾,然后增加结点到链表的末尾;

  3. 需要注意的是,头结点是万万不能乱动的,所以我们最好将头结点复制到一个临时结点变量中,对临时变量进行遍历。

代码实现:

// 头结点中是不存储数据的,因此数据无所谓。只要存储下一个结点的地址即可
private static final HeroNode headNode = new HeroNode(0, "", "");

/**
 * @Description 1. 插入结点到链表中(直接在末尾追加)
 */
private static void insertNode(HeroNode node) {
   
    // 首先需要拷贝一份头结点
    HeroNode tempNode = headNode;
    // 插入结点到链表之中,需要首先遍历链表
    while (true) {
   
        // 判断有没有到达链表末尾
        if (tempNode.next == null) {
   
            // 如果当前结点的下一个结点为 null 时,说明当前结点是最后一个结点
            tempNode.next = node;
            node.next = null;
            System.out.println("++++++++++++ 插入结点成功!");
            break;
        }
        // 没有到达链表末尾,那就后移一个位置
        tempNode = tempNode.next;
    }
}

2.2 按顺序插入结点

按顺序插入结点,对应的是需求 2,也就是根据编号从小到大的顺序将英雄插入到链表中。

思路:

  1. 首先还是要创建一个头结点,然后拷贝一个头结点作为辅助变量,使用辅助变量来遍历整个链表;
  2. 如果出现某个结点(假设是 A 结点)的下一个结点(假设是 B 结点)的编号大于待插入结点的情况,那么就首先将 B 结点记录在待插入的结点中,然后再将这个待插入结点插入到 A 结点之后;
  3. 如果遍历到了链表末尾还没找到编号更大的,就直接插入到末尾即可。

代码实现:

/**
 * @Description 2. 根据编号从小到大顺序插入结点
 */
public static void insertNodeByOrder(HeroNode node) {
   
    // 头结点是代码的柱石,不能动,复制一份作为辅助变量
    HeroNode tempNode = headNode;
    // 遍历链表
    while (true) {
   
        if (tempNode.next == null) {
        // 如果到了链表末尾,就直接插入
            tempNode.n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值