虽然这个文章看着很多,但是大多是对于细节的讲解,如果想要快速了解,可以直接观看末尾代码.上面的代码内容都是来自于文章末尾的代码.

很重要的算法,也是比较简单的算法。

但是在java中,因为不存在c和c++中指针这个东西.

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

java中的指针都是靠其他方式模拟的.比如之前的队列指针是依靠数组下标代替的.

在链表中,java也采用了一种独特的方式来解决这个问题.

以下是链表结构:

class HeroNode{
public int no;
public String name;
public String nickname;
public HeroNode next;//指向下一个节点
}

这个代码本身就是一道题,名字起的有点奇怪~~~.

第2,3,4行比较好理解,就是链表中所保存的数据.

第五行复杂一些.

我们发现其中第5行有一个

public HeroNode next;//指向下一个节点

这里面实际上存储的是下一个节点的引用.

在C中,指针储存的是下一个地址,只不过在java中并不能直接操作地址,只能操作引用.所以将下一个位置的引用直接给next,要想找到下一个节点就直接调用这个next就知道了.

没错,实际上这个节点中的next值包含着下一个节点.

实际上你可以这么理解:

(实际上如果你在idea中debug的话,应该也是如此.)

因为java中操作的都是引用,实际上并不耗费太多空间

既然知道了这点,我们可以写构造函数了

   //构造器

    public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
//这个要传入的值主要是这个链表节点的val.

为了能让节点被打印出来,我们也要写一个toString办法

   public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
//为了显示方法,我们重新toString

就这样我们已经将我们的节点定义完毕.

之后就要写节点的功能了.

第一步就是初始化头节点,以及获取头节点的功能.

这个功能还是很用用处的.可以直接找到链表的开头,省去还要保存头节点的麻烦.

在leetcode里面你会遇见这种问题.

/初始化一個頭結點
private HeroNode head=new HeroNode(0,"","");
//返回头节点
public HeroNode getHead() {
return head;
}

这个步骤完成就该制作添加方式了



就是这个步骤.

要明确一下,这个并不是在链表中间插入节点而是在链表的最后加入节点.

也就说只需要遍历整个链表,到尾部位置.

ADD

/添加節點到單項列表
//思路当不考虑编号顺序时
//1.找到当前链表的最后的节点
//2.将最后这个节点的next指向新的节点 public void add(HeroNode heroNode){
//因为head节点不能动,因此我们需要一个辅助的遍历temp
HeroNode temp=head;
//遍历链表找到最后
while (true){
//找到链表的最后了
if (temp.next==null){
//
break;
}
temp=temp.next;
//如果没有找到最后
}
temp.next=heroNode;
}

ADDBYORDER

这种方式是按顺序加入到链表中.

采用的方式

temp指针用来寻找需要插入的位置.

找到之后将node1的next指针指向要插入的节点node2(将node2的引用存入node1的next种),将node2的next指向节点node3(将node3的引用存到节点node2的next中)

概念比较简单.

代码里面有解释的,分开说恐怕会说乱.

 //第二种方式添加英雄时,根据排名将英雄插入到指定位置
//(如果有这个排名,则添加失败,并给出提示.)
public void addByOrder(HeroNode heroNode){
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为单链表,因为我们找到的temp时位于添加位置的前一个节点,否则加插入不了.
HeroNode temp=head;
boolean flag=false;//添加的编号是否存在,默认为false
//这个循环就是找位置,并不是找到位置直接插入,这也是为什么break,找到位置就跳出
while(true){
if(temp.next==null){
//说明temp已经到了链表的最后了
break;
}
if(temp.next.no>heroNode.no){
//位置找到,就到temp的后面插入.因为要按顺序插入,所以找到第一个比要插入节点大的节点.
break;
}else if(temp.next.no==heroNode.no){
flag=true;
break; }
temp=temp.next;
}
//判断falg的值
if(flag){
//不能添加,说明编号存在
System.out.printf("准备插入的英雄的编号%d 已经存在了,不能加入\n",heroNode.no);
}else{
heroNode.next=temp.next;//将加入节点的指针指向下一个节点(temp.next是一个值,表示temp下一个节点)
temp.next=heroNode;//(temp.next在这里是一个指针)
}
}

删除节点

删除节点思路其实比较简单就是让temp辅助节点找到想要删除的位置的前一个位置.

temp.next.no==no

找到之后将temp的(要删除目标前一个的)next指向要删除目标的next(要删除目标的下一个).

即:

temp.next=temp.next.next;

这里插一嘴:

在链表中移动依靠的是不断的将链表的next赋给自己.

 temp=temp.next;//temp后移,遍历

就像之前说到的一样,因为next存的是引用.所以在每个next中都包含着下一个链表节点的地址,于是就可以不断地调用,直到链表的最后.

LIST()

显示链表中所有的元素.

