【Java数据结构学习笔记之一】线性表的存储结构及其代码实现

应用程序后在那个的数据大致有四种基本的逻辑结构:

  • 集合:数据元素之间只有"同属于一个集合"的关系
  • 线性结构:数据元素之间存在一个对一个的关系
  • 树形结构:数据元素之间存在一个对多个关系
  • 图形结构或网状结构:数据元素之间存在多个对多个的关系



对于数据不同的逻辑结构,计算机在物理磁盘上通常有两种屋里存储结构

  • 顺序存储结构
  • 链式存储结构


本篇博文主要讲的是线性结构,而线性结构主要是线性表,非线性结构主要是树和图。
线性表的基本特征:

  • 总存在唯一的第一个数据元素
  • 总存在唯一的最后一个数据元素
  • 除第一个数据元素外,集合中的每一个数据元素都只有一个前驱的数据元素
  • 除最后一个数据元素外,集合中的每一个数据元素都只有一个后继的数据元素



1.线性表的顺序存储结构:是指用一组地址连续的存储单元一次存放线性表的元素。为了使用顺序结构实现线性表,程序通常会采用数组来保存线性中的元素,是一种随机存储的数据结构,适合随机访问。java中ArrayList类是线性表的数组实现。

 

  1 import java.util.Arrays;
  2 public class SequenceList<T>
  3 {
  4     private int DEFAULT_SIZE = 16;
  5     //保存数组的长度。
  6     private int capacity;
  7     //定义一个数组用于保存顺序线性表的元素
  8     private Object[] elementData;
  9     //保存顺序表中元素的当前个数
 10     private int size = 0;
 11     //以默认数组长度创建空顺序线性表
 12     public SequenceList()
 13     {
 14         capacity = DEFAULT_SIZE;
 15         elementData = new Object[capacity];
 16     }
 17     //以一个初始化元素来创建顺序线性表
 18     public SequenceList(T element)
 19     {
 20         this();
 21         elementData[0] = element;
 22         size++;
 23     }
 24     /**
 25      * 以指定长度的数组来创建顺序线性表
 26      * @param element 指定顺序线性表中第一个元素
 27      * @param initSize 指定顺序线性表底层数组的长度
 28      */
 29     public SequenceList(T element , int initSize)
 30     {
 31         capacity = 1;
 32         //把capacity设为大于initSize的最小的2的n次方
 33         while (capacity < initSize)
 34         {
 35             capacity <<= 1;
 36         }
 37         elementData = new Object[capacity];
 38         elementData[0] = element;
 39         size++;
 40     }
 41     //获取顺序线性表的大小
 42     public int length()
 43     {
 44         return size;
 45     }
 46     //获取顺序线性表中索引为i处的元素
 47     public T get(int i)
 48     {
 49         if (i < 0 || i > size - 1)
 50         {
 51             throw new IndexOutOfBoundsException("线性表索引越界");
 52         }
 53         return (T)elementData[i];
 54     }
 55     //查找顺序线性表中指定元素的索引
 56     public int locate(T element)
 57     {
 58         for (int i = 0 ; i < size ; i++)
 59         {
 60             if (elementData[i].equals(element))
 61             {
 62                 return i;
 63             }
 64         }
 65         return -1;
 66     }
 67     //向顺序线性表的指定位置插入一个元素。
 68     public void insert(T element , int index)
 69     {
 70         if (index < 0 || index > size)
 71         {
 72             throw new IndexOutOfBoundsException("线性表索引越界");
 73         }
 74         ensureCapacity(size + 1);
 75         //将index处以后所有元素向后移动一格。
 76         System.arraycopy(elementData , index , elementData
 77              , index + 1 , size - index);
 78         elementData[index] = element;
 79         size++;
 80     }
 81     //在线性顺序表的开始处添加一个元素。
 82     public void add(T element)
 83     {
 84         insert(element , size);
 85     }
 86     //很麻烦,而且性能很差
 87     private void ensureCapacity(int minCapacity)
 88     {
 89         //如果数组的原有长度小于目前所需的长度
 90         if (minCapacity > capacity)
 91         {
 92             //不断地将capacity * 2,直到capacity大于minCapacity为止
 93             while (capacity < minCapacity)
 94             {
 95                 capacity <<= 1;
 96             }
 97             elementData = Arrays.copyOf(elementData , capacity);
 98         }
 99     }
100     //删除顺序线性表中指定索引处的元素
101     public T delete(int index)
102     {
103         if (index < 0 || index > size - 1)
104         {
105             throw new IndexOutOfBoundsException("线性表索引越界");
106         }
107         T oldValue = (T)elementData[index];
108         int numMoved = size - index - 1;
109         if (numMoved > 0)
110         {
111             System.arraycopy(elementData , index+1
112                 , elementData, index ,     numMoved);
113         }
114         //清空最后一个元素
115         elementData[--size] = null; 
116         return oldValue;
117     }
118     //删除顺序线性表中最后一个元素
119     public T remove()
120     {
121         return delete(size - 1);
122     }
123     //判断顺序线性表是否为空表
124     public boolean empty()
125     {
126         return size == 0;
127     }
128     //清空线性表
129     public void clear()
130     {
131         //将底层数组所有元素赋为null
132         Arrays.fill(elementData , null);
133         size = 0;
134     }
135     public String toString()
136     {
137         if (size == 0)
138         {
139             return "[]";
140         }
141         else
142         {
143             StringBuilder sb = new StringBuilder("[");
144             for (int i = 0 ; i < size ; i++ )
145             {
146                 sb.append(elementData[i].toString() + ", ");
147             }
148             int len = sb.length();
149             return sb.delete(len - 2 , len).append("]").toString();
150         }
151     }
152 }

 

