算法是什么(二)手写个链表(java)
算法是什么(二)手写个链表(java)
liuyuhang原创,未经允许禁止转载
目录
很多语言的API中都提供了链表实现,或者扩展库中实现了链表。
但是更多的情况下,Map(或hash)和List(非定容数组)的使用率更高。
这并非意味着链表不应该掌握或不使用了。
链表本质上是一种及其高等的数据结构展现,扩展性极强。
链表可轻松扩展成树结构,二叉树,环,栈,队列,双向队列等。
很多种数据结构都是依据链表的形式扩展出来的,虽然我知道的并不多,但是我知道链表的重要性。
所以,手写一个链表试试。
1、本质
链表的本质是Node(节点),其中保存着信息(info),前一个节点(prevNode),后一个节点(nextNode)。
和基础操作API构成
2、特性
链表为了操作的方便性,多数会将链表做成双向链表,即既包含next,又包含prev
3、基础API
链表的操作,至少需要增删改查,不然还算啥容器了:
增:默认从末尾添加,添加指定index,在首添加三种方式添加单一元素。
删:删除头,删除尾,删除指定index的元素,删除当前指针元素。
改:设置头,设置尾,设置指定index的元素,设置当前指针元素。
查:获取当前指针元素,获取首元素,获取尾元素,获取下一个元素,获取上一个元素,获取指定index的元素
有些API还提供了更多的操作:
获取当前容器的size
获取当前指针的index
迭代器
排序工具
判空
克隆
转数组
逆转
去重复
序列化
等等。。。
链表可做的操作会比想象的多的多。
4、Java中的链表
Java中常用的链表是LinkedList,实现了List接口,继承AbstractLinkedList。同时还有其他接口
同时,还有Queue大类,并非在Collection接口下,但是底层有些是使用链表实现的,功能有些是重复的。
对于需要作为原子操作的各种功能的队列来说,可以考虑。
在扩展Java中的链表的时候,有几种方式供选择:
①继承LinkedList,添加扩展算法;
②实现List,继承AbstractLinkedList,同时扩展算法;
③使用装饰模式,在构造器中调用链表的构造器,同时扩展算法;
④不受约束自己写一个吧。。。
5、写一个简单的链表
我尝试写了一个简单的链表,以前也大概看过C的链表和Java的链表,写的过程中全凭记忆,
大约花了十个小时才写完,哩哩啦啦好多天。
代码拙劣,为了以后尝试链表的其他算法(排序,转换,反转,环链表,扩展二叉树)做准备。
代码如下:
package com.FM.ArrayStudy; public class SimpleLinkedList<T> { private Node<T> pointer;// 当前指针节点
private Node<T> firstNode;// 首个节点
private Node<T> lastNode;// 末尾节点
private Integer index = 0;// 当前指针index
private Integer size = 0;// 当前容量 /**
* 获取当前pointer的info
* @return
*/
public T getThis() {
if (null == pointer) {
return firstNode.info;
} else {
return pointer.info;
}
} /**
* 获取下一个元素的内容,若没有下一个元素,则返回null
*
* @return
*/
public T getNext() {
if (index.equals(size - 1)) {
return null;
} else {
if (null == pointer) {
pointer = firstNode;
pointer = pointer.next;
T info = pointer.info;
index++;
return info;
} else {
pointer = pointer.next;
T info = pointer.info;
index++;
return info;
}
}
} /**
* 修改指定index的元素的方法
*
* @param index
* @param t
* @throws Exception
*/
public void set(Integer index, T t) throws Exception {
if (index > -1 && index < size - 1) {
Node<T> node = getNodeByIndex(index);
node.info = t;
} else {
throw new Exception("get ele " + index + " out of index");
}
} /**
* 修改首元素
*
* @param t
*/
public void setFirst(T t) {
firstNode.info = t;
} /**
* 修改尾元素
*
* @param t
*/
public void setLast(T t) {
lastNode.info = t;
} /**
* 从指定index移除node的方法
*
* @param index
* @throws Exception
*/
public void remove(Integer index) throws Exception {
if (index > -1 && index < size) {
if (index.equals(0)) {
Node<T> node = getNodeByIndex(1);
firstNode = node;
firstNode.prve = null;
} else if (index.equals(size - 1)) {
Node<T> node = getNodeByIndex(size - 2);
lastNode = node;
lastNode.next = null;
} else {
Node<T> node = getNodeByIndex(index);
Node<T> nextNode = node.next;
Node<T> prveNode = node.prve;
prveNode.next = nextNode;
nextNode.prve = prveNode;
node = null;
}
size--;
} else {
throw new Exception("get ele " + index + " out of index");
} } /**
* 获取当前元素在链表中的位置
*
* @return
*/
public int getIndex() {
return index;
} /**
* 获取当前链表size的方法
*
* @return
*/
public Integer size() {
return size;
} /**
* 判断容器是否为空的方法,为空返回true
*
* @return
*/
public boolean isEmpty() {
if (size.equals(0)) {
return true;
} else {
return false;
} } /**
* 根据index查询链表中元素的方法
*
* @param index
* @return
* @throws Exception
*/
public T getByIndex(Integer index) throws Exception {
if (index > -1 && index < size) {
Node<T> nodeByIndex = getNodeByIndex(index);
return nodeByIndex.info;
} else {
throw new Exception("get ele " + index + " out of index");
}
} /**
* 根据index获取node的方法
*
* @param index
* @return
*/
private Node<T> getNodeByIndex(Integer index) {
Node<T> temp = firstNode;// 取firstnode
if (index != 0) {// 查看当前index,若index!=0,则递归直到index
for (int i = 0; i < index; i++) {
temp = temp.next;
}
}
this.index = index;// 调整当前index
return temp;// 返回节点
} /**
* 向链表末尾默认添加一个元素的方法
*
* @param t
*/
public void add(T t) {
if (size == 0) {// 首次创建
Node<T> node = new Node<T>();
firstNode = node;
lastNode = node;
node.info = t;
size++;
} else {
lastNode.next = new Node<T>();
lastNode.next.info = t;
lastNode.next.prve = lastNode;
lastNode = lastNode.next;
size++;
}
} /**
* 在首添加元素
*
* @param t
*/
public void addFirst(T t) {
if (size == 0) {
add(t);
} else {
Node<T> node = new Node<T>();
node.info = t;
node.next = firstNode;
node.prve = null;
firstNode.next = node;
size++;
}
} /**
* 在尾部添加元素
*
* @param t
*/
public void addLast(T t) {
add(t);
} /**
* Node节点 链表内部数据结构和指针
*
* @author Liuyuhang
* @param <T>
*/
private class Node<T> {
T info;// 储存info
Node<T> next;// 下一个节点指针
Node<T> prve;// 上一个节点指针 @Override
public String toString() {// 同时打印next和prev会导致无限引用,堆栈溢出
return "Node [info=" + info + ", next=" + next + "]";
} } @Override
public String toString() {// 打印first节点会因为next引出整个链表的所有内容
return "SimpleLinkedList [node=" + firstNode + "]";
} }
测试可用,自己拿去玩吧。
以上!
算法是什么(二)手写个链表(java)的更多相关文章
- 利用神经网络算法的C#手写数字识别(二)
利用神经网络算法的C#手写数字识别(二) 本篇主要内容: 让项目编译通过,并能打开图片进行识别. 1. 从上一篇<利用神经网络算法的C#手写数字识别>中的源码地址下载源码与资源, ...
- 利用神经网络算法的C#手写数字识别(一)
利用神经网络算法的C#手写数字识别 转发来自云加社区,用于学习机器学习与神经网络 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwri ...
- 利用神经网络算法的C#手写数字识别
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwritten_character_recognition.zip 下载源码 - 70. ...
- k最邻近算法——使用kNN进行手写识别
上篇文章中提到了使用pillow对手写文字进行预处理,本文介绍如何使用kNN算法对文字进行识别. 基本概念 k最邻近算法(k-Nearest Neighbor, KNN),是机器学习分类算法中最简单的 ...
- 在opencv3中实现机器学习算法之:利用最近邻算法(knn)实现手写数字分类
手写数字digits分类,这可是深度学习算法的入门练习.而且还有专门的手写数字MINIST库.opencv提供了一张手写数字图片给我们,先来看看 这是一张密密麻麻的手写数字图:图片大小为1000*20 ...
- 二. 手写SpringMVC框架
1.1 新建DispatcherServlet 1.2 在src目录下,新建applicationContext.xml <?xml version="1.0" encodi ...
- 手写数字识别的k-近邻算法实现
(本文为原创,请勿在未经允许的情况下转载) 前言 手写字符识别是机器学习的入门问题,k-近邻算法(kNN算法)是机器学习的入门算法.本文将介绍k-近邻算法的原理.手写字符识别问题分析.手写字符识别的k ...
- 机器学习框架ML.NET学习笔记【5】多元分类之手写数字识别(续)
一.概述 上一篇文章我们利用ML.NET的多元分类算法实现了一个手写数字识别的例子,这个例子存在一个问题,就是输入的数据是预处理过的,很不直观,这次我们要直接通过图片来进行学习和判断.思路很简单,就是 ...
- java - day015 - 手写双向链表, 异常(续), IO(输入输出)
类的内存分配 加载到方法区 对象在堆内存 局部变量在栈内存 判断真实类型,在方法区加载的类 对象.getClass(); 类名.class; 手写双向链表 package day1501_手写双向链表 ...
随机推荐
- RegExp.prototype.exec()使用技巧
RegExp.prototype.exec() exec() 方法在一个指定字符串中执行一个搜索匹配.返回一个结果数组或 null. 如果你只是为了判断是否匹配(true或 false),可以使用 R ...
- JS里的居民们7-对象和数组转换
编码 学习通用的数据用不同的数据结构进行存储,以及相互的转换 对象转为数组: var scoreObject = { "Tony": { "Math": 95, ...
- centos yum安装常用命令
安装killall命令 yum install -y psmisc 安装sz(下载)和rz(上传)命令 yum install -y lrzsz 安装 ifconfig 命令 yum install ...
- 软件项目技术点(6)——结合鼠标操作绘制动态canvas画布
AxeSlide软件项目梳理 canvas绘图系列知识点整理 我们创建一个类封装了所有鼠标需要处理的事件. export class MouseEventInfo { el: HTMLElemen ...
- 基础架构之Mongo
项目需求中,有些需求的数据是不必长时间持久化或一些非结构化设计,这时可以考虑用Mongo作为存储,具体介绍介绍详见官方 https://www.mongodb.com,这篇文章主要介绍安装及启用身份认 ...
- 多线程 读写锁SRWLock
在<秒杀多线程第十一篇读者写者问题>文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法——读写锁SRWLock来解决这一 ...
- SpringMVC学习(六)——@InitBinder注解
有些类型的数据是无法自动转换的,比如请求参数中包含时间类型的数据,无法自动映射到Controller里的Date参数.需要使用@initBinder注解为binder提供一个数据的转换器,这个转换器可 ...
- JDBC mysql驱动
在用JDBC连接MySQL数据库时,需要使用驱动 mysql-connector-java-5.1.41-bin.jar 在本地java应用程序中,只需将jar包导入到项目library中即可, 而在 ...
- Dubbo框架介绍与安装 Dubbo 注册中心(Zookeeper-3.4.6)
背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. • 单一应用架构 • 当网站流量很小时, ...
- redis基本操作命令
前面我们看了redis的简介安装和数据类型,接下来介绍一下redis基本数据类型的操作命令: 一:redis键值对的管理和操作 DEL key 该命令用于在 key 存在时删除 key. DUMP k ...