点击上方“Java专栏”,选择“置顶或者星标”

第一时间阅读精彩文章!

1、☞ 程序员进阶必备资源免费送「21种技术方向!」 点击查看☜

2、☞ 《Java面试手册》.PDF    点击查看

  1. 线性表抽象数据类型概述

  首先来说明一下什么是抽象数据类型,我们都知道java在默认情况下,所有的基本数据类型(int,float,boolean等)都支持基本运算,如加减法,这是因为系统已帮我们实现了这些基本数据类型的的基本运算。而对于自定义的数据类型(如类)也需要定义相应的运算,但在实际使用这些自定义的数据类型的运算时需要自己实现相关的运算,也就是说用户自定义的数据类型的运算需要我们自己利用系统提供的基本运算来定义和实现。这些自定义了数据结构(如自定义类)和包含相关运算组合实现的数据类型就称其为抽象数据类型(ADT,Abstract Data Type),因此一个ADT会包含数据声明和运算声明。常用的ADT包含链表、栈、队列、优先队列、二叉树、散列表、图等,所以接下来我们要分析的顺序表和链表也属于ADT范畴。下面引用自java数据结构一书对线性表的定义:

  线性表是由n(n>=0)个类型相同的数据元素a0,a1,…,an-1组成的有限的序列,在数学中记作(a0,a1,…,an-1),其中ai的数据类型可以是基本数据类型(int,float等)、字符或类。n代表线性表的元素个数,也称其为长度(Length)。若n=0,则为空表;若n > 0,则ai(0 < i < n-1)有且仅有一个前驱(Predecessor)元素ai-1和一个后继(Successor)元素ai+1,a0没有前驱元素,ai没有后继元素。

以上便是对线性表抽象数据类型概述,下面我们开始分别针对顺序表和链表进行深入分析。

2.线性表的顺序存储设计与实现(顺序表)

2.1 顺序存储结构的设计原理概要

  顺序存储结构底层是利用数组来实现的,而数组可以存储具有相同数据类型的元素集合,如int,float或者自定义类型等,当我们创建一个数组时,计算机操作系统会为该数组分配一块连续的内存块,这也就意味着数组中的每个存储单元的地址都是连续的,因此只要知道了数组的起始内存地址就可以通过简单的乘法和加法计算出数组中第n-1个存储单元的内存地址,就如下图所示:

通过上图可以发现为了访问一个数组元素,该元素的内存地址需要计算其距离数组基地址的偏移量,即用一个乘法计算偏移量然后加上基地址,就可以获得数组中某个元素的内存地址。其中c代表的是元素数据类型的存储空间大小,而序号则为数组的下标索引。整个过程需要一次乘法和一次加法运算,因为这两个操作的执行时间是常数时间,所以我们可以认为数组访问操作能再常数时间内完成,即时间复杂度为O(1),这种存取任何一个元素的时间复杂度为O(1)的数据结构称之为随机存取结构。而顺序表的存储原理正如上图所示,因此顺序表的定义如下(引用):

  线性表的顺序存储结构称之为顺序表(Sequential List),它使用一维数组依次存放从a0到an-1的数据元素(a0,a1,…,an-1),将ai(0< i <> n-1)存放在数组的第i个元素,使得ai与其前驱ai-1及后继ai+1的存储位置相邻,因此数据元素在内存的物理存储次序反映了线性表数据元素之间的逻辑次序。

2.2 顺序存储结构的实现分析

  接着我们来分析一下顺序表的实现,先声明一个顺序表接口类ISeqList,然后实现该接口并实现接口方法的代码,ISeqList接口代码如下:

package com.zejian.structures.LinkedList;

/**

* Created by zejian on 2016/10/30.

* 顺序表顶级接口

*/

