java 线性表定义_Java数据结构之线性表

这篇博客主要介绍了Java中的线性结构,包括顺序结构和链式结构的线性表。顺序结构的线性表如ArrayList和Vector,通过数组实现;链式结构则有单链表和双向链表,如LinkedList。文中提供了自定义实现的顺序线性表、单链表和双向链表的代码示例,详细讲解了它们的插入、删除、查找等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~

java中的数据结构模型可以分为一下几部分:

1.线性结构

2.树形结构

3.图形或者网状结构

接下来的几章,我们将会分别讲解这几种数据结构,主要也是通过Java代码的方式来讲解相应的数据结构。

今天要讲解的是:Java线性结构

Java数据结构之线性结构

说到线性结构的话,我们可以根据其实现方式分为三类:

1)顺序结构的线性表

2)链式结构的线性表

3)栈和队列的线性表

1、顺序结构的线性表

所谓顺序存储,指的是两个元素在物理上的存储地址和逻辑上的存储地址是一致的,逻辑上相邻的两个元素,它们在物理中存储的地址

也是相邻的。对于jdk中典型的应用就是List接口的实现类ArrayList和Vector(它们两个的区别在于是否是线程同步的)。

如果感兴趣可以查看它们的源代码,下面我们以数组为例,来模仿一下它们...

package com.yonyou.test;

import java.util.Arrays;

/**

* 测试类

* @author 小浩

* @创建日期 2015-3-20

*/

public class Test

{

public static void main(String[] args) {

SequenceList list=new SequenceList();

System.out.println("线性表的初始化长度为:"+list.length());

list.add("Hello");

list.add("World");

list.add("天下太平");

System.out.println("当前list中的元素为:"+list);

list.remove();

System.out.println("当前list中的元素为:"+list);

System.out.println("当前元素线性表是否为空:"+list.empty());

}

}

/**

* 创建一个线性链表

* 注意这个类是线程不安全的,在多线程下不要使用

* @author 小浩

* @创建日期 2015-3-20

* @param

*/

class SequenceList

