文章目录

  • 链表介绍
    • 1. 单链表应用实例
      • 1.1 实现思路
      • 1.2 代码实现
    • 2.单链表常见面试题
      • 2.1 求单链表中有效节点的个数
      • 2.2 查找单链表中倒数第K个节点
      • 2.3 单链表的反转
      • 2.4 逆序打印单链表
      • 2.5 合并两个单链表,合并后依然有序
    • 3. 双链表应用实例
      • 3.1 单链表缺点
      • 3.2 实现思路
      • 3.3 代码实现

链表介绍

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

  • 链表是以节点的方式来存储,是链式存储
  • 每个节点包含data域,next域:指向下一个节点
  • 链表的各个节点不一定是连续存储
  • 链表分带头节点的链表和没有头结点的链表

单链表(带头节点)逻辑结构示意图如下:

1. 单链表应用实例

使用带头的单向链表实现 -带编号的英雄人物的增删改查操作。

1.1 实现思路

1.add方法添加英雄时,直接添加到链表尾部


2.addByOrder方法添加英雄时,根据英雄编号将英雄插入到指定的位置

3.update方法修改节点功能。
1)通过遍历根据no找到该节点,没找到就后移temp=temp.next
2)替换name和nickName
4.删除节点

1.2 代码实现

package com.zh.LinkedList;public class SingleLinkedListDemo01 {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 list = new SingleLinkedList();
//        list.add(hero1);
//        list.add(hero2);
//        list.add(hero3);
//        list.add(hero4);
//        list.listTraverse();list.addByOrder(hero1);list.addByOrder(hero4);list.addByOrder(hero3);list.addByOrder(hero2);list.listTraverse();HeroNode heroNew = new HeroNode(2,"佐伊","暮光星灵");list.update(heroNew);System.out.println("-------------------修改后------------------");list.listTraverse();list.delete(1);System.out.println("-------------------删除后------------------");list.listTraverse();}
}//创建链表
class SingleLinkedList{//创建一个头节点,头节点不能动,不存放具体的数据HeroNode head = new HeroNode(0,"","");/*当不考虑编号顺序时1.找到当前链表的最后节点2.将最后这个节点的next指向新的节点*/public void add(HeroNode heroNode){//因为头节点不能动,所以创建一个辅助节点tempHeroNode temp = head;while (true){//找到链表的最后节点if (temp.next==null){break;}else {//如果没有找到链表的最后,后移一位temp = temp.next;}}//将最后这个节点的next指向新的节点temp.next=heroNode;}//按英雄编号来添加public void addByOrder(HeroNode heroNode){HeroNode temp = head;boolean flag = false;//判断添加的节点是否存在while (true){if (temp.next==null){//链表为空break;}if (temp.next.no > heroNode.no){//如果temp指向的下一个节点编号大于添加的编号,说明新节点位于temp与temp.next之间break;}else if (temp.next.no == heroNode.no){//说明添加的节点编号已经存在flag = true;break;}//节点后移temp = temp.next;}if (flag){System.out.println("要添加的节点已存在,编号为"+heroNode.no);}else {//插入到链表中,将新节点的next指向temp.next,temp.next指向新节点heroNode.next = temp.next;temp.next = heroNode;}}//链表节点修改public void update(HeroNode heroNode){//判断是否为空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 == heroNode.no){flag = true;break;}//没找到就后移temp = temp.next;}if (flag) {temp.name = heroNode.name;temp.nickName = heroNode.nickName;}else {System.out.println("没有找到编号为"+ heroNode.no+"的节点,不能进行修改");}}//删除节点public void delete(int no){HeroNode temp = head;boolean flag = false;if (head.next==null){System.out.println("链表为空");}while (true){if (temp.next==null){//已经遍历完break;}if (temp.next.no==no){flag = true;break;}temp = temp.next;}if (flag){temp.next = temp.next.next;}else {System.out.println("没有找到编号为"+no+"的节点,无法删除");}}//链表遍历public void listTraverse(){//创建一个辅助节点遍历HeroNode temp = head.next;//判断链表是否为空if (temp==null){System.out.println("链表为空");return;}else {while (true){//判断链表是否到最后if (temp == null){break;}else{//输出节点信息System.out.println(temp);//将节点后移temp=temp.next;}}}}
}
//单链表的英雄节点
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;}@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}
}

2.单链表常见面试题

2.1 求单链表中有效节点的个数

  /**获取单链表有效节点的个数** @param head* @return*/public  int getLength(HeroNode head){int length = 0;HeroNode temp = head;if (head.next==null){return 0;}while (temp.next!=null){length++;temp = temp.next;}return length;}

