数组

先由一个例子复习一下数组的操作:

class  HighArray
{private long[] a;private  int nElems;//-----------------------------------public HighArray(int max)   //构造函数{a=new long[max];nElems = 0;}//-----------------------------------public boolean find(long searchKey)   //查找元素{int j;for(j=0;j<nElems;j++)if(a[j]==searcheKey)break;   //退出for循环,记录了j的值if(j==nElems)   return false;   //没找到elsereturn true;   //找到了}//--------------------------------------public void insert(long value)   //插入元素{a[nElems]=value;nElems++;} //--------------------------------------public boolean delete(long value)   //删除元素{int j;for(j=0;j<nElems;j++)if(value=a[j])break;if(j=nElems)return false;   //不存在那个元素else{for(int k=j;k<nElesm;k++)a[k]=a[k+1];   //遍历,从查找的那个位置起,把数组中的元素向前挪动一位nElems--;   //总长度减一return true;}}   //end  delete()//----------------------------------------public void display(){for(int j=0;j<nElems;j++)System.out.println(a[j]+" ");System.out.rpintln("");}
}

接着在客户端始测试 数组的 增 删 查 操作

class  HighArrayApp
{public static void main(String[] args){int maxSize=100;HighArray arr;arr= new HightArray(maxSize);arr.insert(57);arr.insert(27);arr.insert(17);arr.insert(67);arr.insert(87);arr.insert(33);arr.insert(22);arr.insert(11);arr.insert(66);arr.insert(66);int searchKey=35;if(arr.find(searchKey))System.out.println("Found"+searchKey);elseSystem.out.println("Can't found"+searchKey);arr.delete(55); arr.delete(66);arr.display();}
}

上例演示了一个数组增删查的过程,它的特点是高度的抽象,操作“增删改”都被封装到 HightArray类中,并且暴露出接口(insert(),delete(),display())供用户使用,这里的用户就是HightArrayApp,它只需调用方法完成操。

接着复习一下数组的二分查找

二分查找适用于有序数组,关于排序我在上一节博文有写,那么今天在这里默认数组有序的情况学习一下二分查找把

class  OrdArray
{private long[] a;private  int nElems;//-----------------------------------public HighArray(int max){a=new long[max];nElems = 0;}//-----------------------------------public int size(){return nElems}//-----------------------------------public int find(long searchKey)   //查找元素{int lowerBound=0;int upperBound=nElems-1;int curIn;while(true){curIn=(lowerBound+upperBound)/2;if(a[curIn]==searchKey)return curIn;else if(lowerBound>upperBound)return nElems;else{if(a[curIn]<searchKey)lowerBound=curIn+1;elseupperBound=curIn-1;}}}//--------------------------------------public void insert(long value)   //插入元素{int j;for(j=0;j<nElmes;j++)if(a[j]>value);break;for(int k=nElems;k>j;k--)a[k]=a[k-1];a[j]=value;nElems++;} //--------------------------------------public boolean delete(long value)   //删除元素{int j;for(j=0;j<nElems;j++)if(value=a[j])break;if(j=nElems)return false;   //不存在那个元素else{for(int k=j;k<nElesm;k++)a[k]=a[k+1];   //遍历,从查找的那个位置起,把数组中的元素向前挪动一位nElems--;   //总长度减一return true;}}   //end  delete()//----------------------------------------public void display(){for(int j=0;j<nElems;j++)System.out.println(a[j]+" ");System.out.rpintln("");}
}

接着在客户端测试 数组的 增 删 查 操作

class  HighArrayApp
{public static void main(String[] args){int maxSize=100;HighArray arr;arr= new HightArray(maxSize);arr.insert(57);arr.insert(27);arr.insert(17);arr.insert(67);arr.insert(87);arr.insert(33);arr.insert(22);arr.insert(11);arr.insert(66);arr.insert(66);int searchKey=35;if(arr.find(searchKey)!=arr.size())System.out.println("Found"+searchKey);elseSystem.out.println("Can't found"+searchKey);arr.display();arr.delete(55); arr.delete(66);arr.display();}
}

