数据的存储一般分线性存储结构和链式存储结构两种。前者是一种顺序的存储方式,在内存中用一块连续的内存空间存储数据,即逻辑上相连的物理位置相邻,比较常见的就是数组;后者是一种链式存储方式,不保证顺序性,逻辑上相邻的元素之间用指针所指定,它不是用一块连续的内存存储,逻辑上相连的物理位置不一定相邻。本篇主要介绍链式存储结构基于链表的实现,使用的语言为Java。

链表是一种递归的数据结构,它要么为空(null),要么指向是指向一个结点(node)的引用,该节点含有一个泛型元素(该泛型元素可以是任意数据类型),和一个指向另一个链表的引用。链表有很多种,下面主要介绍单向链表、双端链表、有序链表、双向链表。

单向链表:单向链表是最简单、最基础的链表,它的一个结点(node)分两部分,第一部分存储结点的数据信息(data),第二部分存储指向下一结点的地址(next)信息。最后一个结点(链尾)指向一个空地址(null)。单向链表一般只在链表表头(链头)结点的位置插入元素,这样每次新加入的元素都会在链头位置,而最先加入的元素会在链尾位置。删除操作时,如果在链头位置删除,只需要把头结点指向其下一个结点即可;如果是在中间位置删除,只需要将其前一个结点指向其下一个结点即可。单向链表示意图如下图所示:

单向链表的Java代码实现:

