应用程序后在那个的数据大致有四种基本的逻辑结构:
线性表的基本特征:
2.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素。
链表又可分为:
下面给出线性表双向链表的实现:java中LinkedList是线性表的链式实现,是一个双向链表。
线性表的两种实现比较
- 集合:数据元素之间只有"同属于一个集合"的关系
- 线性结构:数据元素之间存在一个对一个的关系
- 树形结构:数据元素之间存在一个对多个关系
- 图形结构或网状结构:数据元素之间存在多个对多个的关系
对于数据不同的逻辑结构,计算机在物理磁盘上通常有两种屋里存储结构
- 顺序存储结构
- 链式存储结构
线性表的基本特征:
- 总存在唯一的第一个数据元素
- 总存在唯一的最后一个数据元素
- 除第一个数据元素外,集合中的每一个数据元素都只有一个前驱的数据元素
- 除最后一个数据元素外,集合中的每一个数据元素都只有一个后继的数据元素
1.线性表的顺序存储结构:是指用一组地址连续的存储单元一次存放线性表的元素。为了使用顺序结构实现线性表,程序通常会采用数组来保存线性中的元素,是一种随机存储的数据结构,适合随机访问。java中ArrayList类是线性表的数组实现。
package com.ruicai.dsx;
import java.util.Arrays;
/**
* 线性表的顺序存储结构
* @author 邓绍祥
* @param <T>泛型,
*/
public class SequenceList<T> {
private int DEFAULT_SIZE=16;//默认大小
private int capacity; //保存数组的长度
private Object[] elementData;//对象数组,用来保存顺序表的元素
private int size=0;//顺序表中的当前的元素个数。
/*
* 无参数构造方法,给数组的长度赋上默认大小的值
* 给存放元素的对象数组开辟capacity大小的空间
* 只要一实例化此对象,就实例化了一个能装16个元素的表。此时是空表
*/
public SequenceList(){
capacity=DEFAULT_SIZE;
elementData=new Object[capacity];
}
/*
* 有参构造方法,先调用无参构造出一个能装16个元素的顺序线性表。
* 然后以一个初始化元素来创建顺序线性表
*/
public SequenceList(T element){
this();
elementData[0]=element;
size++;
}
/**
* 以指定长度的数组来创建顺序线性表
* @param element 指定线性表中的第一个元素
* @param initSize 指定顺序线性表底层的数组的长度。
*/
public SequenceList(T element,int initSize){
capacity=1; //容量设置为1
while(capacity<initSize){
//<<= :先左移,再将左移后的结果赋值。将capacity的值左移一位,结果是是二进制的10,转为十进制的结果为2
//如此capacity刚好大于initSize的最小的2的n次方
capacity<<=1;
}
elementData=new Object[capacity];//根据capacity的大小开辟相应的空间
elementData[0]=element;
size++;
}
/**
* 获取线性表的长度
* @return
*/
public int getLength(){
return size;
}
public T getElement(int i){
if(i<0||i>size-1){
throw new IndexOutOfBoundsException("数组越界");
}
return (T)elementData[i];
}
/**
* 查询顺序线性表中指定元素的索引
* @param element 指定的元素
* @return 返回索引
*/
public int getIndex(T element){
for(int i=0;i<size;i++){
if(elementData[i].equals(element)){
return i;
}
}
return -1; //返回-1则代表顺序线性表中无该元素
}
/**
* 给顺序线性表扩容,判断如果给定的参数大于当前顺序表的容量,则扩容,
* 否则什么也不做。
* @param minCapacity
*/
private void ensureCapacity(int minCapacity){
if(minCapacity>capacity){
while(capacity<minCapacity){
capacity<<=1;//左移一位,扩容
}
//Array.copyOf(arg1,arg2)是数组的复制操作,第一参数是要复制的数组对象,第二参数是要复制的长度,如果长度超多原数组的长度
//则保留数组默认值
elementData=Arrays.copyOf(elementData, capacity);//如此变实现了扩容
}
}
/**
* 向线性表指定位置插入一个元素
* @param element
* @param index
*/
public 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 index
* @return 将该值返回
*/
public T delete(int index){
if(index<0||index>size-1){
throw new IndexOutOfBoundsException("数组越界");
}
T odlValue=(T)elementData[index];
int numMoved=size-index-1;//需要移动的次数,
if(numMoved>0){
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
//清空最后一个元素,因为最后一个元素还存在
elementData[--size]=null;
return odlValue;
}
/**
* 删除最后一个元素
* @return
*/
public T remove(){
return delete(size-1);
}
/**
* 判断线性表是否为空
* @return
*/
public boolean isEmpty(){
return size==0;
}
/**
* 清空线性表
*/
public void clear(){
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.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素。
链表又可分为:
- 单链表:每个节点只保留一个引用,该引用指向当前节点的下一个节点,没有引用指向头结点,尾节点的next引用为null。
- 循环链表:一种首尾相连的链表。
- 双向链表:每个节点有两个引用,一个指向当前节点的上一个节点,另外一个指向当前节点的下一个节点。
下面给出线性表双向链表的实现:java中LinkedList是线性表的链式实现,是一个双向链表。
package com.ruicai.test;
/**
* description:双向链表
* @author:dsx_zz@qq.com
* @date:2018/2/8 11:03
*/
public class LinkedList<T> {
//定义一个内部类Node,Node实例代表链表的节点
private class Node{
//保存节点的数据,定义为泛型
private T data;
//指向上个节点的引用,上个节点也是Node,
private Node prev;
//指向下个节点的引用,下个节点也是Node
private Node next;
//无参构造
private 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 LinkedList(){
//空链表,header和tail都是null
header=null;
tail=null;
}
//以指定数据元素来创建链表,该链表只有一个元素
public LinkedList(T element){
header=new Node(element,null,null);
//只有一个节点,header,tail都指向该节点
tail=header;
size++;
}
//包含全属性的构造函数
public LinkedList(Node header,Node tail,int size){
this.header=header;
this.tail=tail;
this.size=size;
}
//返回链表的长度,节点个数
public int length(){
return size;
}
//获取链式线性表中索引为index处的元素
public T get(int index){
return getNodeByIndex(index).data;
}
//根据索引index获取指定位置的节点
public Node getNodeByIndex(int index){
if(index<0||index>size-1){
throw new IndexOutOfBoundsException("线性表越界异常");
}
if (index<=size/2){ //index小于链表元素个数的一半
//从header节点开始,这样效率高些
Node current=header; //从头开始数嘛
//此循环条件相当于给链式线性表赋索引,
for (int i=0;i<=size/2&¤t!=null;i++,current=current.next){
if (i==index){
return current;
}
}
}else {
Node current=tail;//从尾节点开始搜索,for循环的条件倒着来
for (int i=size-1;i>size/2&¤t!=null;i++,current=current.prev){
if (i==index){
return current;
}
}
}
return null;
}
/**
* 获取链式线性表指定元素的索引
* @param element:指定的元素
* @return 返回索引
*/
public int getElementIndex(T element){
Node current=header;
for (int i=0;i<size&¤t!=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){
addAtTail(element);
}
}
/**
* 采用尾插法,插入新的节点
* @param element
*/
public void addAtTail(T element){
//如果该链表是空的,则直接创建一个
if(header==null){
header=new Node(element,null,null);
}else{
/* 创建一个新节点,新节点的prev指向原来的tail节点 */
Node newNode=new Node(element,tail,null);
tail.next=newNode;//让原来的尾节点的next指向新节点
//新节点变为尾节点
tail=newNode;
}
size++;
}
/**
* 采用头插法添加一个新的元素
* @param element
*/
public void addAtHeader(T element){
if(header==null){
header=new Node(element,null,null);
}else {
/*创建一个新节点,新节点的next指向原来链表的header节点*/
Node newNode=new Node(element,null,header);
/*原来链表的头节点的prev指向新节点*/
header.prev=newNode;
/*newNode变为新的头节点*/
header=newNode;
}
size++;
}
/**
* 删除线性表指定索引处的元素
* @param index
* @return 返回被删除的节点
*/
public T delete(int index){
if(index<0||index>size-1){
throw new IndexOutOfBoundsException("链表索引越界");
}
Node del=null;
if(index==0){
//索引为0,即删除的是头节点
del=header;
header=header.next;//将原来头结点的next变为新的头结点
header.prev=null;//注意顺序,先赋值在删除
}else {
Node prev=getNodeByIndex(index-1);//获取要删除节点的前一个
del=prev.next;//要删除的节点
/*要删除节点的上一个节点的next指向要删除节点的next*/
prev.next=del.next;
if(del.next!=null){
del.next.prev=prev;
}
del.prev=null;
del.next=null;
}
size--;
return del.data;//
}
//删除链式线性表的最后一个元素
public T remove(){
return delete(size-1);
}
//判断线性表是否为空链表
public boolean isEmpty(){
return size==0;
}
//清空线性表
public void clear(){
header=null;
tail=null;
size=0;
}
//覆写toString方法
public String toString(){
if (isEmpty()){
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 - 1 , len).append("]").toString();
}
}
}
线性表的两种实现比较
- 空间性能:顺序表:顺序表的存储空间是静态分布的,需要一个长度固定的数组,因此总有部分数组元素被浪费。
链表:链表的存储空间是动态分布的,因此不会空间浪费。但是由于链表需要而外的空间来为每个节点保存指针,因此要牺牲一部分空间。 - 时间性能:顺序表:顺序表中元素的逻辑顺序与物理存储顺序是保持一致的,而且支持随机存取。因此顺序表在查找、读取时性能很好。
链表:链表采用链式结构来保存表内元素,因此在插入、删除元素时性能要好
本文介绍了线性表的两种存储结构:顺序存储结构和链式存储结构,并详细阐述了它们的特点、优缺点及实现方式。包括顺序表的数组实现和链表的不同类型如单链表、循环链表和双向链表。
1878

被折叠的 条评论
为什么被折叠?



