数据结构之线性表
来源:互联网 发布:any do 软件 编辑:程序博客网 时间:2024/06/09 20:13
数据结构之线性表
理解了数据结构其实就是讨论数据之间的关系,知道算法是什么,如何估算程序的时间复杂度,以及什么是抽象数据类型之后,就可以开始学习前人为我们总结出来的这些典型的数据结构了。
现实生活中的排队就是一个典型的线性表的案例,线性表的抽象数据类型如下:
ADT 线性表 (List)Data 线性表的数据对象集合用数学方法可以这样表示{a1,a2a3,...an,},其中除了第一个元素没有前驱元素,最后一个元素没有后驱元素,内一个元素都是有且只有一个前驱元素和后驱元素。数据元素之间的关系是一对一的关系。(其实这么一大段话就是用文字把线性表这个概念描述出来而已,心里只要想着排队就可以了)OperationInitList(); //初始化操作,建立一个新的线性表 LListIsEmpty(); //判断线性表是否为空ClearList(); //清空线性表GetElem(); //获得 L 中第 i 个元素的值,并返回给 e LocateElem(); //判断 L 中是否有和 e 相同的元素,有就返回1,没有返回0ListInsert(); //在第 i 个位置插入元素 eListDelete(); //删除第 i 个元素,并把值放到 e 中ListLength(); //获得 L 的长度endADT
一般抽象数据类型中定义的是关于这个数学模型最基本的操作,其他复杂的操作都可以由这些基本操作组合而成。
线性表中的顺序存储结构
这里指的是线性表的物理结构,也就是在内存中的存储结构。顺序结构就是在内存中划出一个指定大小的内存块,用来存储数据。让这些数据不只是在逻辑上相邻,在物理存储上也是相邻的。
那实现顺序存储结构的代码怎么写呢?下面就是啦,当然是用最爱的 Java 写的啦。
public class ArrayList1<E> { private static final int defaultSize = 20; private int length;//当前长度 private int size;//线性表大小 private Object data[]; public ArrayList1(){ InitList(defaultSize); } public ArrayList1(int size){ InitList(size); } //初始化线性表 private void InitList(int size){ this.size = size; data = new Object[size]; } //判断表示否为空 protected boolean ListIsEmpty(){ if(this.length == 0){ return true; } return false; } /*清空线性表,表面上是把所有数据清空,其实是让程序认定这没有数据。 也就是把 length 置为零*/ protected void ClearList(){ this.length = 0; } //返回顺序表中第 i 个位置的元素, //时间复杂度为 O(1) protected E GetElem(int i){ if(i <= 0 || i > this.length){ return null; } return (E) this.data[i-1]; } //在第 i 个位置插入元素 e,没写好 //时间复杂度为 O(n) protected boolean ListInsert(int i,E e){ if(i <= 0 || i > this.size){ return false; }else if(i == this.length + 1){ this.data[i-1] = e; this.length++; return true; }else if(i > this.length + 1 && i <= this.size){ this.data[this.length] = e; this.length++; return true; } for(int j = (this.length - i + 1);j > 0;j--){ this.data[j + 1] = this.data[j]; } this.data[i-1] = e; this.length++; return true; } //在第 i 个位置删除元素 //时间复杂度为 O(n) protected boolean ListDelete(int i){ if(i <= 0 || i > this.length){ return false; } for(int j = i;j < this.length;j++){ this.data[j-1] = this.data[j]; } this.length--; return true; } //获得线性表长度 protected int ListLength(){ return this.length; } //获得线性表长度 protected int ListSize(){ return this.size; } //判断是否有和指定元素相同的,有,则返回它所在位置序号,没有则返回 -1 protected int LocateElem(E e){ for(int i = 0;i < this.length;i++){ if(e.equals(this.data[i])){ return i+1; } } return -1; }}
通过时间复杂度的推导,可以知道顺序表中插入和删除的时间复杂度为 O(n),查询的时间复杂度为 O(1)。
由此可得出,顺序表优点是查询快,缺点是添加、删除慢。
线性表中的链式存储结构
由于顺序存储结构在插入和删除时,时间复杂度过大,然后科学家们就想出了链式存储的方式来解决这个问题。
链式存储不强制采用一块地址连续的空间来存储数据,可以连续,也可以不连续,只要保证他在逻辑上是相邻的就可以了。
实现的方式是每一个通过结点来存储数据,以及下一个结点所在的地址。如下图所示,
具体实现代码如下:(这里讨论的是有头结点的单链表)
public class LinkedList1<E>{ private class Node{//结点 private E e;//数据域 private Node next;//指针域 public Node(E e){ this.e = e; this.next = null; } public Node() { } public E getE() { return e; } public void setE(E e) { this.e = e; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } } private Node head;//头结点 private int size;//链表大小 public LinkedList1(){ head = new Node(); this.size = 0; } //添加到后面 protected boolean add(E e){ Node n = new Node(e); if(this.size == 0){ head.setNext(n); }else{ Node node = get(this.size); node.setNext(n); } this.size++; return true; } //返回指定位置的结点 protected E getElem(int position){ if(this.size == 0 || position <= 0 || position > this.size){ return null; } Node node = get(position); return node.getE(); } //判断是否为空 protected boolean ListIsEmpty(){ if(this.size == 0){ return true; }else{ return false; } } //得到大小 protected int ListLength(){ return this.size; } //清空 protected void ClearList(){ this.head.setNext(null); this.size = 0; } //删除某个位置的元素 // head a b c d e 5 /2 protected boolean ListDelete(int p){ if(p <= 0 || p > this.size){ return false; } Node node = get(p); Node nodeBefore = get(p-1); nodeBefore.setNext(node.getNext()); this.size--; return true; } //在某个位置上插入元素 //head a b c d e 5 3 protected boolean ListInsert(E e,int p){ if(p <= 0 || p > this.size + 1){ return false; } if(p == this.size + 1){ add(e); return true; } Node newNode = new Node(e); Node node = get(p-1); newNode.setNext(node.getNext()); node.setNext(newNode); this.size++; return true; } //这个方法的时间复杂度还是 O(n) private Node get(int p){ if(p == 0){ return head; } Node node = head; for(int i = 0;i<p;i++){ node = node.getNext(); } return node; }
链表还有几种其他变形,静态链表,循环链表,双向链表等,其实本事上都差不多。
静态链表,就是利用一个数组来存储结点,结点包含了数据域和指针域。
循环链表,就是尾结点的指针域不再为空,而是指向头结点,构成循环。
双向链表,就是一个结点包含一个数据域和两个指针域,分别用来指向后一个结点和前一个节点。
总之,实际开发中,利用它们之间的特性,选择合适的数据结构完成上一层次的需求,就可以了。
补充,上述对于链表的实现其实写的不对,上面 get() 方法的时间复杂度是O(n),而插入和删除都使用了这个方法,导致时间复杂度也是 O(n) ,而不是链表本来的 O(1) ,这里的确写错了。
正确的写法应该是加上一个尾指针,专门用来指向末尾结点,至少可以保证直接在末尾添加时时间复杂度是 O(1)
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- 数据结构之线性表
- python——图片爬虫:爬取爱女神网站(www.znzhi.net)上的妹子图 基础篇
- 两个List<model>取差集
- Play framework如何使用play控制台
- MySQL技术内幕系列 电子书 作者: 姜承尧
- 关于ad10相关问题
- 数据结构之线性表
- 在ubuntu利用nginx反响代理在一个端口下配置多个站点
- listview滚动
- keras画图(可视化)中遇到的问题以及解决办法
- “玲珑杯”ACM比赛 Round #18 ABC题解
- 网络端口分类调研
- BZOJ1758 [WC2010]重建计划
- 纯CSS3小沙漏
- python实现 模糊C均值聚类算法(Fuzzy-C-Means)-基于iris数据集