2.2 查找单链表中倒数第K个节点

    /*** 查找链表中倒数第K个节点* @param head* @param index* @return*/public HeroNode findLastIndexNode(HeroNode head,int index){//index表示倒数第index个节点//获取链表长度int length = getLength(head);HeroNode temp = head.next;if (head.next==null){return null;}//从第一个开始遍历length - index就可以得到倒数第index个节点int signal = length - index;if (index <= 0 || index > length){return null;}for (int i = 0; i <signal ; i++) {temp=temp.next;}return temp;}

2.3 单链表的反转

思路:

  1. 定义一个新的头结点reverseHead.
  2. 遍历原来的链表,每遍历一个节点就将其取出,放在新链表最前端
  3. 原来链表的head.next = reverseHead
    /*** 单链表的反转* @param head*/public void reverseList(HeroNode head){//如果链表为空或只有一个节点,那么直接返回if (head.next==null || head.next.next==null){return;}//创建一个新的头结点HeroNode reverseHead = new HeroNode(0,"","");HeroNode cur = head.next;HeroNode next = null;while (cur!=null){next =cur.next;//保存当前节点的下一个节点//将cur的下一个节点指向链表的最前端cur.next = reverseHead.next;//将cur连接到cur的下一个节点和头结点之间。reverseHead.next = cur;//后移cur = next;}//将头结点指向反转后的头结点,实现反转head.next = reverseHead.next;}

2.4 逆序打印单链表

要求:反向遍历,用栈实现

    /*** 单链表逆序输出(用栈实现)* @param head*/public void reversePrint(HeroNode head){if (head.next==null){return;}HeroNode cur = head.next;Stack<HeroNode> stack = new Stack<>();while (cur!=null){stack.add(cur);//将链表的节点压入栈cur = cur.next;}while (stack.size()>0){System.out.println(stack.pop());//先进后出输出}}

2.5 合并两个单链表,合并后依然有序

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {if (l1 == null) return l2;if (l2 == null) return l1;ListNode head = null;if (l1.val <= l2.val){head = l1;head.next = mergeTwoLists(l1.next, l2);} else {head = l2;head.next = mergeTwoLists(l1, l2.next);}return head;
}

3. 双链表应用实例

3.1 单链表缺点

管理单向链表的缺点分析:

  1. 单向链表,查找的只能是一个方向,而双向链表可以向前或者向后查找
  2. 单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以前面单链表删除,总是找到temp,temp是待删除节点的前一个节点。

下面用双链表来实现上面的增删改查功能。

3.2 实现思路

遍历:和单链表一样,但是既可以向前遍历也可以向后遍历
添加(默认添加到双向链表最后): 先找到双向链表最后的一个节点

temp.next = heroNode
heroNode.pre = temp;

修改 :和单链表思路一样
删除:因为是双向链表,所以可以自我删除,直接找到要删除的节点

temp.pre.next = temp.next;
temp.next.pre = temp.pre;

3.3 代码实现

public class DoubleLinkedListDemo {public static void main(String[] args) {HeroNode2 hero1 = new HeroNode2(1,"伊泽瑞尔","冒险家");HeroNode2 hero2 = new HeroNode2(2,"拉克丝","光辉女郎");HeroNode2 hero3 = new HeroNode2(3,"亚索","疾风剑豪");HeroNode2 hero4 = new HeroNode2(4,"卢锡安","圣枪游侠");HeroNode2 hero2New = new HeroNode2(2,"佐伊","暮光星灵");DoubleLinkedList list = new DoubleLinkedList();//添加节点list.add(hero1);list.add(hero2);list.add(hero3);list.add(hero4);System.out.println("----------------遍历-----------------");list.traverse();System.out.println("----------------修改后-----------------");list.update(hero2New);list.traverse();System.out.println("----------------删除后-----------------");list.del(3);list.traverse();}}
class DoubleLinkedList{//创建一个头节点,头节点不能动,不存放具体的数据private HeroNode2 head = new HeroNode2(0,"","");public void setHead(HeroNode2 head) {this.head = head;}public HeroNode2 getHead() {return head;}/*** 在链表的最后添加节点*/public void  add(HeroNode2 heroNode){//头结点不能动,创建一个辅助节点HeroNode2 temp = head;//找到最后一个节点while (temp.next!=null){temp = temp.next;}//将新节点添加到节点的最后temp.next = heroNode;heroNode.pre = temp;}/*** 遍历链表*/public void traverse(){if (head.next == null){System.out.println("链表为空");return;}HeroNode2 temp = head.next;while (temp!=null){System.out.println(temp);temp=temp.next;}}/*** 修改链表节点*/// 修改一个节点的内容, 可以看到双向链表的节点内容修改和单向链表一样// 只是 节点类型改成 HeroNode2public void update(HeroNode2 newHeroNode) {// 判断是否空if (head.next == null) {System.out.println("链表为空~");return;}// 找到需要修改的节点, 根据no编号// 定义一个辅助变量HeroNode2 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.printf("没有找到 编号 %d 的节点,不能修改\n", newHeroNode.no);}}/*** 删除节点*/public void del(int no) {// 判断当前链表是否为空if (head.next == null) {// 空链表System.out.println("链表为空,无法删除");return;}HeroNode2 temp = head.next; // 辅助变量(指针)boolean flag = false; // 标志是否找到待删除节点的while (true) {if (temp == null) { // 已经到链表的最后break;}if (temp.no == no) {flag = true;break;}temp = temp.next; // temp后移,遍历}// 判断flagif (flag) { // 找到// 可以删除// temp.next = temp.next.next;[单向链表]temp.pre.next = temp.next;// 这里我们的代码有问题?// 如果是最后一个节点,就不需要执行下面这句话,否则出现空指针if (temp.next != null) {temp.next.pre = temp.pre;}} else {System.out.printf("要删除的 %d 节点不存在\n", no);}}
}
class HeroNode2{public int no;//序号public String name;public String nickName;public HeroNode2 next;//指向下一个节点public HeroNode2 pre;//指向前一个节点public HeroNode2(int no, String name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}
}

(数据结构与算法)单链表与双链表增删改查的实现。相关推荐

  1. c语言 双向链表增删修查,手写双链表,并实现增删改查

    手写双链表,并实现增删改查 public class DoublyLinkedListT { // 一个空的头节点 private final Node head = new Node(null); ...

  2. python单链表操作_单链表的创建、增删改查等操作(Python实现)

    单链表的创建.增删改查等操作(Python实现) # 单链表 class Node: def __init__(self, elem): self.elem = elem self.next = No ...

  3. 单链表的实现与增删改查

    单链表的实现与CRUD 单链表的实现与增删改查 链表实现 结果: Hero{id=1, name='dog1', otherName='aadog1'} Hero{id=2, name='dog2', ...

  4. 【一起学数据结构与算法】Java实现双链表

    目录 一.双链表的概念 二.双链表一些方法的实现 2.1 双链表的属性 2.2 打印双链表 2.3 得到双链表的长度 2.4 查找是否包含关键字key是否在双链表中 2.5 头插法 2.6 尾插法 2 ...

  5. 数据结构与算法详细解析之双链表结构解析

    一.双向链表结构分析及其特点: 双链表结构:双向链表中每一个Node节点含Data域及pre指针(指向上一个节点).Next指针(指向下一个节点): 特点: 1)双向链表可以双向查找(向前或者向后查找 ...

  6. 数据结构-链表:对链表进行初始化、增删改查

    废话少说,先上代码: #include <stdio.h> #include <stdlib.h>typedef struct Node {int data;struct No ...

  7. 无空头链表详解(增删改查)

    #include <stdio.h> #include <stdlib.h>//节点结构体 struct Node {int a;struct Node *pNext; } ; ...

  8. 韩老师——数据结构与算法—单链表的生成及增删改查操作和常见关于链表的面试题java代码实现

    话不多说直接上代码. public class SingleLinkedListDemo {public static void main(String[] args) {//测试HeroNode n ...

  9. 【数据结构与算法】实验 编写双链表的结点查找和删除算法

    Experiment 1 Linked List(4 hours) Prerequisites: Students should have mastered the following prerequ ...

  10. 单链表的实现:增删改查

    1 /* 2 *@author ? 3 *@since 2011/07/29 4 *@function 实现链表的常用操作,比较粗糙. 5 * 功能大概是:显示链表结构:查询链表:删除节点:插入结点: ...

最新文章

  1. 操作系统、Linux、CPU的几个疑惑点
  2. WebSocket使用案例
  3. 一步步编写操作系统4 安装x86虚拟机 bochs
  4. 【c# 学习笔记】所有类的父类:System.object
  5. Apache kafka原理与特性(0.8V)
  6. Solidity编程 二 之Solidity安装
  7. stm32双向可控硅调压程序_双向可控硅的工作原理
  8. Flume Sink
  9. Python稳基修炼之计算机等级考试易错细节题4(含答案)
  10. MISRA C 2004
  11. matlab 复数夹角如何求,matlab钟输入一个复数,如何输出复数的模和相角?
  12. ANT 下载,ant的配法 整理
  13. 俄罗斯卢布为什么整个2016年都在升值?
  14. 360校招笔试题总结4
  15. 最完整最全面的汉化中文游戏列表
  16. LIN雨量传感器:PCB拆解及LIN数据协议解析
  17. Java 处理资源的try语句 (try-with-resources, TWR)
  18. 2022年金砖国家职业技能大赛(决赛)网络空间安全赛项 | 浙江赛区选拔赛 任务书
  19. Java获取单词的首字母的方法_java – 从句子中的每个单词中提取第一个字母
  20. 港科夜闻|香港科大张明杰教授课题组报道相分离介导突触前膜活性区的组织方式...

热门文章

  1. C++ Primer 5th笔记(chap 18 大型程序工具)类型转换与多个基类
  2. C++(十)——模板(上)
  3. buu 凯撒?替换?呵呵!
  4. [ATF]-TEE/REE系统切换时ATF的寄存器的保存和恢复
  5. 【安全漏洞】Rocket.Chat 远程命令执行漏洞分析
  6. python实现AES算法
  7. CPU和软件模拟异常的执行流程
  8. 10、 HAVING:过滤分组
  9. currentThread()方法的作用
  10. 判断字符串是否以指定字符开头