数组的问题

  1. 在一个无序数组中插入只需要O(1)的时间,查找却需要O(N)
  2. 在一个有序数组中查找只需要O(logN),但插入却需要O(N)
  3. 不论是有序还是无序,删除操作,都需要花费O(N)
  4. 一旦数组被创建,大小就被固定住,数组初始化的大小不容易控制

链表

既然数组作为数据存储结构有一定的缺陷,那么接下来将介绍一种新的数据存储结构:链表。

链结点

在链表中,每个数据项都obeisance包含在链结点Link中。一个链结点是某个类的对象,这个类就叫做Link,因为一个链表中有许多类似的链结点,所以需要使用不同于链表的类来表达链结点。

每个Link对象都包含对下一个链结点引用的字段,通常叫做next。

但是链表对象包含有对第一个链结点的引用。

关系而非位置

数组是根据下标号直接访问,在链表中,寻找一个特定元素的唯一方法就是沿着这个元素链从头开始一直向下寻找。从第一项开始,刀第二个,然后到第三个。


单链表

这个链表仅有的操作是:

  1. 在链表头插入一个数据线
  2. 在链表头删除一个数据线
  3. 遍历链表显示它的内容

下面我们来看一下代码描述:

Link类 是链结点的抽象描述

class Link
{public int iData;//假设Link中的数据是int和double,实际有可能是Object对象public double dData;public Link next;   //这里看上去很突兀,但实际是一个链结点的引用,不是"Link中包含了一个Link"//------------------------------public Link(int id,double dd){iData=id;dData=dd;}//-------------------------------public void display(){System.out.print("{"+iData+","+dData+"}");}
}   //end class Link

LinkList是链表的抽象

class LinkList
{private Link first;   //头结点的引用//-------------------------------public LinkList(){first=null;}//-------------------------------public void insertFirst(int id,double dd){//插入新结点的过程://first已经指向了链表的第一个结点,插入新的结点://1.创建的newLink结点的next等于first;//2.然后改变first的值,使得first指向新创建的链结点Link newLink =new Link(id,dd);newLink.next=first;first=newLink;}//-------------------------------public Link deletefirst(){//通过把first重新指向第二个链结点,删除和第一个链结点的链接,记住first是链表的属性,next是链结点的属性Link temp=first;first=first.next;   //如何删除:first-->old nextreturn temp;   //返回删除的链结点Link}//---------------------------------public Lind find(int key)  //查找某个元素{Link current =first;while(current.iData!=key){if(current.next==null)return null;elsecurrent =current.next;}return current;}//--------------------------------public Link delete(int key)   //删除某个元素{Link current =frist;Link previous=first;while(current.iData!=key){if(current.next==null)return null;else{previous=current;current=current.next;}}if(current==first)first=first.next;elseprevious.next=current.next;return current;}//---------------------------------public void displayList()   //输出{System.out.print("List(first-->last): ");Link current=first;while(current!=null){current.displayLink();current=current.next;}System.out.println(" ");}
}   //end class LinkList

LinkListApp是测试链表和链结点的客户端