2.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素。
链表又可分为:

  • 单链表:每个节点只保留一个引用,该引用指向当前节点的下一个节点,没有引用指向头结点,尾节点的next引用为null。
  • 循环链表:一种首尾相连的链表。
  • 双向链表:每个节点有两个引用,一个指向当前节点的上一个节点,另外一个指向当前节点的下一个节点。


下面给出线性表双向链表的实现:java中LinkedList是线性表的链式实现,是一个双向链表。

  1 public class DuLinkList<T>
  2 {
  3     //定义一个内部类Node,Node实例代表链表的节点。
  4     private class Node
  5     {
  6         //保存节点的数据
  7         private T data;
  8         //指向上个节点的引用
  9         private Node prev;
 10         //指向下个节点的引用
 11         private Node next;
 12         //无参数的构造器
 13         public Node()
 14         {
 15         }
 16         //初始化全部属性的构造器
 17         public Node(T data , Node prev , Node next)
 18         {
 19             this.data = data;
 20             this.prev = prev;
 21             this.next = next;
 22         }
 23     }
 24     //保存该链表的头节点
 25     private Node header;
 26     //保存该链表的尾节点
 27     private Node tail;
 28     //保存该链表中已包含的节点数
 29     private int size;
 30     //创建空链表
 31     public DuLinkList()
 32     {
 33         //空链表,header和tail都是null
 34         header = null;
 35         tail = null;
 36     }
 37     //以指定数据元素来创建链表,该链表只有一个元素
 38     public DuLinkList(T element)
 39     {
 40         header = new Node(element , null , null);
 41         //只有一个节点,header、tail都指向该节点
 42         tail = header;
 43         size++;
 44     }
 45     //返回链表的长度    
 46     public int length()
 47     {
 48         return size;
 49     }
 50 
 51     //获取链式线性表中索引为index处的元素
 52     public T get(int index)
 53     {
 54         return getNodeByIndex(index).data;
 55     }
 56     //根据索引index获取指定位置的节点
 57     private Node getNodeByIndex(int index)
 58     {
 59         if (index < 0 || index > size - 1)
 60         {
 61             throw new IndexOutOfBoundsException("线性表索引越界");
 62         }
 63         if (index <= size / 2)
 64         {
 65             //从header节点开始
 66             Node current = header;
 67             for (int i = 0 ; i <= size / 2 && current != null
 68                 ; i++ , current = current.next)
 69             {
 70                 if (i == index)
 71                 {
 72                     return current;
 73                 }
 74             }
 75         }
 76         else
 77         {
 78             //从tail节点开始搜索
 79             Node current = tail;
 80             for (int i = size - 1 ; i > size / 2 && current != null
 81                 ; i++ , current = current.prev)
 82             {
 83                 if (i == index)
 84                 {
 85                     return current;
 86                 }
 87             }
 88         }
 89         return null;
 90     }
 91     //查找链式线性表中指定元素的索引
 92     public int locate(T element)
 93     {
 94         //从头节点开始搜索
 95         Node current = header;
 96         for (int i = 0 ; i < size && current != null
 97             ; i++ , current = current.next)
 98         {
 99             if (current.data.equals(element))
100             {
101                 return i;
102             }
103         }
104         return -1;
105     }
106     //向线性链式表的指定位置插入一个元素。
107     public void insert(T element , int index)
108     {
109         if (index < 0 || index > size)
110         {
111             throw new IndexOutOfBoundsException("线性表索引越界");
112         }
113         //如果还是空链表
114         if (header == null)
115         {
116             add(element);
117         }
118         else
119         {
120             //当index为0时,也就是在链表头处插入
121             if (index == 0)
122             {
123                 addAtHeader(element);
124             }
125             else
126             {
127                 //获取插入点的前一个节点
128                 Node prev = getNodeByIndex(index - 1);
129                 //获取插入点的节点
130                 Node next = prev.next;
131                 //让新节点的next引用指向next节点,prev引用指向prev节点
132                 Node newNode = new Node(element , prev , next);
133                 //让prev的next指向新节点。
134                 prev.next = newNode;
135                 //让prev的下一个节点的prev指向新节点
136                 next.prev = newNode;
137                 size++;
138             }
139         }
140     }
141     //采用尾插法为链表添加新节点。
142     public void add(T element)
143     {
144         //如果该链表还是空链表
145         if (header == null)
146         {
147             header = new Node(element , null , null);
148             //只有一个节点,header、tail都指向该节点
149             tail = header;
150         }
151         else
152         {
153             //创建新节点,新节点的pre指向原tail节点
154             Node newNode = new Node(element , tail , null);
155             //让尾节点的next指向新增的节点
156             tail.next = newNode;
157             //以新节点作为新的尾节点
158             tail = newNode;
159         }
160         size++;
161     }
162     //采用头插法为链表添加新节点。
163     public void addAtHeader(T element)
164     {
165         //创建新节点,让新节点的next指向原来的header
166         //并以新节点作为新的header
167         header = new Node(element , null , header);
168         //如果插入之前是空链表
169         if (tail == null)
170         {
171             tail = header;
172         }
173         size++;
174     }
175     //删除链式线性表中指定索引处的元素
176     public T delete(int index)
177     {
178         if (index < 0 || index > size - 1)
179         {
180             throw new IndexOutOfBoundsException("线性表索引越界");
181         }
182         Node del = null;
183         //如果被删除的是header节点
184         if (index == 0)
185         {
186             del = header;
187             header = header.next;
188             //释放新的header节点的prev引用
189             header.prev = null;
190         }
191         else
192         {
193             //获取删除点的前一个节点
194             Node prev = getNodeByIndex(index - 1);
195             //获取将要被删除的节点
196             del = prev.next;
197             //让被删除节点的next指向被删除节点的下一个节点。
198             prev.next = del.next;
199             //让被删除节点的下一个节点的prev指向prev节点。
200             if (del.next != null)
201             {
202                 del.next.prev = prev;
203             }        
204             //将被删除节点的prev、next引用赋为null.
205             del.prev = null;
206             del.next = null;
207         }
208         size--;
209         return del.data;
210     }
211     //删除链式线性表中最后一个元素
212     public T remove()
213     {
214         return delete(size - 1);
215     }
216     //判断链式线性表是否为空链表
217     public boolean empty()
218     {
219         return size == 0;
220     }
221     //清空线性表
222     public void clear()
223     {
224         //将底层数组所有元素赋为null
225         header = null;
226         tail = null;
227         size = 0;
228     }
229     public String toString()
230     {
231         //链表为空链表时
232         if (empty())
233         {
234             return "[]";
235         }
236         else
237         {
238             StringBuilder sb = new StringBuilder("[");
239             for (Node current = header ; current != null
240                 ; current = current.next )
241             {
242                 sb.append(current.data.toString() + ", ");
243             }
244             int len = sb.length();
245             return sb.delete(len - 2 , len).append("]").toString();
246         }
247     }
248     public String reverseToString()
249     {
250         //链表为空链表时
251         if (empty())
252         {
253             return "[]";
254         }
255         else
256         {
257             StringBuilder sb = new StringBuilder("[");
258             for (Node current = tail ; current != null 
259                 ; current = current.prev )
260             {
261                 sb.append(current.data.toString() + ", ");
262             }
263             int len = sb.length();
264             return sb.delete(len - 2 , len).append("]").toString();
265         }
266     }
267 }

  线性表的两种实现比较

    • 空间性能: 顺序表:顺序表的存储空间是静态分布的,需要一个长度固定的数组,因此总有部分数组元素被浪费。

 

    • 链表:链表的存储空间是动态分布的,因此不会空间浪费。但是由于链表需要而外的空间来为每个节点保存指针,因此要牺牲一部分空间。
    • 时间性能: 顺序表:顺序表中元素的逻辑顺序与物理存储顺序是保持一致的,而且支持随机存取。因此顺序表在查找、读取时性能很好。

 

    • 链表:链表采用链式结构来保存表内元素,因此在插入、删除元素时性能要好

 

