算法是什么(二)手写个链表(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)的更多相关文章

  1. 利用神经网络算法的C#手写数字识别(二)

    利用神经网络算法的C#手写数字识别(二)   本篇主要内容: 让项目编译通过,并能打开图片进行识别.   1. 从上一篇<利用神经网络算法的C#手写数字识别>中的源码地址下载源码与资源, ...

  2. 利用神经网络算法的C#手写数字识别(一)

    利用神经网络算法的C#手写数字识别 转发来自云加社区,用于学习机器学习与神经网络 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwri ...

  3. 利用神经网络算法的C#手写数字识别

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwritten_character_recognition.zip 下载源码 - 70. ...

  4. k最邻近算法——使用kNN进行手写识别

    上篇文章中提到了使用pillow对手写文字进行预处理,本文介绍如何使用kNN算法对文字进行识别. 基本概念 k最邻近算法(k-Nearest Neighbor, KNN),是机器学习分类算法中最简单的 ...

  5. 在opencv3中实现机器学习算法之:利用最近邻算法(knn)实现手写数字分类

    手写数字digits分类,这可是深度学习算法的入门练习.而且还有专门的手写数字MINIST库.opencv提供了一张手写数字图片给我们,先来看看 这是一张密密麻麻的手写数字图:图片大小为1000*20 ...

  6. 二. 手写SpringMVC框架

    1.1 新建DispatcherServlet 1.2 在src目录下,新建applicationContext.xml <?xml version="1.0" encodi ...

  7. 手写数字识别的k-近邻算法实现

    (本文为原创,请勿在未经允许的情况下转载) 前言 手写字符识别是机器学习的入门问题,k-近邻算法(kNN算法)是机器学习的入门算法.本文将介绍k-近邻算法的原理.手写字符识别问题分析.手写字符识别的k ...

  8. 机器学习框架ML.NET学习笔记【5】多元分类之手写数字识别(续)

    一.概述 上一篇文章我们利用ML.NET的多元分类算法实现了一个手写数字识别的例子,这个例子存在一个问题,就是输入的数据是预处理过的,很不直观,这次我们要直接通过图片来进行学习和判断.思路很简单,就是 ...

  9. java - day015 - 手写双向链表, 异常(续), IO(输入输出)

    类的内存分配 加载到方法区 对象在堆内存 局部变量在栈内存 判断真实类型,在方法区加载的类 对象.getClass(); 类名.class; 手写双向链表 package day1501_手写双向链表 ...

随机推荐

  1. java设计模式之抽象工厂模式学习

    工厂模式有个问题就是,类的创建依赖工厂.要想增加一个工厂类,就要修改原来的代码,这违背了闭包原则.所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的 ...

  2. css3之背景定位

    属性: background-position: left top || left bottom || right top || right bottom || center center || 像素 ...

  3. JavaEE中表现层、持久层、业务层的职责分析(转载)

    表现层.持久层.业务层 注:本文转载于:http://www.blogjava.net/jiabao/archive/2007/04/08/109189.html 为了实现web层(struts)和持 ...

  4. ASICS各跑鞋分类及选购方法

    从跑吧转来的,老帖子后面的鞋子可能不能与时俱进 不过前面的方法不错. 1简介: ASICS鞋子鞋底如果有AHAR或AHAR+的为超耐磨标志,而且超耐度一般都是黑色,用指甲刮鞋底时如刮车轮底胶.ASIC ...

  5. 01_Zookeeper简述

    [Zookeeper应用场景] zookeeper作为一个开源的分布式应用协调系统,已经用到了许多分布式项目中,用来完成统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等工作. [Zook ...

  6. Android 集成百度统计

    在这里简单的介绍下怎么统计自己研发的APP 的用户活跃度,和使用量,以此来展示自己APP的用户使用量! 我们的APP都需要注入数据分析,以供我们实时的了解APP的下载和使用量提供了依据! 不过我还是更 ...

  7. ES6入门——let和const命令

    let和const命令 1.let命令 用法:类似于var,用来声明一个变量,区别是所声明的变量只在let命令所在的代码块内有效. let命令很适合用在for循环的计数器中,因为let声明的变量仅在作 ...

  8. “云中论道”之——使用开源技术和Azure公有云服务快速搭建云端IoT解决方案(上)

    “云中论道”技术课堂第一课开讲啦!微软各路技术咖们齐聚一堂,为大家带来干货不断!作为“云中论道“课堂的开课之作,我们首先邀请到了微软Azure专家级的架构师:槐长清,他为我们带来了关于“使用开源技术和 ...

  9. SQL Server ->> SQL Server 2016新特性之 --- Query Store

    前言 SQL Server 2016引入新的查询语句性能监控.调试和优化工具/功能 -- Query Store.以前我们发现一条查询语句性能突然下降,我们要去找出问题的所在往往需要通过调用一些DMV ...

  10. SQL点点滴滴_UPDATE小计

    1.更新tb_card中c_customer字段的值等于tb_customer表中c_no的值 update tb_card set c_customer=ct.c_no from tb_custom ...