{

//数组的默认初始化长度

private int DEFAULT_SIZE = 10;

// 保存数组的长度。

private int capacity;

// 定义一个数组用于保存顺序线性表的元素

private Object[] elementData;

// 保存顺序表中元素的当前个数

private int size = 0;

// 以默认数组长度创建空顺序线性表

public SequenceList()

{

capacity = DEFAULT_SIZE;

elementData = new Object[capacity];

}

// 以一个初始化元素来创建顺序线性表

public SequenceList(T element)

{

this();

elementData[0] = element;

size++;

}

/**

* 以指定长度的数组来创建顺序线性表

* @param element 指定顺序线性表中第一个元素

* @param initSize 指定顺序线性表底层数组的长度

*/

public SequenceList(T element , int initSize)

{

capacity = 1;

// 把capacity设为大于initSize的最小的2的n次方

while (capacity < initSize)

{

capacity <<= 1;

}

elementData = new Object[capacity];

elementData[0] = element;

size++;

}

/**

* 获取顺序线性表的大小

* @return

*/

public int length()

{

return size;

}

/**

* 获取顺序线性表中索引为i处的元素

* @param i

* @return

*/

@SuppressWarnings("unchecked")

public T get(int i)

{

if (i < 0 || i > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

return (T)elementData[i];

}

/**

* 查找顺序线性表中指定元素的索引

* @param element

* @return

*/

public int locate(T element)

{

for (int i = 0 ; i < size ; i++)

{

if (elementData[i].equals(element))

{

return i;

}

}

return -1;

}

/**

* 向顺序线性表的指定位置插入一个元素。

* @param element

* @param index

*/

private void insert(T element , int index)

{

if (index < 0 || index > size)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

ensureCapacity(size + 1);

// 将index处以后所有元素向后移动一格。

System.arraycopy(elementData , index , elementData

, index + 1 , size - index);

elementData[index] = element;

size++;

}

/**

* 在线性顺序表的开始处添加一个元素。

* @param element

*/

public void add(T element)

{

insert(element , size);

}

/**

* 扩充底层数组长度,很麻烦,而且性能很差

* @param minCapacity

*/

private void ensureCapacity(int minCapacity)

{

// 如果数组的原有长度小于目前所需的长度

if (minCapacity > capacity)

{

// 不断地将capacity * 2,直到capacity大于minCapacity为止

while (capacity < minCapacity)

{

capacity <<= 1;

}

elementData = Arrays.copyOf(elementData , capacity);

}

}

/**

* 删除顺序线性表中指定索引处的元素

* @param index

* @return

*/

@SuppressWarnings("unchecked")

private T delete(int index)

{

if (index < 0 || index > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

T oldValue = (T)elementData[index];

int numMoved = size - index - 1;

if (numMoved > 0)

{

System.arraycopy(elementData , index+1

, elementData, index , numMoved);

}

// 清空最后一个元素

elementData[--size] = null;

return oldValue;

}

/**

* 删除顺序线性表中最后一个元素

* @return

*/

public T remove()

{

return delete(size - 1);

}

/**

* 判断顺序线性表是否为空表

* @return

*/

public boolean empty()

{

return size == 0;

}

/**

* 清空线性表

*/

public void clear()

{

// 将底层数组所有元素赋为null

Arrays.fill(elementData , null);

size = 0;

}

/**

* 重写toString

*/

public String toString()

{

if (size == 0)

{

return "[]";

}

else

{

StringBuilder sb = new StringBuilder("[");

for (int i = 0 ; i < size ; i++ )

{

sb.append(elementData[i].toString() + ", ");

}

int len = sb.length();

return sb.delete(len - 2 , len).append("]").toString();

}

}

}

具体的讲解这里就不说了,还是那句话,如果感兴趣可以查看相关源代码。

2、链式结构的线性表

链式存储结构的线性表是相对于顺序结构的线性表而言的。对于链式存储的线性表,其中各个元素的物理地址和逻辑地址是不确定的。

也就是说逻辑上相邻的两个元素,他们在物理上不一定是相邻的。

在Java的jdk中典型的应用就是List接口下面的LinkedList(线程不安全,多线程下不要使用),如果感兴趣的可以查看相关源代码。

这里仅仅简单的模仿一下。

首先模仿的是单链表:

package com.yonyou.test;

/**

* 测试类

* @author 小浩

* @创建日期 2015-3-20

*/

public class Test

{

public static void main(String[] args) {

LinkList list=new LinkList();

System.out.println("线性表的初始化长度为:"+list.length());

list.add("Hello");

list.add("World");

list.add("天下太平");

System.out.println("当前list中的元素为:"+list);

list.remove();

System.out.println("当前list中的元素为:"+list);

System.out.println("当前元素线性表是否为空:"+list.empty());

}

}

/**

* 创建一个链式线性表

* 注意这个类是线程不安全的,在多线程下不要使用

* @author 小浩

* @创建日期 2015-3-20

* @param

*/

class LinkList

{

/**定义一个内部类Node,Node实例代表链表的节点。

*

* @author 小浩

* @创建日期 2015-3-20

*/

private class Node

{

// 保存节点的数据

private T data;

// 指向下个节点的引用

private Node next;

// 无参数的构造器

@SuppressWarnings("unused")

public Node()

{

}

// 初始化全部属性的构造器

public Node(T data , Node next)

{

this.data = data;

this.next = next;

}

}

// 保存该链表的头节点

private Node header;

// 保存该链表的尾节点

private Node tail;

//保存该链表中已包含的节点数

private int size;

/**

* 创建空链表

*/

public LinkList()

{

// 空链表,header和tail都是null

header = null;

tail = null;

}

/**

* 以指定数据元素来创建链表,该链表只有一个元素

* @param element

*/

public LinkList(T element)

{

header = new Node(element , null);

// 只有一个节点,header、tail都指向该节点

tail = header;

size++;

}

/**

* 返回链表的长度

* @return

*/

public int length()

{

return size;

}

/**

* 获取链式线性表中索引为index处的元素

* @param index

* @return

*/

public T get(int index)

{

return getNodeByIndex(index).data;

}

// 根据索引index获取指定位置的节点

private Node getNodeByIndex(int index)

{

if (index < 0 || index > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

// 从header节点开始

Node current = header;

for (int i = 0 ; i < size && current != null

; i++ , current = current.next)

{

if (i == index)

{

return current;

}

}

return null;

}

/**

* 查找链式线性表中指定元素的索引

* @param element

* @return

*/

public int locate(T element)

{

// 从头节点开始搜索

Node current = header;

for (int i = 0 ; i < size && current != null

; i++ , current = current.next)

{

if (current.data.equals(element))

{

return i;

}

}

return -1;

}

/** 向线性链式表的指定位置插入一个元素。

*

* @param element

* @param index

*/

public void insert(T element , int index)

{

if (index < 0 || index > size)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

// 如果还是空链表

if (header == null)

{

add(element);

}

else

{

// 当index为0时,也就是在链表头处插入

if (index == 0)

{

addAtHeader(element);

}

else

{

// 获取插入点的前一个节点

Node prev = getNodeByIndex(index - 1);

// 让prev的next指向新节点,

// 让新节点的next引用指向原来prev的下一个节点。

prev.next = new Node(element , prev.next);

size++;

}

}

}

/**

* 采用尾插法为链表添加新节点。

* @param element

*/

public void add(T element)

{

// 如果该链表还是空链表

if (header == null)

{

header = new Node(element , null);

// 只有一个节点,header、tail都指向该节点

tail = header;

}

else

{

// 创建新节点

Node newNode = new Node(element , null);

// 让尾节点的next指向新增的节点

tail.next = newNode;

// 以新节点作为新的尾节点

tail = newNode;

}

size++;

}

/**采用头插法为链表添加新节点。

*

* @param element

*/

private void addAtHeader(T element)

{

// 创建新节点,让新节点的next指向原来的header

// 并以新节点作为新的header

header = new Node(element , header);

// 如果插入之前是空链表

if (tail == null)

{

tail = header;

}

size++;

}

/**

* 删除链式线性表中指定索引处的元素

* @param index

* @return

*/

public T delete(int index)

{

if (index < 0 || index > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

Node del = null;

// 如果被删除的是header节点

if (index == 0)

{

del = header;

header = header.next;

}

else

{

// 获取删除点的前一个节点

Node prev = getNodeByIndex(index - 1);

// 获取将要被删除的节点

del = prev.next;

// 让被删除节点的next指向被删除节点的下一个节点。

prev.next = del.next;

// 将被删除节点的next引用赋为null.

del.next = null;

}

size--;

return del.data;

}

/**

* 删除链式线性表中最后一个元素

* @return

*/

public T remove()

{

return delete(size - 1);

}

/**

* 判断链式线性表是否为空表

* @return

*/

public boolean empty()

{

return size == 0;

}

/**

* 清空线性表

*/

public void clear()

{

// header、tail赋为null

header = null;

tail = null;

size = 0;

}

/**

* 重写toString方法

*/

public String toString()

{

// 链表为空链表时

if (empty())

{

return "[]";

}

else

{

StringBuilder sb = new StringBuilder("[");

for (Node current = header ; current != null

; current = current.next )

{

sb.append(current.data.toString() + ", ");

}

int len = sb.length();

return sb.delete(len - 2 , len).append("]").toString();

}

}

}

其次模仿的是循环链表(即将链表的尾节点的next指向头节点)

具体代码就不写了,感兴趣的可以自己写写看,也不难...主要就是注意设置和维护下面这条语句

tail.next=header;

最后要讲解的双向链表的内容(相对于单链表而言,每个节点不仅有一个next,而且有一个指向前一个元素的pre)

具体的内容请参考下面的代码:

package com.yonyou.test;

/**

* 测试类

* @author 小浩

* @创建日期 2015-3-20

*/

public class Test

{

public static void main(String[] args) {

DoubleLinkList list=new DoubleLinkList();

System.out.println("线性表的初始化长度为:"+list.length());

list.add("Hello");

list.add("World");

list.add("天下太平");

System.out.println("当前list中的元素为:"+list);

list.remove();

System.out.println("当前list中的元素为:"+list);

System.out.println("当前元素线性表是否为空:"+list.empty());

}

}

/**

* 创建一个双向链式线性表

* 注意这个类是线程不安全的,在多线程下不要使用

* @author 小浩

* @创建日期 2015-3-20

* @param

*/

class DoubleLinkList

{

// 定义一个内部类Node,Node实例代表链表的节点。

private class Node

{

// 保存节点的数据

private T data;

// 指向上个节点的引用

private Node prev;

// 指向下个节点的引用

private Node next;

// 无参数的构造器

public Node()

{

}

// 初始化全部属性的构造器

public Node(T data , Node prev , Node next)

{

this.data = data;

this.prev = prev;

this.next = next;

}

}

// 保存该链表的头节点

private Node header;

// 保存该链表的尾节点

private Node tail;

// 保存该链表中已包含的节点数

private int size;

/**

* 创建空链表

*/

public DoubleLinkList()

{

// 空链表,header和tail都是null

header = null;

tail = null;

}

/**

* 以指定数据元素来创建链表,该链表只有一个元素

* @param element

*/

public DoubleLinkList(T element)

{

header = new Node(element , null , null);

// 只有一个节点,header、tail都指向该节点

tail = header;

size++;

}

/**

* 返回链表的长度

* @return

*/

public int length()

{

return size;

}

/**

* 获取链式线性表中索引为index处的元素

* @param index

* @return

*/

public T get(int index)

{

return getNodeByIndex(index).data;

}

/**根据索引index获取指定位置的节点

*

* @param index

* @return

*/

private Node getNodeByIndex(int index)

{

if (index < 0 || index > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

if (index <= size / 2)

{

// 从header节点开始

Node current = header;

for (int i = 0 ; i <= size / 2 && current != null

; i++ , current = current.next)

{

if (i == index)

{

return current;

}

}

}

else

{

// 从tail节点开始搜索

Node current = tail;

for (int i = size - 1 ; i > size / 2 && current != null

; i++ , current = current.prev)

{

if (i == index)

{

return current;

}

}

}

return null;

}

/**

* 查找链式线性表中指定元素的索引

* @param element

* @return

*/

public int locate(T element)

{

// 从头节点开始搜索

Node current = header;

for (int i = 0 ; i < size && current != null

; i++ , current = current.next)

{

if (current.data.equals(element))

{

return i;

}

}

return -1;

}

/**

* 向线性链式表的指定位置插入一个元素。

* @param element

* @param index

*/

public void insert(T element , int index)

{

if (index < 0 || index > size)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

// 如果还是空链表

if (header == null)

{

add(element);

}

else

{

// 当index为0时,也就是在链表头处插入

if (index == 0)

{

addAtHeader(element);

}

else

{

// 获取插入点的前一个节点

Node prev = getNodeByIndex(index - 1);

// 获取插入点的节点

Node next = prev.next;

// 让新节点的next引用指向next节点,prev引用指向prev节点

Node newNode = new Node(element , prev , next);

// 让prev的next指向新节点。

prev.next = newNode;

// 让prev的下一个节点的prev指向新节点

next.prev = newNode;

size++;

}

}

}

/**

* 采用尾插法为链表添加新节点。

* @param element

*/

public void add(T element)

{

// 如果该链表还是空链表

if (header == null)

{

header = new Node(element , null , null);

// 只有一个节点,header、tail都指向该节点

tail = header;

}

else

{

// 创建新节点,新节点的pre引用指向原tail节点

Node newNode = new Node(element , tail , null);

// 让尾节点的next指向新增的节点

tail.next = newNode;

// 以新节点作为新的尾节点

tail = newNode;

}

size++;

}

/**

* 采用头插法为链表添加新节点。

* @param element

*/

public void addAtHeader(T element)

{

// 创建新节点,让新节点的next指向原来的header

// 并以新节点作为新的header

header = new Node(element , null , header);

// 如果插入之前是空链表

if (tail == null)

{

tail = header;

}

size++;

}

/**

* 删除链式线性表中指定索引处的元素

* @param index

* @return

*/

public T delete(int index)

{

if (index < 0 || index > size - 1)

{

throw new IndexOutOfBoundsException("线性表索引越界");

}

Node del = null;

// 如果被删除的是header节点

if (index == 0)

{

del = header;

header = header.next;

// 释放新的header节点的prev引用

header.prev = null;

}

else

{

// 获取删除点的前一个节点

Node prev = getNodeByIndex(index - 1);

// 获取将要被删除的节点

del = prev.next;

// 让被删除节点的next指向被删除节点的下一个节点。

prev.next = del.next;

// 让被删除节点的下一个节点的prev指向prev节点。

if (del.next != null)

{

del.next.prev = prev;

}

// 将被删除节点的prev、next引用赋为null.

del.prev = null;

del.next = null;

}

size--;

return del.data;

}

/**

* 删除链式线性表中最后一个元素

* @return

*/

public T remove()

{

return delete(size - 1);

}

/**

* 判断链式线性表是否为空链表

* @return

*/

public boolean empty()

{

return size == 0;

}

/**

* 清空线性表

*/

public void clear()

{

// 将底层数组所有元素赋为null

header = null;

tail = null;

size = 0;

}

/**

* 重写toString方法

*/

public String toString()

{

// 链表为空链表时

if (empty())

{

return "[]";

}

else

{

StringBuilder sb = new StringBuilder("[");

for (Node current = header ; current != null

; current = current.next )

{

sb.append(current.data.toString() + ", ");

}

int len = sb.length();

return sb.delete(len - 2 , len).append("]").toString();

}

}

public String reverseToString()

{

// 链表为空链表时

if (empty())

{

return "[]";

}

else

{

StringBuilder sb = new StringBuilder("[");

for (Node current = tail ; current != null

; current = current.prev )

{

sb.append(current.data.toString() + ", ");

}

int len = sb.length();

return sb.delete(len - 2 , len).append("]").toString();

}

}

}

由于篇幅的限制,对于线性结构中的栈和队列的讲解,请看下一篇博客~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值