publicinterfaceISeqList {

/**

* 判断链表是否为空

* @return

*/

boolean isEmpty();

/**

* 链表长度

* @return

*/

int length();

/**

* 获取元素

* @param index

* @return

*/

T get(int index);

/**

* 设置某个元素的值

* @param index

* @param data

* @return

*/

T set(int index, T data);

/**

* 根据index添加元素

* @param index

* @param data

* @return

*/

boolean add(int index, T data);

/**

* 添加元素

* @param data

* @return

*/

boolean add(T data);

/**

* 根据index移除元素

* @param index

* @return

*/

T remove(int index);

/**

* 根据data移除元素

* @param data

* @return

*/

boolean remove(T data);

/**

* 根据data移除元素

* @param data

* @return

*/

boolean removeAll(T data);

/**

* 清空链表

*/

void clear();

/**

* 是否包含data元素

* @param data

* @return

*/

boolean contains(T data);

/**

* 根据值查询下标

* @param data

* @return

*/

int indexOf(T data);

/**

* 根据data值查询最后一个出现在顺序表中的下标

* @param data

* @return

*/

int lastIndexOf(T data);

/**

* 输出格式

* @return

*/

String toString();

}

}

 代码中声明了一个Object数组,初始化数组大小默认为64,存储的元素类型为泛型T,length则为顺序表的长度,部分方法实现比较简单,这里不过多分析,我们主要分析get(int index)、set(int index, T data)、add(int index, T data)、remove(int index)、removeAll(T data)、indexof(T data)等方法的实现。

  • get(int index) 实现分析

    从顺序表中获取值是一种相当简单的操作并且效率很高,这是由于顺序表内部采用了数组作为存储数据的容器。因此只要根据传递的索引值,然后直接获取数组中相对应下标的值即可,代码实现如下:

public T get(int index){

if(index>=0&& index<this.length)

return(T) this.table[index];

returnnull;

}

  • set(int index, T data) 实现分析 在顺序表中替换值也是非常高效和简单的,只要根据传递的索引值index找到需要替换的元素,然后把对应元素值替换成传递的data值即可,代码如下:

public T set(int index, T data){

if(index>=0&& index<this.length&& data!=null)

{

T old = (T)this.table[index];

this.table[index] = data;

return old;

}

returnnull;

}

  • add(int index, T data)实现分析 在顺序表中执行插入操作时,如果其内部数组的容量尚未达到最大值时,可以归结为两种情况,一种是在头部插入或者中间插入,这种情况下需要移动数组中的数据元素,效率较低,另一种是在尾部插入,无需移动数组中的元素,效率高。但是当顺序表内部数组的容量已达到最大值无法插入时,则需要申请另一个更大容量的数组并复制全部数组元素到新的数组,这样的时间和空间开销是比较大的,也就导致了效率更为糟糕了。因此在插入频繁的场景下,顺序表的插入操作并不是理想的选择。下面是顺序表在数组容量充足下头部或中间插入操作示意图(尾部插入比较简单就不演示了):

    顺序表在数组容量不充足的情况下头部或中间插入操作示意图:

理解了以上几种顺序表的插入操作后,我们通过代码来实现这个插入操作如下,注释很清晰就过多分析了:

/**

* 根据index插入元素

* @param index 插入位置的下标,0作为起始值

* @param data 插入的数据

* @return

*/

publicboolean add(int index, T data){

if(data==null)

returnfalse;

//插入下标的容错判断,插入在最前面

if(index<0)

index=0;

//插入下标的容错判断,插入在最后面

if(index>this.length)

index = this.length;

//判断内部数组是否已满

if(this.length==table.length)

{

//把原数组赋值给临时数组

Object[] temp = this.table;

//对原来的数组进行成倍拓容,并把原数组的元素复制到新数组

this.table = newObject[temp.length*2];

//先把原数组下标从0到index-1(即插入位置的前一个位置)复制到新数组

for(int i=0; i

this.table[i] = temp[i];

}

}

//从原数组的最后一个元素开始直到index位置,都往后一个位置

// 最终腾出来的位置就是新插入元素的位置了

for(int j=this.length-1; j>=index; j--) {

this.table[j + 1] = this.table[j];

}

//插入新值

this.table[index] = data;

//长度加一

this.length++;

//插入成功

returntrue;

}

  • remove(int index) 实现分析 顺序表的删除操作和前的插入操作情况是类似的,如果是在中间或者头部删除顺序表中的元素,那么在删除位置之后的元素都必须依次往前移动,效率较低,如果是在顺序表的尾部直接删除的话,则无需移动元素,此情况下删除效率高。如下图所示在顺序表中删除元素ai时,ai之后的元素都依次往前移动: 

删除操作的代码实现如下:

/**

* 根据index删除元素

* @param index 需要删除元素的下标

* @return

*/

public T remove(int index)