importjava.util.Stack;public classLinkedListOnePoint {private Node head; //头结点

private int size; //链表长度,即链表中结点数量

publicLinkedListOnePoint(){

head= null;

size= 0;

}//私有内部类,代表链表每个结点

private classNode{private Object data; //链表结点的值

private Node next; //指向的下一个结点

publicNode(Object data){this.data =data;

}

}//判断链表是否为空

public booleanisEmpty(){return size==0?true:false;

}//返回链表长度

public intsize(){returnsize;

}//查看链表头结点,不移除

publicObject headNode(){if(size == 0) return null;returnhead.data;

}//在链表表头插入一个结点(入栈)

public voidinsertInHead(Object obj){

Node newNode= newNode(obj);if(size == 0){

head=newNode;

}else{

newNode.next=head;

head=newNode;

}

size++;

}//删除链表表头结点(出栈)

publicObject deleteHeadNode(){if(size == 0) return null;

Object obj=head.data;if(head.next == null){

head= null; //只有一个结点

}else{

head=head.next;

}

size--;returnobj;

}//链表查找:判断链表中是否包含某个元素

public booleancontainObject(Object obj){if(size == 0) return false;

Node n=head;while(n != null){if(n.data == obj) return true;else n =n.next;

}return false;

}//删除链表中的指定结点(如果包含多个指定结点,只会删除第一个)

public booleandeleteNode(Object obj){if(size == 0){

System.out.println("链表为空!");return false;

}//先在链表中查询是否包含该结点,找到之后获取该结点和其前一个结点

Node previous = null; //前一个结点

Node current = head; //当前结点

while(current.data !=obj){if(current.next == null){

System.out.println("没有找到该结点!");return false;

}

previous=current;

current=current.next;

}if(current ==head){this.deleteHeadNode();

}else{

previous.next=current.next;

size--;

}return true;

}//正向打印链表

public voiddisplay(){if(size == 0) System.out.println("链表为空!");

Node n=head;while(n != null){

System.out.print("

n=n.next;

}

System.out.println();

}//反向打印链表(用栈)

public voidprintListFromTailToHead(Node node){if(node == null) System.out.println("链表为空!");

Stack sta = new Stack();while(node != null){

sta.push((Integer) node.data);//先将链表压入栈中

node =node.next;

}while(!sta.empty()){

System.out.print(sta.pop()+"

}

System.out.println();

}//反向打印链表(递归)

public voidprintListFromTailToHeadDiGui(Node node){if(node == null){

System.out.println("链表为空!");

}else{if(node.next != null){

printListFromTailToHeadDiGui(node.next);

}

System.out.print(node.data+"

}

}public static voidmain(String[] args) {

LinkedListOnePoint list= newLinkedListOnePoint();

System.out.println(list.isEmpty());//true

System.out.println(list.size()); //0

list.display(); //链表为空!

list.printListFromTailToHead(list.head); //链表为空!

list.insertInHead(0);

list.insertInHead(1);

list.insertInHead(2);

list.insertInHead(3);

list.display();//

list.printListFromTailToHead(list.head); //0

list.printListFromTailToHeadDiGui(list.head); //0

System.out.println(list.isEmpty()); //false

System.out.println(list.size()); //4

System.out.println(list.containObject(1)); //true

}

}

View Code

我们知道,栈是一种“后进先出”的数据结构,对栈的插入和删除操作都是在栈头位置进行的,这与在单向链表的表头插入和删除元素的原理类似,因此可以用单向链表实现栈。

单向链表实现栈的Java代码:

/** 单链表实现栈*/

public classLinkedListToStack {privateLinkedListOnePoint linkedlist;publicLinkedListToStack(){

linkedlist= newLinkedListOnePoint();

}//栈大小

public intsize(){returnlinkedlist.size();

}//是否为空栈

public booleanisEmpty(){returnlinkedlist.isEmpty();

}//入栈

public voidpush(Object obj){

linkedlist.insertInHead(obj);

}//出栈

publicObject pop(){if(this.isEmpty()) return null;returnlinkedlist.deleteHeadNode();

}//查看栈顶元素

publicObject peek(){if(this.isEmpty()) return null;returnlinkedlist.headNode();

}//打印栈中元素

public voiddisplay(){

linkedlist.display();

}public static voidmain(String[] args) {

LinkedListToStack stack= newLinkedListToStack();

stack.push(0);

stack.push(1);

stack.push(2);

stack.push(3);

stack.display();//

System.out.println(stack.peek()); //3

System.out.println(stack.pop()); //3

System.out.println(stack.pop()); //2

System.out.println(stack.pop()); //1

System.out.println(stack.pop()); //0

System.out.println(stack.pop()); //null

}

}

View Code

双端链表:双端链表和单向链表大体上是一样的,不同的是,单向链表在表尾部分插入元素时,需要从头结点一直遍历到尾结点才能进行插入操作,这样难免有些繁琐。因此如果加入一个对尾结点的引用,这样就可以很方便地在尾结点进行插入操作,这就是双端链表。除了有一个头结点(head),还有一个尾结点(tail)。注意它和后面双向链表的区别!

双端链表的Java代码实现:

importjava.util.Stack;/** 双端链表,比单链表多了个尾结点*/

public classLinkedListTwoPoint {private Node head; //头结点

private Node tail; //尾结点

private int size; //链表长度,即链表中结点数量

publicLinkedListTwoPoint(){

head= null; //头结点

tail = null; //尾结点

size = 0; //链表长度,即链表中结点数量

}//私有内部类,代表链表每个结点

private classNode{private Object data; //链表结点的值

private Node next; //指向的下一个结点

publicNode(Object data){this.data =data;

}

}//判断链表是否为空

public booleanisEmpty(){return size==0?true:false;

}//返回链表长度

public intsize(){returnsize;

}//查看链表头结点,不移除

publicObject headNode(){if(size == 0) return null;returnhead.data;

}//查看链表尾结点,不移除

publicObject tailNode(){if(size == 0) return null;returntail.data;

}//在链表表头插入一个结点

public voidinsertInHead(Object obj){

Node newNode= newNode(obj);if(size == 0){

head=newNode;

tail=newNode;

}else{

newNode.next=head;

head=newNode;

}

size++;

}//在链表表尾插入一个结点

public voidinsertInTail(Object obj){

Node newNode= newNode(obj);if(size == 0){

head=newNode;

tail=newNode;

}else{

tail.next=newNode;

tail=newNode;

}

size++;

}//删除链表表头结点

publicObject deleteHeadNode(){if(size == 0) return null;

Object obj=head.data;if(head.next == null){ //只有一个结点

head = null;

tail= null;

}else{

head=head.next;

}

size--;returnobj;

}//链表查找:判断链表中是否包含某个元素

public booleancontainObject(Object obj){if(size == 0) return false;

Node n=head;while(n != null){if(n.data == obj) return true;else n =n.next;

}return false;

}//删除链表中的指定结点(如果包含多个指定结点,只会删除第一个)

public booleandeleteNode(Object obj){if(size == 0){

System.out.println("链表为空!");return false;

}//先在链表中查询是否包含该结点,找到之后获取该结点和其前一个结点

Node previous = null; //前一个结点

Node current = head; //当前结点

while(current.data !=obj){if(current.next == null){

System.out.println("没有找到该结点!");return false;

}

previous=current;

current=current.next;

}if(current ==head){this.deleteHeadNode();

}else{

previous.next=current.next;

size--;

}return true;

}//正向打印链表

public voiddisplay(){if(size == 0) System.out.println("链表为空!");

Node n=head;while(n != null){

System.out.print(n.data+ "

n=n.next;

}

System.out.println();

}//反向打印链表(用栈)

public voidprintListFromTailToHead(Node node){if(node == null) System.out.println("链表为空!");

Stack sta = new Stack();while(node != null){

sta.push((Integer) node.data);//先将链表压入栈中

node =node.next;

}while(!sta.empty()){

System.out.print(sta.pop()+"

}

System.out.println();

}//反向打印链表(递归)

public voidprintListFromTailToHeadDiGui(Node node){if(node == null){

System.out.println("链表为空!");

}else{if(node.next != null){

printListFromTailToHeadDiGui(node.next);

}

System.out.print(node.data+"

}

}public static voidmain(String[] args) {

LinkedListTwoPoint list= newLinkedListTwoPoint();

System.out.println(list.isEmpty());//true

System.out.println(list.size()); //0

list.display(); //链表为空!

list.printListFromTailToHead(list.head); //链表为空!

list.insertInHead(0);

list.insertInHead(1);

list.insertInHead(2);

list.insertInHead(3);

list.display();//3

list.printListFromTailToHead(list.head); //0

list.printListFromTailToHeadDiGui(list.head); //0

System.out.println(list.isEmpty()); //false

System.out.println(list.size()); //4

System.out.println(list.containObject(1)); //true

}

}

View Code

我们知道,队列是一种“先进先出”的数据结构,队列的插入操作是在队尾进行的,而删除操作是在队头进行的,这与在双端链表的表尾插入和在表头删除操作是类似的,因此可以用双端链表实现队列。

双端链表实现队列的Java代码:

/** 双端链表实现队列*/

public classLinkedListToQueue {privateLinkedListTwoPoint linkedlist;publicLinkedListToQueue(){

linkedlist= newLinkedListTwoPoint();

}//队列大小

public intsize(){returnlinkedlist.size();

}//是否为空队列

public booleanisEmpty(){returnlinkedlist.isEmpty();

}//入列,在链表表尾插入节点

public voidadd(Object obj){

linkedlist.insertInTail(obj);

}//出列,在链表表头删除结点

publicObject poll(){if(this.isEmpty()) return null;returnlinkedlist.deleteHeadNode();

}//查看队列头元素

publicObject peekHead(){if(this.isEmpty()) return null;returnlinkedlist.headNode();

}//查看队列尾元素

publicObject peekTail(){if(this.isEmpty()) return null;returnlinkedlist.tailNode();

}//打印队列元素

public voiddisplay(){

linkedlist.display();

}public static voidmain(String[] args) {

LinkedListToQueue stack= newLinkedListToQueue();

stack.add(0);

stack.add(1);

stack.add(2);

stack.add(3);

stack.display();//0

System.out.println(stack.peekHead()); //0

System.out.println(stack.peekTail()); //3

System.out.println(stack.poll()); //0

System.out.println(stack.poll()); //1

System.out.println(stack.poll()); //2

System.out.println(stack.poll()); //3

System.out.println(stack.poll()); //null

}

}

View Code

有序链表:链表本身是一种无序的数据结构,元素的插入和删除不能保证顺序性,但是有没有有序的链表呢?答案是肯定的,我们在单向链表中插入元素时,只需要将插入的元素与头结点及其后面的结点比较,从而找到合适的位置插入即可。一般在大多数需要使用有序数组的场合也可以使用有序链表,有序链表在插入时因为不需要移动元素,因此插入速度比数组快很多,另外链表可以扩展到全部有效的使用内存,而数组只能局限于一个固定的大小中。

有序链表的Java代码实现:

/** 有序链表*/

public classLinkedListInOrder {private Node head; //头结点

private int size; //链表长度,即链表中结点数量

publicLinkedListInOrder(){

head= null;

size= 0;

}//私有内部类,代表链表每个结点

private classNode{private Integer data; //链表结点的值

private Node next; //指向的下一个结点

publicNode(Integer data){this.data =data;

}

}//判断链表是否为空

public booleanisEmpty(){return size==0?true:false;

}//返回链表长度

public intsize(){returnsize;

}//在链表中插入一个结点,保持链表有序性(头结点最小,尾结点最大)

public voidinsertNode(Integer obj){

Node newNode= newNode(obj);if(size == 0){ //空链表直接放入头结点

head =newNode;

}else{

Node previous= null; //插入位置前一个结点

Node current = head; //插入位置后一个结点(当前结点)

while(current.data

previous =current;

current=current.next;

}if(current.next == null){ //如果插入的结点大于链表中所有结点,则放到链尾

current.next =newNode;

}else{

previous.next= newNode; //在合适位置插入

newNode.next =current;

}

}

size++;

}//删除链表表头结点

publicObject deleteHeadNode(){if(size == 0) return null;

Object obj=head.data;if(head.next == null){

head= null; //只有一个结点

}else{

head=head.next;

}

size--;returnobj;

}//正向打印链表

public voiddisplay(){if(size == 0) System.out.println("链表为空!");

Node n=head;while(n != null){

System.out.print("

n=n.next;

}

System.out.println();

}public static voidmain(String[] args) {

LinkedListInOrder list= newLinkedListInOrder();

System.out.println(list.isEmpty());//true

System.out.println(list.size()); //0

list.display(); //链表为空!

list.insertNode(0);

list.insertNode(1);

list.insertNode(2);

list.insertNode(3);

list.insertNode(2);

list.insertNode(4);

list.display();//

System.out.println(list.isEmpty()); //false

System.out.println(list.size()); //6

System.out.println("*****************************");

}

}

View Code

双向链表:前面的几种链表只能从头结点遍历到尾结点这一个方向,每个结点都只能指向其下一个结点。而双向链表的每个结点既能指向下一个结点,又能指向前一个结点,双向链表既能从头结点向尾结点遍历,又能从尾结点向头结点遍历,既有一个头结点,又有一个尾结点。

双向链表的Java代码实现:

/** 双向链表

* 单向链表只可向一个方向遍历,一般查找一个结点的时候需要从第一个结点开始每次访问下一个结点,一直访问到需要的位置。

* 双向链表的每个结点都有指向的前一个结点和后一个节点,既有链表表头又有表尾,即可从链头向链尾遍历,又可从链尾向链头遍历。

* LinkedList中的私有静态内部类Node实际上就是一个双向链表,代表泛型,指明结点的数据类型

* private static class Node {

E item;

Node next;

Node prev;

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}*/

public classLinkedListTwoDirections {private Node head; //头结点

private Node tail; //尾结点

private int size; //链表长度,即链表中结点数量

publicLinkedListTwoDirections(){

head= null;

tail= null;

size= 0;

}//私有内部类,代表链表每个结点

private classNode{private Object data; //链表结点的值

private Node previous; //当前结点指向的前一个结点

private Node next; //当前结点指向的下一个结点

publicNode(Object data){this.data =data;

}

}//判断链表是否为空

public booleanisEmpty(){return size==0?true:false;

}//返回链表长度

public intsize(){returnsize;

}//查看链表头结点,不移除

publicObject headNode(){if(size == 0) return null;returnhead.data;

}//查看链表尾结点,不移除

publicObject tailNode(){if(size == 0) return null;returntail.data;

}//在链表表头插入一个结点

public voidinsertInHead(Object obj){

Node newNode= newNode(obj);if(size == 0){

head=newNode;

tail=newNode;

}else{

newNode.next=head;

head.previous=newNode;

head=newNode;

}

size++;

}//在链表表尾插入一个结点

public voidinsertInTail(Object obj){

Node newNode= newNode(obj);if(size == 0){

head=newNode;

tail=newNode;

}else{

newNode.previous=tail;

tail.next=newNode;

tail=newNode;

}

size++;

}//删除链表表头结点

publicObject deleteHeadNode(){if(size == 0) return null;

Object obj=head.data;if(head.next == null){ //只有一个结点

head = null;

tail= null;

}else{

head=head.next;

head.previous= null;

}

size--;returnobj;

}//删除链表表尾结点

publicObject deleteTailNode(){if(size == 0) return null;

Object obj=tail.data;if(tail.previous == null){ //只有一个结点

head = null;

tail= null;

}else{

tail=tail.previous;

tail.next= null;

}

size--;returnobj;

}//正向打印链表

public voiddisplay(){if(size == 0) System.out.println("链表为空!");

Node n=head;while(n != null){

System.out.print("

n=n.next;

}

System.out.println();

}public static voidmain(String[] args) {

LinkedListTwoDirections list= newLinkedListTwoDirections();

System.out.println(list.isEmpty());//true

System.out.println(list.size()); //0

list.display(); //链表为空!

list.insertInHead(0);

list.insertInHead(1);

list.insertInHead(2);

list.insertInHead(3);

list.display();//

System.out.println(list.deleteHeadNode()); //3

list.insertInTail(1);

list.insertInTail(2);

list.display();//

}

}

View Code

转载请注明出处 http://www.cnblogs.com/Y-oung/p/8886142.html

工作、学习、交流或有任何疑问,请联系邮箱:[email protected]  微信:yy1340128046

原文:https://www.cnblogs.com/Y-oung/p/8886142.html

数据结构之链表及其Java实现_数据结构之链表及其Java实现相关推荐

  1. 6-4 链表拼接 (20分)_数据结构之链表

    在面试过程中,数据结构和算法基本上算是研发类岗位必考的部分,而链表基本上又是数据结构中相对容易掌握.而且容易出题的部分,因此我们先整理一下链表部分的经典题目. (声明:以下所有程序都是用java编写) ...

  2. 数据结构和算法 java实现_数据结构与算法——常用数据结构及其Java实现

    前言 仿佛一下子,2017年就快过去一半了,研一马上就要成为过去式了,我打算抓住研一的尾巴,好好梳理一下数据结构与算法,毕竟这些基础知识是很重要的嘛.所以准备在这里搞一个系列的文章,以期透彻. 本系列 ...

  3. s数据结构替换子表java版_数据结构与算法分析Java语言描述(第3版) PDF和源码免费 下载...

    <数据结构与算法分析Java语言描述(第3版)>PDF和源码免费 下载 免积分下载 用户下载说明: 图书简介: 数据结构:Java语言描述(原书第3版)是国外数据结构与算法分析方面的经典教 ...

  4. Java数据结构第五版期末考试_数据结构(java)期末复习

    [单选题]多基因遗传病的发病风险与下列哪个因素无关 [单选题]一般手锯的往复长度不应小于锯条长度的( ). [判断题]机器上所有;零件都必须进行机械加工. [单选题]一般划线精度能达到( ). [填空 ...

  5. java书籍_非科班,自学java需要把软件工程的课程全部学习完吗?

    问题一:非科班是否能自学Java.问题二:自学Java是否需要把软件工程课程全部学完?问题三:如何自学Java? 解决问题一:非科班是否能自学Java.不知道你是否有这个担心疑虑,从事Java技术开发 ...

  6. 成为java高手_我如何想成为Java

    成为java高手 我喜欢Java. 我喜欢用Java编程. 但是在使用Python一段时间后,我希望对其进行一些更改. 它几乎纯粹是语法上的,因此可能有更好的JVM语言,但是我并不真正感兴趣,因为我仍 ...

  7. 如何java面试_短时间如何过java面试?

    这题我会!作为一个编程界老司机,我曾总结过一套Java常见的面试考点大全,不知道帮助过多少程序员拿下offer. 现在我把这套Java面试大全放出来,希望对大家有所帮助! 本文内容过长,建议大家先赞后 ...

  8. 拓扑排序之java实现_有向图和拓扑排序Java实现

    package practice; import java.util.ArrayDeque; import java.util.Iterator; import java.util.Stack; pu ...

  9. socket编程java笔试_面向应届实习生的Java笔试题

    2014-03-27 06:30:02 阅读( 187 ) JAVA基础测试题 班级                 姓名              分数 1.下面的程序输出 public class ...

最新文章

  1. 异常处理python要求输入的为英文_python(异常处理)
  2. HDU - 4461 The Power of Xiangqi
  3. oracle rac ora 12560,rac ORA-12541: TNS:no listener问题 非常急!!
  4. awb数据怎么计算_白平衡自己主动(AWB)算法---2,颜色计算
  5. linux删除grid数据文件,MongoDB进阶系列(13)——GridFS大文件的添加、获取、查看、删除...
  6. jzoj6275-[NOIP提高组模拟1]小L的数列【矩阵乘法,欧拉定理】
  7. swing 聊天气泡背景_Java Swing中的聊天气泡
  8. 升级 ubuntu_Ubuntu 19.04 已经到期!现有用户必须升级到 Ubuntu 19.10
  9. 【LeetCode】剑指 Offer 47. 礼物的最大价值
  10. CentOS6.5安装与配置Mysql数据库
  11. SSM运行异常 org.springframework.beans.factory.UnsatisfiedDependencyException
  12. Linux基础之bash脚本编程初级-变量与算术运算
  13. Java多线程 | 用多个线程模拟实现银行叫号系统
  14. Java中this的应用
  15. ISO9001 质量管理体系认证
  16. NVIDIA驱动安装
  17. Spring中的IOC和AOP是什么意思?
  18. python抓取酷我MV
  19. Docker Dockerfile 验证Docker内部使用jmap报错问题解决
  20. 静态、动态、伪静态的URL结构到底哪种更利于SEO

热门文章

  1. [CH5E02] A Little Shop of Flowers
  2. yum安装提示yum.pid被锁定解决办法
  3. python第三十一课--递归(3.递归的弊端)
  4. 周报_2013第02周(2013/01/06-2013/01/12)
  5. MAGENTO DEBUG
  6. 在Orderby子句中使用CASE 语句
  7. centos7.6内核升级
  8. Linux 日志定时轮询流程详解
  9. 【汇编语言】进制转换
  10. 【Oracle】跟踪文件目录(User_Dump_Dest)生成脚本(11g/9i).txt