转载于:https://www.cnblogs.com/ECJTUACM-873284962/p/7485590.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分析Java核心转储

在本文中&#xff0c;我将向您展示如何调试Java核心文件&#xff0c;以查看导致JVM崩溃的原因。 我将使用在上一篇文章&#xff1a; 生成Java Core Dump中生成的核心文件。 您可以通过以下几种方法来诊断JVM崩溃&#xff1a; hs_err_pid日志文件 当JVM中发生致命错误时&#x…

网页结构 盒模型

HTML是个什么鬼&#xff1f; 前端开发人员要想和浏览器沟通&#xff0c;就要用到浏览器才能够识别的语言&#xff08;HTML超文本标记语言&#xff09;&#xff0c;所以他是一门浏览器能够识别的语言。是一种由标签组成的超文本标记语言&#xff0c;而非编程语言。一个html文档…

idea创建git分支

此时只是在本地创建好了分支&#xff0c;修改源代码后add&#xff0c;commit将本地分支提交到远程仓库分支已创建&#xff0c;其它成员此时就可以从git拉分支转载于:https://www.cnblogs.com/qianqiu-1026/p/8589218.html

已知华氏温度f c语言,编程题:已知两种温度的换算公式C=(5/9)(F-32),试编写一个程序输入华氏度F,输出摄氏度。...