{

if(this.length!=0&& index>=0&& index<this.length)

{

//记录删除元素的值并返回

T old = (T)this.table[index];

//从被删除的元素位置开,其后的元素都依次往前移动

for(int j=index; j<this.length-1; j++) {

this.table[j] = this.table[j + 1];

}

//设置数组元素对象为空

this.table[this.length-1]=null;

//顺序表长度减1

this.length--;

return old;

}

returnnull;

}

  • removeAll(T data) 实现分析 在顺序表中根据数据data找到需要删除的数据元素和前面分析的根据index删除顺序表中的数据元素是一样的道理,因此我们只要通过比较找到与data相等的数据元素并获取其下标,然后调用前面实现的remove(int index)方法来移除即可。代码实现如下:

  1. @Override

  2. publicboolean removeAll(T data) {

  3. boolean done=false;

  4. if(this.length!=0&& data!=null)

  5. {

  6. int i=0;

  7. while(i<this.length)

  8. //找出数据相同的选项

  9. if(data.equals(this.table[i]))

  10. {

  11. this.remove(i);//根据下标删除

  12. done = true;

  13. }

  14. else

  15. i++;//继续查找

  16. }

  17. return done;

  18. }

indexOf(T data) 实现分析 要根据data在顺序表中查找第一个出现的数据元素的下标,只需要通过对比数据项是否相等,相等则返回下标,不相等则返回-1,indexOf和lastIndexOf方法实现如下:

/**

* 根据数据查询下标

* @param data

* @return

*/

@Override

publicint indexOf(T data)

{

if(data!=null)

for(int i=0; i<this.length; i++) {

//相当则返回下标

if(this.table[i].equals(data))

return i;

}

return-1;

}

/**

* 根据data查询最后一个出现在顺序表中的下标

* @param data

* @return

*/

@Override

publicint lastIndexOf(T data)

{

if(data!=null)

for(int i=this.length-1; i>=0; i--)

if(data.equals(this.table[i]))

return i;

return-1;

}

以上便是顺序表的主要的操作方法,当然顺序表中还可以实现其他操作,如在初始化构造函数时传入数组来整体初始化顺序表,比较两个信息表是否相等、是否包含某个数据等。这里贴一下传入数据构建顺序表构造方法实现,其他实现代码我们这里就不贴了,稍后实现源码都会上传gitHub提供给大家:

/**

* 传入一个数组初始化顺序表

* @param array

*/

publicSeqList(T[] array){

if(array==null){

thrownewNullPointerException("array can\'t be empty!");

}

//创建对应容量的数组

this.table = newObject[array.length];

//复制元素

for(int i=0;i

this.table[i]=array[i];

}

this.length=array.length;

}