class LinkListApp
{public static void  main(String[] args){LinkList theList=new LinkList();//创建新的链表theList.insertFirst(22,2.33);theList.insertFirst(44,4.33);theList.insertFirst(55,3.33);theList.insertFirst(88,34.33);theList.displayList();Link f=theList.find(44);if(f!=null)System.out.println("Found Link with key "+f.iData);elseSystemm.out.println("Can't find link");Link d=theList.delete(88);theList.displayList();while(!theList.isEmpety()){Link aLink=theList.deleteFirst();System.out.print("Deleted");aLink.displayLink();System.out.println(" ");} theList.displayList();}   //end main()
}

双端链表

双端链表与单链表的唯一区别是,增加了对最后一个链结点的引用。

这样就允许在表尾插入一个链结点。
当然我们也可以遍历整个链表直到表尾再插入。显然双端链表这样在末尾插入链结点效率更高。

有序链表

有序链表优于有序数组的地方就是插入的速度,因为链表的插入是不需要移动元素的,链表也可以扩展内存,数组的内存是固定的。

有序链表的缺点就是实现起来稍微复杂。

双向链表

双向链表的优点 就是反向遍历非常简单。

为什么?

因为双向链表的每个链结点都有两个属性,分别指向前一个结点和指向后一个结点。

关于有序链表和双向链表的代码叙述有兴趣的可以自行查阅

链表的效率

在表头插入和删除速度很快,花费O(1);
查找,删除和在指定链结点后面插入都需要搜索表中一半以上的链结点,需要O(N)次比较,虽然数组执行这些操作演示O(N)次比较,但是链表仍然要快一些,因为插入删除链结点时,链表不需要移动,增加的效率是显著的,特别是复制实际远远大于比较实际的时候。

链表比数组的优越性还体现在:链表需要多少内存就可以用多少内存,并且可以扩展。

数组太大导致效率低下,数组太小,使用的时候有可能空指针;向量是可扩展的数组,它改变长度的方式是增量扩展,扩大一倍。内存效率上比链表低的多,


ADT 抽象数据类型

接下来讨论一个比链表更广泛的话题:抽象数据类型

简单来说,是一种考虑数据结构的方式:着重于它做了什么,而忽略它 是怎么做的。

栈和队列都是ADT的例子,数组和链表都可以实现它们。

用链表实现栈
栈的push()和pop()操作实际是通过数组操作完成的
arr[++top]=data;
data=arr[top- -];

而链表是类似于这样完成:
theList.insertFist(data);
data=theList.deleteFirst();

栈的使用者调用push()和pop()方法来插入和删除栈中的元素,它们不需要知道栈是用链表还是数组实现的

class Link
{public long dData;public Link next;//--------------------------public Link(long dd){dData=dd;}//--------------------------public void displayLink(){System.out.print(dData+" ");}
}   //end class Link
class LinkList{private Link first;   //头结点的引用//--------------------------------------public LinkList(){first=null;}//--------------------------------------public boolean isEmpty(){return fisrt==null;}//-------------------------------------public void insertFirst(long dd){Link newLink=new Link(dd);newLink.next=first;first=newLink;}//--------------------------------------pubic long deleteFirst(){Link temp=first;first=first.next;return temp.dData;}//-------------------------------------public voi displayList(){Link current =first;while(current!=null){current.displayLink();current=current.next;}System.out.println("");}
}   //end class
class Stack
{private LinkList theList;//------------------------------public Stack(){theList=new LinkList();}//------------------------------public void push(long i){theList.insertFirst(j);}//------------------------------public void pop(){return theList.deleteFirst();}//------------------------------public boolean isEmpty(){return theList.isEmpty();}//-------------------------------public void displayStack(){System.out.print("Stack(top-->bottom):");theList.displayList();}
}   //end class
class LinkStackApp
{public static void main(String[] args){LinkStack theStack=new LinkStack();theStack.push(20);theStack.push(40);theStack.displayStack();theStack.push(60);theStack.push(80);theStack.displayStack();theStack.pop();theStack.pop();theStack.displayStack();}   //end  main()
}   //end class

观察:整个程序,LinkStackApp中的main方法只和LinkStack类有关,LinkStack类只和LinkList类有关。main()和LinkList类是不能通信的。

数据类型和抽象

抽象数据类型分为两步,首先看看“数据类型”再考虑“抽象”

数据类型可以表示内置的类型,比如int,double,也可以用类来创建自己的数据类型。

抽象这个词的意思是“不考虑细节的描述和实现”,抽象是事物的本质和重要特征。

当“抽象数据类型”用于描述栈和队列这样的数据额结构时,它的意义被进一步扩展了。它意味着类的用户不知道方法是怎样运作的,也不知道数据是如何存储的。

对于栈来说,用户只知道push()和pop()方法的存在是被用户使用的,但不需要知道内部的实现,以及内部数据是如何存储。

接口,在ADT中有一个经常被称为“接口”的规范,同城是类的公有方法,在栈中,push() 和pop()就形成了接口。

详解数组,链表和ADT相关推荐

  1. 线性表详解(静态链表、单链表、双向链表、循环链表)

