数据结构(一) 单链表的实现-JAVA
数据结构还是很重要的,就算不是那种很牛逼的,但起码得知道基础的东西,这一系列就算是复习一下以前学过的数据结构和填补自己在这一块的知识的空缺。按照链表、栈、队列、排序、数组、树这种顺序来学习数据结构这门课程把。
-WZY
一、单链表的概念
链表是最基本的数据结构,其存储的你原理图如下图所示
上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。
链表有很多种,比如单链表,双链表等等。我们就对单链表进行学习,其他的懂了原理其实是一样的。
二、用java实现单链表
语言只是一种工具,数据结构真正体会的是那种思想,这句话确实是这样,不管用什么写,其思想是不改变的。以前使用的是C++,现在用的是java,一步步来实现。
2.1、编写一个Node类来充当结点的模型。我们知道,其中有两个属性,1存放数据的data,2存放下一结点的引用,
package com.wuhao.demo01; public class Node {
//为了方便,这两个变量都使用public,而不用private就不需要编写get、set方法了。
//存放数据的变量,简单点,直接为int型
public int data;
//存放结点的变量,默认为null
public Node next; //构造方法,在构造时就能够给data赋值
public Node(int data){
this.data = data;
}
}
2.2、单链表的简单操作(增加,删除,获取总长度,链表元素排序,链表遍历)
2.2.1、增加结点操作,addNode(Node)
想法: 一开始也会想如果什么结点也没有。是不是需要判断插入的是第一个结点的问题,但写完后发现没有必要,是不是第一个结点操作都是一样的,所以通过移动的指针遍历整个链表,找到最后一个结点,往后添加即可。没难度。
/**
* 增加操作
* 直接在链表的最后插入新增的结点即可
* 将原本最后一个结点的next指向新结点
*/
public void addNode(Node node){
//链表中有结点,遍历到最后一个结点
Node temp = head; //一个移动的指针(把头结点看做一个指向结点的指针)
while(temp.next != null){ //遍历单链表,直到遍历到最后一个则跳出循环。
temp = temp.next; //往后移一个结点,指向下一个结点。
}
temp.next = node; //temp为最后一个结点或者是头结点,将其next指向新结点
}
2.2.2、插入结点到链表的指定位置。 insertNodeByIndex(int index,Node node)
注意:要知道插入操作需要的前提是什么,你才好写代码,写完之后,考虑如果在特殊位置上插入,是否也一样。还有需要进行插入位置是否可行的判断。
/**
* insertNodeByIndex:在链表的指定位置插入结点。
* 插入操作需要知道1个结点即可,当前位置的前一个结点
* index:插入链表的位置,从1开始
* node:插入的结点
*/
public void insertNodeByIndex(int index,Node node){
//首先需要判断指定位置是否合法,
if(index<1||index>length()+1){
System.out.println("插入位置不合法。");
return;
}
int length = 1; //记录我们遍历到第几个结点了,也就是记录位置。
Node temp = head; //可移动的指针
while(head.next != null){//遍历单链表
if(index == length++){ //判断是否到达指定位置。
//注意,我们的temp代表的是当前位置的前一个结点。
//前一个结点 当前位置 后一个结点
//temp temp.next temp.next.next
//插入操作。
node.next = temp.next;
temp.next = node;
return;
}
temp = temp.next;
}
}
2.2.3、删除指定位置上的结点 delNodeByIndex(int index)
/**
* 通过index删除指定位置的结点,跟指定位置增加结点是一样的,先找到准确位置。然后进行删除操作。
* 删除操作需要知道1个结点即可:和当前位置的前一个结点。
* @param index:链表中的位置,从1开始
*
*/
public void delNodeByIndex(int index){
//判断index是否合理
if(index<1 || index>length()){
System.out.println("给定的位置不合理");
return;
} //步骤跟insertNodeByIndex是一样的,只是操作不一样。
int length=1;
Node temp = head;
while(temp.next != null){
if(index == length++){
//删除操作。
temp.next = temp.next.next;
return;
}
temp = temp.next;
}
}
2.2.4、单链表进行选择排序 selectSortNode()
前提要知道什么是选择排序,如果不会,请查看我讲解排序的文章
分析
/**
* 对链表中的结点进行排序,按照从小到大的顺序,使用选择排序。
* 使用双层遍历。第一层遍历,正常遍历链表,第二层遍历,遍历第一层遍历时所用的结点后面所有结点并与之比较
* 选择排序比较简单,明白其原理,就能够写的出来。
*/
public void selectSortNode(){
//判断链表长度大于2,不然只有一个元素,就不用排序了。
if(length()<2){
System.out.println("无需排序");
return;
}
//选择排序
Node temp = head; //第一层遍历使用的移动指针,最处指向头结点,第一个结点用temp.next表示
while(temp.next != null){ //第一层遍历链表,从第一个结点开始遍历
Node secondTemp = temp.next; //第二层遍历使用的移动指针,secondTemp指向第一个结点,我们需要用到是第二个结点开始,所以用secondNode.next
while(secondTemp.next != null){//第二层遍历,从第二个结点开始遍历
if( temp.next.data > secondTemp.next.data){ //第二层中的所有结点依次与第一次遍历中选定的结点进行比较,
int t = secondTemp.next.data;
secondTemp.next.data = temp.next.data;
temp.next.data = t;
}
secondTemp = secondTemp.next;
}
temp = temp.next;
}
}
2.2.5、单链表进行插入排序 insertSortNode()
前提:要知道什么是插入排序。这个使用插入排序写了我好久,懵逼一样的状态,并且我认为我自己写效率不是很高。不管怎样,是骡子是马拿出来溜溜
/**
* 对链表进行插入排序,按从大到小的顺序,只要这里会写,那么手写用数组插入排序
* 也是一样的。先要明白原理。什么是插入排序,这样才好写代码。
* 插入排序:分两组,一组当成有序序列,一组当成无序,将无序组中的元素与有序组中的元素进行比较(如何比较,那么就要知道插入排序的原理是什么这里不过多阐述)
* 这里我想到的方法是,构建一个空的链表当成有序序列,而原先的旧链表为无序序列,按照原理,一步步进行编码即可。
*
*/
public void insertSortNode(){
//判断链表长度大于2,不然只有一个元素,就不用排序了。
if(length()<2){
System.out.println("无需排序");
return;
}
//创建新链表
Node newHead = new Node(0); //新链表的头结点
Node newTemp = newHead; //新链表的移动指针
Node temp = head; //旧链表的移动指针
if(newTemp.next == null){ //将第一个结点直接放入新链表中。
Node node = new Node(temp.next.data);
newTemp.next = node;
temp = temp.next; //旧链表中指针移到下一位(第二个结点处)。
}
while(temp.next != null){ // 遍历现有链表
while(newTemp.next != null){
//先跟新链表中的第一个结点进行比较,如果符合条件则添加到新链表,注意是在第一个位置上增加结点
//如果不符合,则跟新链表中第二个结点进行比较,如果都不符合,跳出while,判断是否是到了新链表的最后一个结点,如果是则直接在新链表后面添加即可 if(newTemp.next.data < temp.next.data){
Node node = new Node(temp.next.data);
node.next = newTemp.next;
newTemp.next = node;
break;
}
newTemp = newTemp.next;
}
if(newTemp.next == null){//到达最末尾还没符合,那么说明该值是新链表中最小的数,直接添加即可到链表中即可
//直接在新链表后面添加
Node node = new Node(temp.next.data);
newTemp.next = node;
}
//旧链表指针指向下一位结点,继续重复和新链表中的结点进行比较。
temp = temp.next;
//新链表中的移动指针需要复位,指向头结点
newTemp = newHead;
}
//开始使用新链表,旧链表等待垃圾回收机制将其收回。
head = newHead; }
2.2.6、当然还可以使用冒泡排序、归并排序,等等等,都可以自己去尝试,我就不写了。如果不懂这些排序,那么就看我写排序的文章把。
2.2.7、计算单链表的长度
/**
* 计算单链表的长度,也就是有多少个结点
* @return 结点个数
*/
public int length() {
int length=0;
Node temp = head;
while(temp.next != null){
length++;
temp = temp.next;
}
return length;
}
2.2.8、遍历单链表,打印data
/**
* 遍历单链表,打印所有data
*/
public void print(){
Node temp = head.next;
while(temp != null){
System.out.print(temp.data+",");
temp = temp.next;
}
System.out.println();
}
三、总结
基本的单链表操作就是上面这些了,自己完全手动写一写,会对单链表这种数据结构的理解有很大的帮助。当然其中比较难的是在于对链表的排序上,说难也不难,只要懂的几种排序的原理,就跟写伪代码一样,去实现即可。这里还有一些操作,大家可以动手做一做,我会在下一篇文章中讲解这些答案。
3.1、如何从链表中删除重复数据
3.2、如何找出单链表中的倒数第k个元素
3.3、如何实现链表的反转
3.4、如何从尾到头输出单链表
3.5、如何寻找单链表的中间结点
3.6、如何检测一个链表是否有环
3.7、如何在不知道头结点的情况下删除指定结点
3.8、如何判断两个链表是否相交
数据结构(一) 单链表的实现-JAVA的更多相关文章
- 数据结构之单链表的实现-java
一.单链表基本概念 单链表是一种链式存取的数据结构,用一组地址任意的存储单元(一般是非连续存储单元)存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点的构成:元素data + 指针next ...
- 数据结构(2):单链表学习使用java实现
单链表是单向链表,它指向一个位置: 单链表常用使用场景:根据序号排序,然后存储起来. 代码Demo: package com.Exercise.DataStructure_Algorithm.Sing ...
- 数据结构之单链表(基于Java实现)
链表:在计算机中用一组任意的存储单元存储线性表的数据元素称为链式存储结构,这组存储结构可以是连续的,也可以是不连续的,因此在存储数据元素时可以动态分配内存. 注:在java中没有指针的概念,可以理解为 ...
- Python数据结构之单链表
Python数据结构之单链表 单链表有后继结点,无前继结点. 以下实现: 创建单链表 打印单链表 获取单链表的长度 判断单链表是否为空 在单链表后插入数据 获取单链表指定位置的数据 获取单链表指定元素 ...
- 理解单链表的反转(java实现)
要求很简单,输入一个链表,反转链表后,输出新链表的表头. 反转链表是有2种方法(递归法,遍历法)实现的,面试官最爱考察的算法无非是斐波那契数列和单链表反转,递归方法实现链表反转比较优雅,但是对于不 ...
- javascript数据结构之单链表
下面是用javascript实现的单链表,但是在输出的时候insert方法中存在问题,chrome的console报错说不能读取空的属性,调试了很久都没有通过,先在这里存着,以后再来修改一下. //数 ...
- Java数据结构之单链表
这篇文章主要讲解了通过java实现单链表的操作,一般我们开始学习链表的时候,都是使用C语言,C语言中我们可以通过结构体来定义节点,但是在Java中,我们没有结构体,我们使用的是通过类来定义我们所需要的 ...
- Java数据结构-03单链表(二)
在之前我们封装了一些操作在接口类中,并在抽象类实现了相同的方法.下面我们开始写代码: 无头结点单链表:(注意下面的AbstractList是之前抽取的类,不是java.util包下的类) public ...
- 图解Java数据结构之单链表
本篇文章介绍数据结构中的单链表. 链表(Linked List)介绍 链表可分为三类: 单链表 双向链表 循环列表 下面具体分析三个链表的应用. 单链表 链表是有序的列表,它在内存中存储方式如下: 虽 ...
随机推荐
- 语音端点检测(Voice Activity Detection,VAD)
本文内容均翻译自这篇博文:(该博主的相关文章都比较好,感兴趣的可以自行学习) Voice Activity Detection(VAD) Tutorial 语音端点检测一般用于鉴别音频信号当中的语音出 ...
- 去除最后一个li的样式
推荐::::方法一,使用:first-child 纯css的:first-child伪类就可以胜任此任务,操作很方便,代码量忽略不计.支持IE7+,不支持IE6 :first-child /:l ...
- 无网 离线状态下pip3安装 django等软件
https://stackoverflow.com/questions/7300321/how-to-use-pythons-pip-to-download-and-keep-the-zipped-f ...
- 学以致用三十一-----IPAddressField has been removed
python 和 django版本 在进行makemigrations的时候报错 设置的字段 class Servers(models.Model): '''服务器信息''' hostname = m ...
- 解答VS2013编译报错不准确是什么原因
1.当程序在错误时,VS2013编译报出的错误有时不会一起全部报出,而是按错误的英文首字母逐个报出的 2.如果报错的信息双击点过去查看时又发现无明显错误问题时,这个这个时候可以是VS编译的缓存问题,这 ...
- win10更新永久关闭
最烦开发的时候windows来个更新 http://www.ghost580.com/win10/2016-10-21/17295.html
- MFC常见问题总结
1. c++中的函数前面加个LRESULT是什么意思啊?在微软vc提供的头文件中有定义在winnt.h中typedef long LONG;在windef.h中typedef LONG LRESULT ...
- ORACLE知识点总结
一.ORACEL常用命令 1.解锁账户:ALTER USER username ACCOUNT UNLOCK; 2.查看数据库字符集:SELECT USERENV ('language') FROM ...
- Java 动态字节码技术
对 Debug 的好奇 初学 Java 时,我对 IDEA 的 Debug 非常好奇,不止是它能查看断点的上下文环境,更神奇的是我可以在断点处使用它的 Evaluate 功能直接执行某些命令,进行一些 ...
- winform复制文件到指定目录
执行步骤 弹出选择对话框:var openFileDialog = new OpenFileDialog(); 设置选择内容,如所有图片:openFileDialog.Filter="图像文 ...