这个依靠的就是上面讲得移动方法.

 public void list(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode temp=head.next;
while (true){
//判断是否到链表最后
if(temp==null){
break;
}
//输出节点信息(因为已经重写节点的toString了)
System.out.println(temp);
//将temp向后移,
temp=temp.next;
}
}
}

以上就是全部内容

下面放上完整代码

package linkdelist;

public class SingleLinkedListDemo {
public static void main(String[] args) {
//进行测试
//先创建节点
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
//创建一个链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
// singleLinkedList.add(heroNode1);
// singleLinkedList.add(heroNode2);
// singleLinkedList.add(heroNode3);
// singleLinkedList.add(heroNode4);
//加入按照编号的顺序
singleLinkedList.addByOrder(heroNode1);
singleLinkedList.addByOrder(heroNode4);
singleLinkedList.addByOrder(heroNode2);
singleLinkedList.addByOrder(heroNode3);
//显示一把
singleLinkedList.list();
//测试修改节点的代码
HeroNode newheroNode = new HeroNode(2, "小路", "小尾巴");
singleLinkedList.update(newheroNode);
System.out.println("修改后链表");
singleLinkedList.list();
//删除一个节点
System.out.println("删除一个");
singleLinkedList.del(4);
singleLinkedList.list();
System.out.println();
System.out.println(getLength(singleLinkedList.getHead()));//2
//测试一下看看是否得到了倒数第k个节点
HeroNode res=findLastIndexNode(singleLinkedList.getHead(),2);
System.out.println(res);
}
//方法:获取到单链表的节点的个数(如果是带头节点的链表,需求不统计头节点)
/**
*heronode链表的头节点
* 返回有效节点的个数.
*/
public static int getLength(HeroNode heroNode){
if (heroNode.next==null){
return 0;
}
int length=0;
//定义一个辅助变量,这里我们没有统计头节点
HeroNode cur=heroNode.next;
while(cur!=null){
length++;
cur=cur.next;
}
return length;
} //查找单链表中的倒数第k个节点[新浪面试题]
//思路
//1.编写一个方法接受head节点,同时接受一个index
//2.inidex表示的是倒数index个节点
//3.先把链表从头到尾遍历,得到链表的总的长度getlength
//4.得到size后,从链表的第一个开始遍历,(size-index个),就可以得到了
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;
for(int i=0;i<size-index;i++){
cur=cur.next;
}
return cur;
}
} //定义SingleLinkedList管理我們的英雄
class SingleLinkedList{
//初始化一個頭結點
private HeroNode head=new HeroNode(0,"","");
//返回头节点
public HeroNode getHead() {
return head;
} //添加節點到單項列表
//思路当不考虑编号顺序时
//1.找到当前链表的最后的节点
//2.将最后这个节点的next指向新的节点 public void add(HeroNode heroNode){
//因为head节点不能动,因此我们需要一个辅助的遍历temp
HeroNode temp=head;
//遍历链表找到最后
while (true){
//找到链表的最后了
if (temp.next==null){
//
break;
}
temp=temp.next;
//如果没有找到最后
}
temp.next=heroNode;
}
//第二种方式添加英雄时,根据排名将英雄插入到指定位置
//(如果有这个排名,则添加失败,并给出提示.)
public void addByOrder(HeroNode heroNode){
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为单链表,因为我们找到的temp时位于添加位置的前一个节点,否则加插入不了.
HeroNode temp=head;
boolean flag=false;//添加的编号是否存在,默认为false
//这个循环就是找位置,并不是找到位置直接插入,这也是为什么break,找到位置就跳出
while(true){
if(temp.next==null){
//说明temp已经到了链表的最后了
break;
}
if(temp.next.no>heroNode.no){
//位置找到,就到temp的后面插入.因为要按顺序插入,所以找到第一个比要插入节点大的节点.
break;
}else if(temp.next.no==heroNode.no){
flag=true;
break; }
temp=temp.next;
}
//判断falg的值
if(flag){
//不能添加,说明编号存在
System.out.printf("准备插入的英雄的编号%d 已经存在了,不能加入\n",heroNode.no);
}else{
heroNode.next=temp.next;//将加入节点的指针指向下一个节点(temp.next是一个值,表示temp下一个节点)
temp.next=heroNode;//(temp.next在这里是一个指针)
}
}
//修改节点的信息,根据编号来修改,既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.printf("没有找到编号等于%d的节点\n",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.next;//temp后移,遍历
}
//判断flag
if(flag){
temp.next=temp.next.next;
}else{
System.out.printf("要删除的%d节点不存在\n",no);
}
} //显示链表
public void list(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode temp=head.next;
while (true){
//判断是否到链表最后
if(temp==null){
break;
}
//输出节点信息(因为已经重写节点的toString了)
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;
} @Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
//为了显示方法,我们重新toString
}