    目录 申明 1. 线性表的定义 2. 线性表的抽象数据类型 3. 线性表的顺序存储结构 3. 1 顺序存储定义 3. 2 顺序存储方式 3. 3 数据长度与线性表长度区别 3. 4 地址计算方法 4. ...

  2. 不带头节点的链表有哪些缺点_23张图!万字详解「链表」,从小白到大佬!

    链表和数组是数据类型中两个重要又常用的基础数据类型. 数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解 ...

  3. watch深度监听数组_vue watch普通监听和深度监听实例详解(数组和对象)

    vue watch普通监听和深度监听实例详解(数组和对象) 下面通过一段代码给大家介绍vue watch的普通监听和深度监听,具体代码如下所示: var vm=new Vue({ data:{ num ...

  4. 23张图!万字详解「链表」,从小白到大佬!

    链表和数组是数据类型中两个重要又常用的基础数据类型. 数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解 ...

  5. java 双向链表_23张图!万字详解「链表」,从小白到大佬

    链表和数组是数据类型中两个重要又常用地基础数据类型,数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解决 ...

  6. C语言详解 - 数组

    1. 使用数组的好处 要计算某班的平均分数,假设该班只有10个学生.利用前面所学知识,可以有如下两种方法: 方法一:利用一个变量存储分数 #include <stdio.h> int ma ...

  7. 详解单链表经典OJ题

    文章目录 前言 一 删除链表中等于给定值"val"的所有节点 二 反转一个单链表 三 求中间节点 四 链表倒数第K个节点 五 合并有序链表 六 链表分割 注意细节: 七 删除重复结 ...

  8. python的ctypes模块详解数组_python ctypes结构数组

    代码中的STRUCT_ARRAY应该是指向指针数组的指针,而不是指向数组中元素的指针,因为您要将STRUCT_2指针附加到数组. 在c中,STRUCT_ARRAY可以定义为* STRUCT_2 [le ...

  9. 链表 java详解_链表详解——Java版

    什么是链表? 链表是一个线性结构,但是存储的数据可以是非线性的.链表由一个个子节点构成,每个节点有两个部分:数据域和指针域,数据域就是实际存储数据的,指针域可以有一个和两个,单链表就是单个指针域指向后 ...

最新文章

  1. Office 365系列之十:批量部署O365客户端
  2. jQuery属性操作
  3. neo4j CQL语句
  4. ubuntu 安装deb程序文件失败的解决方法
  5. 法的详细步骤_空气能热水工程安装步骤讲解
  6. HTML-参考手册: HTML 音频/视频
  7. oracle 同步索引,oracle全文索引之同步和优化索引做了什么
  8. puppet经典应用
  9. Kconfig中select与depends on原理
  10. matlab纹理分析,基于MATLAB的遥感影像纹理特征分析
  11. 刺激战场android免费辅助网盘,刺激战场xs辅助
  12. MySQL系列之日志汇总:redo log、undo log、binlog、errorlog、slow query log、general log、relay log
  13. Windows故障恢复控制台使用方法
  14. cppm报考条件,看下您符合报考CPPM吗?
  15. 小米开源框架MACE 源码阅读笔记 1
  16. QPM-PHP多进程开发-Supervisor配置参考
  17. Windows上安装Linux
  18. JAVA---冒泡排序
  19. python简单程序实例-python简单项目实例
  20. c语言中哪个键是逐行运行的,c语言中的快捷键有哪些?

热门文章

  1. java 绘制长方形_用java画矩形与正方形
  2. ava Springboot养老院信息管理系统源码
  3. 泵引理 Pumping Lemma
  4. HapiJS开发手册
  5. c语言入门——三子棋(N子棋)
  6. 中科院计算机研究生推免面试,【中科院信工所学生兼职面试】研究生推免面试经验分享。-看准网...
  7. rstudio中johansen协整检验代码
  8. odoo12 物流 自动计算运费 ,采购销售使用不同计量单位自动换算
  9. 十三款流行的无线网络黑客工具介绍
  10. Deep Upsupervised Cardinality Estimation 解读(2019 VLDB)