链表介绍

  • 链表时有序的列表,但是它在内存中是存储如下

小结

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含 data域,next域:指向下一个节点
  3. 如图:发现链表的各个节点不一定是连续存储
  4. 链表带头节点的链表和没带头节点的链表,根据实际的需求来确定

单链表

单链表(带头节点)逻辑示意图

单链表的应用实例

使用带 head头的单向链表实现水浒传英雄排行榜,管理

  1. 完成对英雄任务的增删改查操作,注:删除和修改,查找
  2. 第一种方式在添加英雄时,直接添加到链表的尾部,
  3. 第二种方时在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

单链表的创建示意图(添加),显示单向链表的分析

添加-直接添加到末尾

public class SingleLinkedListDemo {
public static void main(String[] args) {
//进项测试
//先创建节点
HeroNode hero1 = new HeroNode(1, "松江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//加入 想要创建链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(hero1);
singleLinkedList.add(hero4);
singleLinkedList.add(hero2);
singleLinkedList.add(hero3);
//显示一把
singleLinkedList.list();
}
} //定义 SingleLinkedListDemo 管理我们的英雄
class SingleLinkedList {
//先初始化一个头节点,头节点不要动,不存放具体的数据
private HeroNode head = new HeroNode(0, "", ""); //添加节点到单向链表
/*
* 思路:当不考虑编号顺序时
* 1. 找到当前链表的最后节点,
* 2. 将最后这个节点的next 指向 新的节点
* */
public void add(HeroNode heroNode) {
//因为 head节点不能动,因此我们需要一个辅助遍历 temp
HeroNode temp = head;
//遍历链表找到最后,
while (true) {
//找到链表的最后
if (temp.next == null) {
break;
}
//如果没有找到最后
temp = temp.next;
}
//当退出 while循环时,temp就指向链表的最后
//将最后这个节点的 next,指向新的节点
temp.next = heroNode;
} //显示链表 遍历
public void list() {
//先判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while (true) {
//判断是否到链表最后
if (temp == null) {
break;
}
//输出节点的信息
System.out.println(temp);
//不要忘记 将 temp后移
temp = temp.next;
}
}
} //定义HeroNode,每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next;//指向下一个节点 //构造器
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
} //为了显示方法,我们重写 toString next就不要显示了
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}

添加-顺序添加

    //第二种方时在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