使用python的写法为&#xff1a;valinput("请输入带有温度表示符号的温度值(例如&#xff1a;32c)")if val[-1] in ["C","c"]:f1.8*float(val[0:-1])32print("转换后的温度为&#xff1a;%.2fF"%f)elif val[-1] in ["F",&qu…

MongoDB身份验证

我最近更新了Mongometer &#xff0c;使其更加灵活。 发布新版本后不久&#xff0c;其中一位用户通过在帖子中发表评论来反馈问题。 我启动了我的机器&#xff0c;打开了我的IDE&#xff0c;发现了问题&#xff0c;并在半小时内将修复程序推送到了github 。 这不是快速的成功案…

golang ffmpeg 做网络直播

最近在公司做在线视频转码的工作&#xff0c;研究了下ffmpeg 最后直接研究了下网络直播&#xff0c;我是在我自己的mac 上面测试的&#xff0c;效果&#xff0c;还可以&#xff0c;先看看效果图吧 ffmpeg 我是通过brew安装 的&#xff0c;这步就略了 VLC这个播放器怎么安装的也…

androidstudio带pom的上传到jcenter_输送机@网带输送机@304网带输送机@304不锈钢网带输送机@输送机网带厂家定制...

输送机网带输送机食品网带输送机304网带输送机304不锈钢网带输送机输送机网带厂家定制输送机主要用于运输食品原料或成品。食品输送机根据输送带不同可分为皮带的&#xff0c;链板的&#xff0c;网带的。输送形式有&#xff1a;直行的&#xff0c;爬坡提升的&#xff0c;清洗的…

box-sizing -- 盒模型

项目开发中&#xff0c;在浏览同事的代码&#xff0c;发现他经常用一个属性--box-sizing&#xff0c;很好奇是什么&#xff0c;于是乎&#xff0c;上网查阅资料学了起来。  首先我们先复习一下盒模型的组成&#xff1a;一个div通常由 content(内容) margin padding border组成…

有温度传感器的风机控制系统C语言,毕业论文--基于单片机的工业风机控制器设计与实现.doc...

大连东软信息学院本科毕业设计(论文)论文题目论文题目&#xff1a;基于单片机的工业风机控制器设计与实现系 所&#xff1a;电子工程系专 业&#xff1a;电子信息工程(嵌入式系统工程方向)学生姓名&#xff1a;学生学号&#xff1a;指导教师&#xff1a;导师职称&#xff1a;讲…