2.3 顺序存储结构的效率分析

  通过上述的分析,我们对顺序表的实现已有了比较清晰的认识,接下来看一下顺序表的执行效率问题,主要针对获取、插入、修改、删除等主要操作。前面分析过,由于顺序表内部采用了数组作为存储容器,而数组又是随机存取结构的容器,也就是说在创建数组时操作系统给数组分配的是一块连续的内存空间,数组中每个存储单元的地址都是连续的,所以在知道数组基地址后可以通过一个简单的乘法和加法运算即可计算出其他存储单元的内存地址(实际上计算机内部也就是这么做的),这两个运算的执行时间是常数时间,因此可以认为数组的访问操作能在常数时间内完成,即顺序表的访问操作(获取和修改元素值)的时间复杂为O(1)。  对于在顺序表中插入或者删除元素,从效率上则显得不太理想了,由于插入或者删除操作是基于位置的,需要移动数组中的其他元素,所以顺序表的插入或删除操作,算法所花费的时间主要是用于移动元素,如在顺序表头部插入或删除时,效率就显得相当糟糕了。若在最前插入或删除,则需要移动n(这里假设长度为n)个元素;若在最后插入或删除,则需要移动的元素为0。这里我们假设插入或删除值为第i(0

如果在各个位置插入元素的概率相同即 pi=1n+1pi=1n+1 (n+1个插入位置任意选择一个的概率)则有:

也就是说,在等概率的情况下,插入或者删除一个顺序表的元素平均需要移动顺序表元素总量的一半,其时间复杂度是O(n)。当然如果在插入时,内部数组容量不足时,也会造成其他开销,如复制元素的时间开销和新建数组的空间开销。  因此总得来说顺序表有以下优缺点:

  • 优点

    • 使用数组作为内部容器简单且易用

    • 在访问元素方面效率高

    • 数组具有内存空间局部性的特点,由于本身定义为连续的内存块,所以任何元素与其相邻的元素在物理地址上也是相邻的。

  • 缺点

    • 内部数组大小是静态的,在使用前必须指定大小,如果遇到容量不足时,需动态拓展内部数组的大小,会造成额外的时间和空间开销

    • 在内部创建数组时提供的是一块连续的空间块,当规模较大时可能会无法分配数组所需要的内存空间

  • 顺序表的插入和删除是基于位置的操作,如果需要在数组中的指定位置插入或者删除元素,可能需要移动内部数组中的其他元素,这样会造成较大的时间开销,时间复杂度为O(n)

    3.线性表的链式存储设计与实现(链表)

3.1 链表的链式存储结构设计原理概要

  通过前面对线性顺序表的分析,我们知道当创建顺序表时必须分配一块连续的内存存储空间,而当顺序表内部数组的容量不足时,则必须创建一个新的数组,然后把原数组的的元素复制到新的数组中,这将浪费大量的时间。而在插入或删除元素时,可能需要移动数组中的元素,这也将消耗一定的时间。鉴于这种种原因,于是链表就出场了,链表在初始化时仅需要分配一个元素的存储空间,并且插入和删除新的元素也相当便捷,同时链表在内存分配上可以是不连续的内存,也不需要做任何内存复制和重新分配的操作,由此看来顺序表的缺点在链表中都变成了优势,实际上也是如此,当然链表也有缺点,主要是在访问单个元素的时间开销上,这个问题留着后面分析,我们先通过一张图来初步认识一下链表的存储结构,如下:

  从图可以看出线性链表的存储结构是用若干个地址分散的存储单元存放数据元素的,逻辑上相邻的数据元素在物理位置上不一定相邻,因此每个存储单元中都会有一个地址指向域,这个地址指向域指明其后继元素的位置。在链表中存储数据的单元称为结点(Node),从图中可以看出一个结点至少包含了数据域和地址域,其中数据域用于存储数据,而地址域用于存储前驱或后继元素的地址。前面我们说过链表的插入和删除都相当便捷,这是由于链表中的结点的存储空间是在插入或者删除过程中动态申请和释放的,不需要预先给单链表分配存储空间的,从而避免了顺序表因存储空间不足需要扩充空间和复制元素的过程,提高了运行效率和存储空间的利用率。

3.2 单链表的储结构实现分析

到此我们已初步了解了链表的概念和存储结构,接下来,开始分析链表的实现,这里先从单链表入手。同样地,先来定义一个顶级的链表接口:ILinkedList和存储数据的结点类Node,该类是代表一个最基本的存储单元,Node代码如下:

/**

* Created by zejian on 2016/10/21.

* 单向链表节点

*/

publicclassNode {

public T data;//数据域

publicNode next;//地址域

publicNode(T data){

this.data=data;

}

publicNode(T data,Node next){

this.data=data;

this.next=next;

}

}

接着顶级的链表接口ILinkedList,该接口声明了我们所有需要实现的方法。

/**

* Created by zejian on 2016/10/21.

* 链表顶级接口

*/

publicinterfaceILinkedList {

/**

* 判断链表是否为空

* @return

*/

boolean isEmpty();

/**

* 链表长度

* @return

*/

int length();

/**

* 获取元素

* @param index

* @return

*/

T get(int index);

/**

* 设置某个结点的的值

* @param index

* @param data

* @return

*/

T set(int index, T data);

/**

* 根据index添加结点

* @param index

* @param data

* @return

*/

boolean add(int index, T data);

/**

* 添加结点

* @param data

* @return

*/

boolean add(T data);

/**

* 根据index移除结点

* @param index

* @return

*/

T remove(int index);

/**

* 根据data移除结点

* @param data

* @return

*/

boolean removeAll(T data);

/**

* 清空链表

*/

void clear();

/**

* 是否包含data结点

* @param data

* @return

*/

boolean contains(T data);

/**

* 输出格式

* @return

*/

String toString();

}

创建一个单链表SingleILinkedList并实现ILinkedList接口,覆盖其所有方法,声明一个单链表的头结点head,代表链表的开始位置,如下:

publicclassSingleILinkedListimplementsILinkedList {

protectedNode headNode; //带数据头结点

publicSingleILinkedList(Node head) {

this.headNode = head;

}

//其他代码先省略

.....

}

  • boolean isEmpty()实现分析

    需要判断链表是否为空的依据是头结点head是否为null,当head=null时链表即为空链表,因此我们只需判断头结点是否为空即可,isEmpty方法实现如下:

/**

* 判断链表是否为空

* @return

*/

@Override

publicboolean isEmpty() {

returnthis.head==null;

}

  • int length()实现分析 由于单链表的结点数就是其长度,因此我们只要遍历整个链表并获取结点的数量即可获取到链表的长度。遍历链表需要从头结点HeadNode开始,为了不改变头结点的存储单元,声明变量p指向当前头结点和局部变量length,然后p从头结点开始访问,沿着next地址链到达后继结点,逐个访问,直到最后一个结点,每经过一个结点length就加一,最后length的大小就是链表的大小。实现代码如下:

  1. @Override

  2. publicint length() {

  3. int length=0;//标记长度的变量

  4. Node p=head;//变量p指向头结点

  5. while(p!=null){

  6. length++;

  7. p=p.next;//后继结点赋值给p,继续访问

  8. }

  9. return length;

  10. }

T get(int index)实现分析 在单链表中获取某个元素的值是一种比较费时间的操作,需要从头结点开始遍历直至传入值index指向的位置,其中需要注意的是index是从0开始计算,也就是说传递的index=3时,查找的是链表中第4个位置的值。其查询获取值的过程如下图所示:

代码实现如下:

/**

* 根据index索引获取值

* @param index 下标值起始值为0

* @return

*/

@Override

public T get(int index) {

if(this.head!=null&&index>=0){

int count=0;

Node p=this.head;

//找到对应索引的结点

while(p!=null&&count

p=p.next;

count++;

}

if(p!=null){

return p.data;

}

}

returnnull;

}

通过上图和代码,我们就可以很容易理解链表中取值操作的整个过程了。

  • T set(int index, T data)实现分析 根据传递的index查找某个值并替换其值为data,其实现过程的原理跟get(int index)是基本一样的,先找到对应值所在的位置然后删除即可,不清晰可以看看前面的get方法的图解,这里直接给出代码实现:

/**

* 根据索引替换对应结点的data

* @param index 下标从0开始

* @param data

* @return 返回旧值

*/

@Override

public T set(int index, T data) {

if(this.head!=null&&index>=0&&data!=null){

Node pre=this.head;

int count=0;

//查找需要替换的结点

while(pre!=null&&count

pre=pre.next;

count++;

}

//不为空直接替换

if(pre!=null){

T oldData=pre.data;

pre.data=data;//设置新值

return oldData;

}

}

returnnull;

}

  • add(int index, T data)实现分析

    单链表的插入操作分四种情况:a.空表插入一个新结点,插语句如下:

head=newNode(x,null);

b.在链表的表头插入一个新结点(即链表的开始处),此时表头head!=null,因此head后继指针next应该指向新插入结点p,而p的后继指针应该指向head原来的结点,代码如下:

//创建新结点

Node p =newNode(x,null);

//p的后继指针指向head原来的结点

p.next=head;

//更新head

head=p;

以上代码可以合并为如下代码:

//创建新结点,其后继为head原来的结点,head的新指向为新结点

head=newNode(x,head);

执行过程如下图:

c.在链表的中间插入一个新结点p,需要先找到给定插入位置的前一个结点,假设该结点为front,然后改变front的后继指向为新结点p,同时更新新结点p的后继指向为front原来的后继结点,即front.next,其执行过程如下图所示:

代码实现如下:

//新结点p

Node p =newNode(x,null);

//更新p的后继指向

p.next=front.next;

//更新front的后继指向

front.next=p;

以上三句代码合并为一句简洁代码:

front.next=newNode(x,front.next);

d.在链表的表尾插入一个新结点(链表的结尾)在尾部插入时,同样需要查找到插入结点P的前一个位置的结点front(假设为front),该结点front为尾部结点,更改尾部结点的next指针指向新结点P,新结点P的后继指针设置为null,执行过程如下:

其代码语句如下:

//front的next指针指向新结点,新结点的next指针设置为null

front.next=newNode(x,null);

到此我们也就可以发现单向链表中的中间插入和尾部插入其实可以合并为一种情况。最后这里给出该方法整体代码实现,从代码实现上来看中间插入和尾部插入确实也视为同种情况处理了。

  1. /**

  2. * 根据下标添加结点

  3. * 1.头部插入

  4. * 2.中间插入

  5. * 3.末尾插入

  6. * @param index 下标值从0开始

  7. * @param data

  8. * @return

  9. */

  10. @Override

  11. publicboolean add(int index, T data) {

  12. if(data==null){

  13. returnfalse;

  14. }

  15. //在头部插入

  16. if(this.head==null||index<=1){

  17. this.head = newNode(data, this.head);

  18. }else{

  19. //在尾部或中间插入

  20. int count=0;

  21. Node front=this.head;

  22. //找到要插入结点位置的前一个结点

  23. while(front.next!=null&&count1){

  24. front=front.next;

  25. count++;

  26. }

  27. //尾部添加和中间插入属于同种情况,毕竟当front为尾部结点时front.next=null

  28. front.next=newNode(data,front.next);

  29. }

  30. returntrue;

  31. }

  • T remove(int index) 删除结点实现分析 在单向链表中,根据传递index位置删除结点的操作分3种情况,并且删除后返回被删除结点的数据:a.删除链表头部(第一个)结点,此时需要删除头部head指向的结点,并更新head的结点指向,执行图示如下:

代码实现如下:

//头部删除,更新head指向

head=head.next;

b.删除链表的中间结点,与添加是同样的道理,需要先找到要删除结点r(假设要删除的结点为r)位置的前一个结点front(假设为front),然后把front.next指向r.next即要删除结点的下一个结点,执行过程如下:

代码语句如下:

Node r =front.next;

//更新结点指针指向

front.next=r.next;

r=null;

c.删除链表的最后一个结点,通过遍历操作找到最后一个结点r的前一个结点front,并把front.next设置为null,即可。执行过程如下:

代码如下:

front.next=null;

r=null;

我们把中间删除和尾部删除合并为如下代码:

Node r =front.next;

//如果不是尾部结点,更新结点front指针指向

if(r=!null){

front.next=r.next;

r=null;

}

该方法整体代码实现如下:

/**

* 根据索引删除结点

* @param index

* @return

*/

@Override

public T remove(int index) {

T old=null;

if(this.head!=null&&index>=0){

//直接删除的是头结点

if(index==0){

old=this.head.data;

this.head=this.head.next;

}else{

Node front = this.head;

int count = 0;

//查找需要删除结点的前一个结点

while(front.next != null&& count < index - 1) {

front = front.next;

count++;

}

//获取到要删除的结点

Node r = front.next;

if( r!= null) {

//获取旧值

old =r.data;

//更改指针指向

front.next=r.next;

//释放

r=null;

}

}

}

return old;

}

当然还有如下更简洁的代码写法:

@Override

public T remove(int index) {

T old=null;

if(this.head!=null&&index>=0){

//直接删除的是头结点

if(index==0){

old=this.head.data;

this.head=this.head.next;

}else{

Node front = this.head;

int count = 0;

//查找需要删除结点的前一个结点

while(front.next != null&& count < index - 1) {

front = front.next;

count++;

}

if( front.next!= null) {

//获取旧值

old =front.next.data;

//更改指针指向

front.next=front.next.next;

}

}

}

return old;

}

  • void clear() 实现分析 清空链表是一件非常简单的事,只需让head=null即可;代码如下:

/**

* 清空链表

*/

@Override

publicvoid clear() {

this.head=null;

}

ok~,到此单链表主要的添加、删除、获取值、设置替换值、获取长度等方法已分析完毕

原文:https://blog.csdn.net/javazejian/article/details/52953190

以上,便是今天的分享,希望大家喜欢,觉得内容不错的,欢迎点击「在看」支持,谢谢各位

喜欢文章,点个在看 

java 链表_java数据结构与算法之顺序表与链表深入分析(一)相关推荐

  1. java数据结构与算法之顺序表与链表深入分析

    转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52953190 出自[zejian的博客] 关联文章: java数据结 ...

  2. 数据结构与算法:顺序表和链表的常用操作 (很全,基本涵盖本章所有算法)

    文章目录 前言: 一.顺序表 二.链表 前言: 数据结构中的表操作算是里面较为简单的章节,但也比较重要,3月份学完的此章节,左思右想今天还是写篇 关于顺序表和链表的博客. ps : 复制代码可以在编译 ...

  3. 黑马程序员 C语言数据结构与算法之线性表(链表/栈/队列/顺序表)

    C语言 链表基础知识清晰讲解(黑马) 讲的蛮好,就是音质不太好,有时听不清讲的啥! [黑马]数据结构与算法之线性表(链表/栈/队列/顺序表)[配套源码 嘛蛋,看错了,这是java的... 文章目录 链 ...

  4. java 二维链表_Java数据结构与算法----数组与链表

    数据类型 1 数据类型介绍 数据类型的分类(按照结构划分):线性结构和非线性结构 线性结构:线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系 线性结构有两种不同的存储结构,即顺序 ...

  5. 【数据结构与算法】顺序表V3.0的Java实现

    更新说明 经过了顺序表V1.0的根据接口编写成型.顺序表V2.0的功能大幅度增强,这里推出了顺序表V3.0,功能的丰富性不及V2.0,但加入了迭代器,代码的编写也更加的合理了,应该说是比较好的作品了. ...

  6. java数据接口之链表_Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在'点"中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LI ...

  7. Python数据结构与算法(四)--顺序表

    顺序表 在程序中,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等.一组数据中包含的元素个数可能发生变化(可以增加或删除元素). 根 ...

  8. (PTA数据结构与算法) 6-2 顺序表操作集 ——请告诉我什么是顺序表!!!

    6-2 顺序表操作集 (20 分) 本题要求实现顺序表的操作集. 函数接口定义: List MakeEmpty(); Position Find( List L, ElementType X ); b ...

  9. php算法结构,PHP数据结构与算法:顺序表

    一.概念 把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里,元素间的顺序关系由它们的存储顺序自然表示. 逻辑顺序与物理顺序一致 元素之间的关系以元素在计算机内的"物理位置相邻&qu ...

  10. 数据结构与算法笔记——顺序表(数组)与矩阵

    目录 前言 一.顺序表的结构 二.顺序表的初始化 三.基本操作 1.插入数据 2.删除数据 3.查找数据 总结 前言 顺序表是线性表的顺序存储结构的实现,基本操作有顺序表的初始化.插入.删除和查询. ...

最新文章

  1. Android app 启动页尺寸大小 忘记了怎么办
  2. java 文件md5校验_Java 获取 文件md5校验码
  3. Windoes上安装(升级)虚拟化VMware Workstattion Pro软件、序列码激活软件
  4. USTC English Club Note20171020(3)
  5. AngularJS的学习--$on、$emit和$broadcast的使用
  6. 前端学习(3257):react中添加todolist
  7. Linux 命令(117)—— gzip 命令
  8. 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑
  9. 微信小程序tap事件中target与currentTarget的区别
  10. 开启运维自动化架构师成长之路
  11. 实体关系图(ER图)
  12. 光遇测试服要用什么更新软件,光遇测试服最新版
  13. Macbook 苹果电脑 安装搭建Vue脚手架教程m1芯片Vue脚手架初学
  14. (论文加源码)基于DEAP的脑电情绪识别(CNN,RNN和两种不同的注意力机制)
  15. springboot微信公众号发送模板消息
  16. Linux下修复U盘坏块,DiskGenius修复U盘坏块问题巧借
  17. RK3308实现usb webcamera功能
  18. 刚拿到PMP证书, 想吐槽一下, PMP的考证真的值得考吗?
  19. CRect::SetRect说明
  20. 易语言教程数组删除成员和删除指定成员

热门文章

  1. PaddlePaddle(2)——数据获取与处理(以CV任务为主)
  2. Python拷贝(深拷贝deepcopy与浅拷贝copy)
  3. ubuntu下mysql数据库存储路径修改
  4. RT-Thread 4.0 + STM32F407 学习笔记1
  5. oracle in和exist的区别 not in 和not exist的区别
  6. 网上的tensorflow和cnn教程汇总
  7. 淘宝TDDL深入浅出
  8. numpy中int类型与python中的int
  9. HDU 1269 裸奔的强联通分量
  10. Linux系统下Lighttpd的安置设置-3