public void addByOrder(HeroNode heroNode) {
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助我们找到添加的位置
//因为单链表,因为我们找到的temp,是位于 添加位置的前一个界定啊,否则插入不了
HeroNode temp = head;
boolean flag = false;//flag 标志添加的编号是否存在,默认为false
while (true) {
if (temp.next == null) {//说明 temp已经在链表的最后
break;//无论如何都要退出
}
if (temp.next.no > heroNode.no) {//位置找到,就在 temp的后面插入
break; } else if (temp.next.no == heroNode.no) {//说明希望添加的 heroNode的编号依然存在
flag = true;//说明编号存在
break;
}
//如果上面的都没有成立 temp要后移,遍历当前链表
temp = temp.next;
}
//判断 flag的值
if (flag) {//不能添加,说明编号已经存在
System.out.println("准备插入的英雄的编号" + heroNode.no + "已经存在不能添加");
} else {
//插入到链表中 temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}

更新

思路:

  1. 先找到该节点,通过遍历
  2. temp.neme = newHeroNode.name;

    temp.nickname = newHeroNode.nickname;
    //说明
//修改节点的信息,根据no编号修改,即 no编号不能修改
//1.根据 newHeroNode 的 no来修改即可
public void update(HeroNode newHeroNode) {
//判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//找到需要的修改的节点,根据no编号
//先定义一个辅助变量
HeroNode temp = head.next;
boolean flag = false;//表示是否找到该节点
while (true) {
if (temp == null) {
break;//到链表的最后==>最后节点的下一个
}
if (temp.no == newHeroNode.no) {
//找到了
flag = true;
break;
}
temp = temp.next;
}
//根据 flag判断是否找到修改的节点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else {//没有找到
System.out.println("没有找到编号" + newHeroNode.no + "的节点,不能修改");
}
}

删除

    //删除节点
//思路
//1. head 不能动,因此我们需要一个 temp辅助节点找到待删除的前一个节点
//2. 说明我们在比较时,是 temp.next.no 和 需要删除的节点的no比较
public void del(int no) {
HeroNode temp = head;
boolean flag = false;//标志是否找到待删除节点的前一个节点
while (true) {
if (temp.next == null) {//已经到链表的最后了
break;
}
if (temp.next.no == no) {
//找到了待删除节点的前一个节点 temp
flag = true;
break;
}
//temp 后移,遍历
temp = temp.next;
}
//判断 flag
if (flag) {
//可以删除
temp.next = temp.next.next;
} else {//未找到该节点
System.out.println("找删除的" + no + "节点未找到");
} }

单链表的面试题

  1. 求单链表中有效节点的个数
    //方法:获取单链表的节点的个数(如果是带头节点的链表,需要不统计头节点)
/**
* @param head 链表的头节点
* @return int 返回的就是有效节点的个数
*/
public static int getLength(HeroNode head) {
if (head.next == null) { //空链表
return 0;
}
int length = 0;
//定义一个辅助的变量
HeroNode cur = head.next;
while (cur != null) {
length++;
cur = cur.next;//遍历
}
return length;
}
  1. 查找单链表中的倒数第k个结点 【新浪面试题】
    //查找单链表中的倒数第k个结点 【新浪面试题】

    /**
* 思路:
* 1. 编写一个方法,接收 head节点,同时接收一个 index
* 2. index 表示倒数第 index个节点
* 3. 先把链表从到位遍历,得到链表的总的长度 [调用 getLength方法 获取单链表的个数]
* 4. 得到 size后,我们从链表的第一个开始遍历(size - index)个,就可以得到
* 5. 如果找到,则返回该节点,否则返回 null
*
* @param head 单链表头节点
* @param index 倒数第n个节点的下标
* @return 倒数第n个节点
*/
public static HeroNode findLastIndexNode(HeroNode head, int index) {
//判断如果链表为空,返回null
if (head.next == null) {
return null;//没有找到
}
//第一个遍历得到链表的长度(节点个数)
int size = getLength(head);
//第二次遍历 size-index 位置,就是我们倒数的第k个节点
//想做一个index的校验
if (index <= 0 || index > size) {
return null;
}
//先定义辅助变量,for 循环定义倒数的 index
HeroNode cur = head.next; //假如3个有效数据 //3-1=2
for (int i = 0; i < size - index; i++) {
cur = cur.next;
}
return cur;
}
  1. 单链表的反转【腾讯面试题,有点难度】
    //单链表的反转【腾讯面试题,有点难度】

    /**
* 思路:
* 1. 先定义一个节点 reverseHead = new heroNode();
* 2. reverseHead节点;不存放具体的数据;作用就是表示单链表头 next
* 3. 从头到位遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead的最前端
* 4. 原来的链表的 head.next=reverseHead.next
*
* @param head 需要反转的头节点
*/
public static void reverseList(HeroNode head) {
//如果当前链表为空,或者只有一个节点,无需反转,直接返回
if (head.next == null || head.next.next == null) {
return;
}
//定义一个辅助的指针(变量),帮助我们遍历原来的链表
HeroNode cur = head.next;
HeroNode next = null;//指向当前节点[cur]的下一个节点
//定义:reverseHead节点;不存放具体的数据;作用就是表示单链表头 next
HeroNode reverseHead = new HeroNode(0, "", "");
//遍历给原来的链表
// 并头到位遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead的最前端
//动脑筋
while (cur != null) {
next = cur.next;//先暂时保存当前节点的下一个节点,因为后面需要使用
cur.next = reverseHead.next;//将cur的下一个节点指向新的链表节点的头部[最前端]
reverseHead.next=cur;//将 cur连接到新的链表上
cur = next;//让 cur后移
}
//将 head.next 指向 reverseHead.next ,实现单链表的反转
head.next = reverseHead.next;
}
  1. 从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
    //从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
/**
* 思路:
* 1. 上面的题的要求就是逆序打印单链表
* 2. 方式1: 先将单链表进行反转操作,然后在遍历即可,这样做的问题是会破坏原来的单链表结构,不建议
* 3. 方式2: 可以利用栈这个数据结构,将各个节点压入到栈[先进后出,比如子弹进子弹夹]中,然后
* 利用栈的先进后出的特点,就实现了逆序打印的效果
* 采用方式二
*
* @param head 单链表头部
*/
public static void reversePrint(HeroNode head) {
if (head.next == null) {
return;//空链表不能打印
}
//创建一个栈,将各个节点压入栈中
//Stack特点; 先进后出
Stack<HeroNode> stack = new Stack<>();
HeroNode cur = head.next;
//将链表的所有节点压入栈中
while (cur != null) {
stack.push(cur);
//一定rur要后移,这样就能压入下一个节点
cur = cur.next;
}
//将栈中的节点打印,pop 出栈
while (stack.size() > 0) {
System.out.println(stack.pop());//弹栈
}
}
  1. 合并两个有序的单链表,合并之后的链表依然有序
    // 合并两个有序的单链表,合并之后的链表依然有序
//和反转很像,创建一个新的链表发现链表中的哪一个更小就把他加入到新的链表中
/**
* @param head1 头节点
* @param head2 头节点
* @return 合并后的头节点
*/
public static HeroNode mergeLinkList(HeroNode head1, HeroNode head2) {
if (head1.next == null) {
return head2.next;
}
if (head2.next == null) {
return head1.next;
}
//创建节点用来返回
HeroNode newList = new HeroNode(0, "", "");
HeroNode head = newList;//新链表的头结点
//使用临时变量来存储头节点
HeroNode cur1 = head1.next;
HeroNode cur2 = head2.next;
while (cur1 != null && cur2 != null) {
//比较两个no大小存储小的
if (cur1.no < cur2.no) {
newList.next = cur1;
//下移
cur1 = cur1.next;
} else {
newList.next = cur2;
//下移
cur2 = cur2.next;
}
newList = newList.next;
}
//如果还有每存完的
if (cur1 == null) {
while (cur2 != null) {
newList.next = cur2;
//下移
cur2 = cur2.next;
newList = newList.next;
}
} else {
while (cur1 != null) {
newList.next = cur1;
//下移
cur1 = cur1.next;
newList = newList.next;
}
}
return head;
}

双链表

链表 Linked List的更多相关文章

  1. 链表Linked List

    链表Linked List 1. 链表 数组是一种顺序表,index与value之间是一种顺序映射,以O(1)O(1)的复杂度访问数据元素.但是,若要在表的中间部分插入(或删除)某一个元素时,需要将后 ...

  2. 算法与数据结构基础 - 链表(Linked List)

    链表基础 链表(Linked List)相比数组(Array),物理存储上非连续.不支持O(1)时间按索引存取:但链表也有其优点,灵活的内存管理.允许在链表任意位置上插入和删除节点.单向链表结构一般如 ...

  3. 数据结构之链表(Linked list)

    说明:如果仔细阅读完全文后,可能感觉有些不统一,这里先说明下原因. 链表尾引用不统一:在介绍单链表时,只有一个链表首部的引用(head) 指向第一个节点.你看到后面关于双链表及循环列表时,除了指向第一 ...

  4. 【LeetCode题解】链表Linked List

    1. 链表 数组是一种顺序表,index与value之间是一种顺序映射,以\(O(1)\)的复杂度访问数据元素.但是,若要在表的中间部分插入(或删除)某一个元素时,需要将后续的数据元素进行移动,复杂度 ...

  5. 数据结构与算法 —— 链表linked list(01)

    链表(维基百科) 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必须按顺序存储, ...

  6. 数据结构与算法 —— 链表linked list(03)

    继续关于linked list的算法题: 删除排序链表中的重复元素 给定一个排序链表,删除所有重复的元素使得每个元素只留下一个. 案例: 给定 1->1->2,返回 1->2 给定  ...

  7. [Java]LeetCode141. 环形链表 | Linked List Cycle

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  8. 带环链表 linked list cycle

    1 [抄题]: 给定一个链表,判断它是否有环. [思维问题]: 反而不知道没有环怎么写了:快指针fast(奇数个元素)或fast.next(偶数个元素) == null [一句话思路]: 快指针走2步 ...

  9. LeetCode 141:环形链表 Linked List Cycle

    给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. Given a l ...

  10. LeetCode 141. 环形链表(Linked List Cycle) 19

    141. 环形链表 141. Linked List Cycle 题目描述 给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 ...

随机推荐

  1. Linux命令(二)之克隆虚拟机及修改网卡信息

    .subTitle { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); border- ...

  2. Linux中的DNS主从解析

    目录 一.主服务器配置(紧接着正反解析实验) 1.1.修改区域配置文件 二.从服务器配置(启动另一台虚拟机) 2.1.安装服务 2.2.修改主配置文件 2.3.修改区域配置文件 2.4.修改dns服务 ...

  3. CleanArchitecture Application代码生成插件-让程序员告别CURD Ctrl+C Ctrl+V

    这是一个根据Domain项目中定义的实体对象(Entity)生成符合Clean Architecture原则的Application项目所需要的功能代码,包括常用的Commands,Queries,V ...

  4. HCNA Routing&Switching之地址转换技术NAT

    前文我们了解了包过滤工具ACL相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15156308.html:今天我们来聊一聊地址转换技术NAT相关话题: ...

  5. MATLAB—M函数文件

    文章目录 一.M文件 1.脚本文件和函数文件的区别 二.M函数文件 1.创建方法 2.文件形式 2.函数形参及注释 3.函数的调用 4.函数变量 5.主函数.子函数 三.函数句柄 一.M文件 首先,要 ...

  6. SpringBoot Spring Security 核心组件 认证流程 用户权限信息获取详细讲解

    前言 Spring Security 是一个安全框架, 可以简单地认为 Spring Security 是放在用户和 Spring 应用之间的一个安全屏障, 每一个 web 请求都先要经过 Sprin ...

  7. deepin下启动自己的springcloud服务报错

    java.nio.file.AccessDeniedException: /home/msan/logs/csp/sentinel-record.log.2021-01-04.0.2.lck at s ...

  8. mpvue学习笔记

    坑一: 挂载在Vue.prototype上的属性,在模板语法里面是undefined,必须经过computed计算过一下才能用. 坑二: 关于生命周期钩子 因为小程序的历史页面不会销毁,所以在生命周期 ...

  9. UDP与TCP的对比

    参考网址:https://blog.csdn.net/m0_38121874/article/details/82914634 1.报头(1)TCP协议报头TCP指传输控制协议,其报头格式如下: TC ...

  10. 在ASP.NET Core调用WebService

    一.前言 现实生产中,有一些比较老的系统对外提供的接口都是WebService形式的,如果是使用.NET Framework创建的项目调用WebService非常方便,网上有很多代码示例,这里不在讲解 ...