JAXB和java.util.Map

具有讽刺意味的是&#xff0c;可能很难在JAXB&#xff08;JSR-222&#xff09;中映射java.util.Map类&#xff1f; 在这篇文章中&#xff0c;我将介绍一些使其变得更容易的项目。 Java模型 以下是我们将用于此示例的Java模型。 顾客 Customer类具有Map类型的属性。 我之所以…

转载大神的一篇文章----【如何选择开源许可证?】

原文地址&#xff1a;http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html 如何为代码选择开源许可证&#xff0c;这是一个问题。 世界上的开源许可证&#xff0c;大概有上百种。很少有人搞得清楚它们的区别。即使在最流行的六种----GPL、BSD、…

教你如何用 lib-flexible 实现移动端H5页面适配

前话 好久没写教程了&#xff08;可能会误导新手的菜鸟教程(&#xffe3;▽&#xffe3;)"&#xff09;。 这是我的github&#xff0c;欢迎前端大大们和我一起学习交流 https://github.com/pwcong 最近入职公司做前端实习&#xff0c;这几个星期来学到了移动端H5页面适配…

使用GlassFish 3.1.2.2和Primefaces 3.4的JDBC领域和基于表单的身份验证

我的博客上最受欢迎的帖子之一是有关JDBC安全领域和带有Primefaces的GlassFish上基于表单的身份验证的简短教程。 在收到有关它不再适用于最新的GlassFish 3.1.2.2的评论后&#xff0c;我认为可能是时候重新访问它并提出更新的版本了。 开始了&#xff1a; 制备 就像在原始教程…

thinkcmf常用标签

1、图片地址&#xff1a;{:cmf_get_image_url($vo.icon)} 2、模板控件 模板变量调用&#xff1a;$theme_vars.title <widget name"aboutUs">{$widget.title} //控件标题 {$widget.vars.subTitle} //控件变量 subTitle {:nl2br($widget.vars.content)} //输…

esplise自定义快捷代码补全_【Eclipse】_Eclipse自动补全增强方法 常用快捷键

一&#xff0c;Eclipse自动补全增强方法在Eclipse中&#xff0c;从Window -> preferences -> Java -> Editor -> Content assist -> Auto-Activation下&#xff0c;我们可以在"."号后面加入我们需要自动提示的首字幕&#xff0c;比如"abc"&…

ubuntu下docker安装,配置python运行环境

参考自: 1.最详细ubuntu安装docker教程 2.使用docker搭建python环境 首先假设已经安装了docker&#xff0c;卸载原来的docker 在命令行中运行&#xff1a; sudo apt-get updatesudo apt-get remove docker docker-engine docker.io containerd runc 安装docker依赖 apt-get…

android前置拍照镜像代码,从Android的前置摄像头拍摄的镜像翻转视频

我有一个能够拍摄图像和录制视频的摄像头应用程序。但是&#xff0c;当从设备前置摄像头捕捉图像或录制视频时&#xff0c;结果会翻转&#xff0c;就像您在看镜子一样。我想再次翻转&#xff0c;所以看起来很正常。我设法与图像要做到这一点&#xff0c;通过使用Matrix翻转Bitm…

python 打造一个sql注入脚本 (一)

0x00前言&#xff1a; 昨天刚刚看完小迪老师的sql注入篇的第一章 所以有了新的笔记。 0x01笔记&#xff1a; sql注入原理&#xff1a; 网站数据传输中&#xff0c;接受变量传递的值未进行过滤&#xff0c;导致直接带入数据库查询执行的操作。 sql注入对渗透的作用&#xff1a; …

文档声明和HTML样式表

文档声明 不是注释也不是元素&#xff0c;总是在HTML的第一行 书写格式&#xff1a;<!DOCTYPE HTML> 是用于通知浏览器目前文档正使用哪一个HTML版本&#xff08;相关属性 lang&#xff09; 若不写文档声明&#xff0c;浏览器渲染页面时会进入怪异模式 HTML元素又叫根元素…

JSF –渴望的CDI bean

每个人都知道JSF 2中热切的托管bean。 ManagedBean具有eager属性。 如果eager true并且范围是application&#xff0c;那么必须在应用程序启动时而不是在第一次引用该bean时创建此bean。 当您要在应用程序启动期间加载应用程序范围的数据&#xff08;例如&#xff0c;菜单的某些…