java算法--链表的更多相关文章

  1. Java算法-各种题目总结

    1.排列计算 /*[程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析: 兔子 ...

  2. java算法--稀疏数组

    数据结构必要知识 线性结构 线性结构是最常用的数据结构,数据元素之间存在一对一的线性关系. 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构.顺序存储的线性表称为顺序表,顺序表中的存储元素是 ...

  3. JAVA算法系列 冒泡排序

    java算法系列之排序 手写冒泡 冒泡算是最基础的一个排序算法,简单的可以理解为,每一趟都拿i与i+1进行比较,两个for循环,时间复杂度为 O(n^2),同时本例与选择排序进行了比较,选择排序又叫直 ...

  4. JAVA算法系列 快速排序

    java算法系列之排序 手写快排 首先说一下什么是快排,比冒泡效率要高,快排的基本思路是首先找到一个基准元素,比如数组中最左边的那个位置,作为基准元素key,之后在最左边和最右边设立两个哨兵,i 和 ...

  5. java算法 蓝桥杯 乘法运算

    问题描述 编制一个乘法运算的程序. 从键盘读入2个100以内的正整数,进行乘法运算并以竖式输出. 输入格式 输入只有一行,是两个用空格隔开的数字,均在1~99之间(含1和99). 输出格式 输出为4行 ...

  6. java算法 蓝桥杯 扶老奶奶街

    一共有5个红领巾,编号分别为A.B.C.D.E,老奶奶被他们其中一个扶过了马路. 五个红领巾各自说话: A :我和E都没有扶老奶奶 B :老奶奶是被C和E其中一个扶过大街的 C :老奶奶是被我和D其中 ...

  7. java算法 蓝桥杯 高精度加法

    问题描述 在C/C++语言中,整型所能表示的范围一般为-231到231(大约21亿),即使long long型,一般也只能表示到-263到263.要想计算更加规模的数,就要用软件来扩展了,比如用数组或 ...

  8. java算法 蓝桥杯 格子位置

    问题描述 输入三个自然数N,i,j (1<=i<=N,1<=j<=N),输出在一个N*N格的棋盘中,与格子(i,j)同行.同列.同一对角线的所有格子的位置. 输入格式 输入共三 ...

  9. java算法----排序----(6)希尔排序(最小增量排序)

    package log; public class Test4 { /** * java算法---希尔排序(最小增量排序) * * @param args */ public static void ...

随机推荐

  1. [LC] 106. Construct Binary Tree from Inorder and Postorder Traversal

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  2. nodejs express 框架 上传文件

    web 项目应用express4.0框架 html 表单post 文件上传失败,后端无法获取提交文件 express不支持文件上传. 方式一 若是图片,可以将图片转码为BASE64上传 前端框架ang ...

  3. python django 模板语言循环字典

    urls.py from django.conf.urls import url from django.contrib import admin from cmdb import views url ...

  4. pytorch的visdom启动不了、蓝屏

    pytorch的visdom启动不了.蓝屏 问题描述:我是在ubuntu16.04上用python3.5安装的visdom.可是启动是蓝屏:在网上找了很久的解决方案:有三篇博文: https://bl ...

  5. vue基础指令了解补充及组件介绍

    v-once指令 """ v-once:单独使用,限制的标签内容一旦赋值,便不可被动更改(如果是输入框,可以主动修改) """ <di ...

  6. Android中的AlertDialog和ProgressDialog用法

    手机APP对话框是很多APP都有的下面来看下怎么实现的吧, 打开Android studio 然他自动创建好布局和类; 下面我们修改activity_main.xml中的代码 <?xml ver ...

  7. 文本快速分类利器fasttext使用心得(踩坑之路)

    fasttext是文本分类的一大利器,优点:快,嗷嗷快:缺点:暂未发现.但是我在使用其做文本分类时候还是遇到了挺多坑,今天先总结一个: 网上有人说设置训练参数的时候,ngrams设置大于2可以提高模型 ...

  8. configure: error: C compiler cannot create executables报错处理

    在测试环境安装php的imagick扩展在执行./configure生成编译文件时出现报错如下: 通过查看config.log发现有报错,在网上经验教程里发现前面的报错不管,直奔最后的报错即可,发现是 ...

  9. 题解 P1951 【收费站_NOI导刊2009提高(2)】

    查看原题请戳这里 核心思路 题目让求最大费用的最小值,很显然这道题可以二分,于是我们可以二分花费的最大值. check函数 那么,我们该怎么写check函数呢? 我们可以删去费用大于mid的点以及与其 ...

  10. 高效能Windows人士的N个习惯之一:启动篇

    接触电脑十多年,经历了各种折腾阶段,这几年开始沉静下来,不再追求花哨的界面与应用,只注重工作的效率,逐渐养成了一套自己的操作习惯,感觉不错,特撰文分享.标题借用了一下<高效能人士的七个习惯> ...