1.1基本概念
数据
描述客观事物的数值、字符,能输入到计算机并且被计算机处理的各种符号的集合,数据就是信息在计算及中的表示。
数据元素
是数据的基本单位,在计算机程序中,通常把数据元素作为一个整体进行处理。
如:描述学生信息的一条数据记录就是一个 数据元素
数据元素通常由若干的数据项组成。
如:描述学生信息中的姓名,学号,成绩都是数据项
数据对象
一组相同性质的数据元素的集合。
如:学校中所有学生的集合
数据结构
相互之间存在一种或多种特定关系的数据元素的集合,简单来说就是数据元素之间的关系。
数据结构分为逻辑机构和物理结构
逻辑结构:数据之间关系的描述
集合:数据仅仅属于同一个集合,没有其他相互关系。
线性:面熟一对一的关系。
树形:描述一对多的关系。
图形:描述多对多的关系。
物理结构:在计算机中的存储方式
顺序存储:使用一块连续的存储空间存储数据元素,数据之间紧挨在一起,数据的前去与后续的关系可以通过数据元素在内存中的相对位置反映出来
链式存储:数据元素的存储位置不是连续的,每个元素保存下个元素的存储位置
1.2抽象数据类型
数据类型
一组性质相同的数据的集合以及该数据集合上操作的总称
抽象数据类型
abstract data type 简称ADT,由一组数据模型以及该模型上的一组操作组成
1.3算法以及性能分析
1.3.1算法
算法是为了解决一个特定问题而规定的一系列的操作,是一组有序的指令集合。
数据结构与算法是一对基友。
算法的五个特性:
输入:一个算法有0或多个输入
输出:至少有一个输出,没有输出的算法没有意义
可行性:算法能够实现,并且在有限的时间内完成
确定性:对于特定的合法输入它的输出应该是唯一的
有穷性:算法中执行指令的个数应该是有限的,执行又穷的步骤后能结束
算法的设计要求:
1.3.2时间复杂度
1.3.3算法时间复杂度分析
算法2:
T(n ) = 1+n+1 = O(n),线性阶
各种函数的增长率
1.3.4空间复杂度
为了解决某一问题,正在执行期间所需要的空间大小,不包含存储输入所包含的时间。
记做: S(n) = O(f(n))
1.4线性表
/** * @author Rose * @Email 510130303@qq.com * @date 2020/8/4 9:27 * 通过接口定义一组线性表中的操作 */ public interface MyList { void insert(int i,Object e); //插入一个索引为i,值为e的元素 int getSize(); //返回线性表中数据个数 int indexOf(Object e); //返回线性表中e的索引值 boolean isEmpty(); //返回线性表是否为空 boolean contains(Object e); //判断线性表中是否包含e这个元素 boolean inserBefore(Object p,Object e); //在线性表元素p前插入元素e boolean inserAfter(Object p,Object e); //在线性表元素p后插入元素e Object remove(Object e); //移除值为元素e,返回该元素 Object remove(int i); //移除索引是i的元素,返回该元素 Object replace(int i,Object e); //使用元素e替换索引为i的元素,并返回旧的元素 Object get(int i); //返回索引为i的元素 }
1.4.1线性表的顺序存储
public class MyArrayList implements MyList { private Object[] elements; private static final int DEFAULT_CAPACITY = 16; //数组默认大小 private Integer size = 0; //保存数据的个数 //默认构造 public MyArrayList(){ elements = new Object[DEFAULT_CAPACITY]; } //有参构造 public MyArrayList(int initalCapaity){ elements = new Object[initalCapaity]; } //插入一个索引为i,值为e的元素 public void insert(int i, Object e) { //判断索引是否越界 if (i < 0 || i > size){ throw new IndexOutOfBoundsException("索引:"+i+"越界"); } //如果数组已满,扩容数组 if (size >= elements.length){ expandSpace(); } //从i开始,把元素向后移 for (int j = size;j > i;j--){ elements[j] = elements[j-1]; } //把元素e存储到i位置 elements[i] = e; //元素个数增加 size++; } //数组扩容 private void expandSpace() { //定义一个更大的数组,默认是原来数组的2倍 Object[] newElements = new Object[elements.length*2]; //Object[] newElements = java.util.Arrays.copyOf(elements,elements.length*2); //将原来的数组元素复制到新数组 for (int i = 0;i < elements.length;i++){ newElements[i] = elements[i]; } //旧数组指向新数组 elements = newElements; } //返回元素个数 public int getSize() { return size; } //返回线性表中第一次出现e的索引值,不存在返回-1 public int indexOf(Object e) { //输入值为null的元素 if (e == null){ for (int i = 0;i < size;i++){ if (e.equals(elements[i])){ return i; } } } //输入值不为null的元素 for (int i = 0;i < size;i++){ if (e.equals(elements[i])){ return i; } } return -1; } //判断线性表是否为空 public boolean isEmpty() { return size == 0; } //判断线性表中是否包含e这个元素 public boolean contains(Object e) { return indexOf(e) >= 0; } //在线性表元素p前插入元素e public boolean inserBefore(Object p, Object e) { int index = indexOf(p); //判断是否有p元素 if (index < 0){ return false; } //从前面插入 insert(index,e); return true; } //在线性表元素p后插入元素e public boolean inserAfter(Object p, Object e) { int index = indexOf(p); //判断是否有p元素 if (index < 0){ return false; } //从后面插入 insert(index+1,e); return true; } //移除值第一个为e的元素,返回该元素 public Object remove(Object e) { //获得e的索引 int index = indexOf(e); if (index < 0){ return null; } return remove(index); } //移除索引是i的元素,返回该元素 public Object remove(int i) { //判断i是否越界 if (i < 0 || i >size){ throw new IndexOutOfBoundsException("索引:"+i+"越界"); } //将要删除的元素保存起来 Object old = elements[i]; //将i+1的元素依次前移 for (int j = i;j < size-1;j++){ elements[j] = elements[j+1]; } //最后的置为null elements[size-1] = null; //修改元素个数 size--; return old; } //使用元素e替换索引为i的元素,并返回旧的元素 public Object replace(int i, Object e) { if (i < 0 || i >= size){ throw new IndexOutOfBoundsException("索引:"+i+"越界"); } //保存旧元素 Object old = elements[i]; //替换 elements[i] = e; //返回旧元素 return old; } //返回索引为i的元素 public Object get(int i) { if (i < 0 || i > size){ throw new IndexOutOfBoundsException("索引:"+i+"越界"); } return elements[i]; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); //遍历已经添加的元素 for (int i = 0;i < size;i++){ sb.append(elements[i]); if (i < size-1){//最后一个元素不加逗号 sb.append(","); } } sb.append("]"); return sb.toString(); } }
特点:
优点:
顺序存储使用的是数组实现的,数组可以通过索引值快速访问每个元素
缺点:在插入/删除元素时候,需要移动大量元素,当线性表长度变化较大的时候,很难确定存储空间的容量
1.4.2线性表的链式存储
单链表
每个存储单元有两个存储域,一个存储数据,另一个存储下一个存储单元的地址。
各个存储单元的存储顺序可以是不连续的。
package 线性表; /** * @author Rose * @Email 510130303@qq.com * @date 2020/8/4 14:33 * 线性表的链式存储,单链表 */ public class MySingleLink implements MyList{ private Node head; //头结点 private int size; //保存元素个数 private class Node{ Object data;//存储数据 Node next; //存储下一个结点引用 public Node(Object data, Node next) { this.data = data; this.next = next; } } @Override public void insert(int i, Object e) { //索引越界 if (i < 0 || i > size){ throw new IndexOutOfBoundsException(i+"越界"); } //创建结点 Node newNode = new Node(e,null); //头结点为null的时候,链表不存在,刚创建结点的就是头结点 if (head == null){ head = newNode; }else { //在0位置插入 if (i == 0){ newNode.next= head; //新结点指向原来的结点 head = newNode; }else { //在其他位置插入,要先找到 i 的前一个结点 i-1 Node pNode = head; for (int j = 1;j < i;j++){ pNode = pNode.next; } //先修改新结点的指针域,在修改i-1结点的指针域 newNode.next = pNode.next; pNode.next = newNode; } } size++; } @Override public int getSize() { return size; } //输入 e 返回第一次出现 e的索引值,如果没有返回-1 @Override public int indexOf(Object e) { int i = 0; Node pNode = head; while (pNode != null){ if (e == null && pNode == null){ return i; }else if(e != null && e.equals(pNode.data)){ return i; } i++; pNode = pNode.next; } return -1; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean contains(Object e) { return indexOf(e) >= 0; } @Override public boolean insertBefore(Object p, Object e) { int index = indexOf(p); if (index < 0){ return false; } insert(index,e); return true; } @Override public boolean insertAfter(Object p, Object e) { int index = indexOf(p); if (index < 0){ return false; } insert(index+1,e); return true; } @Override public Object remove(Object e) { int index = indexOf(e); if (index < 0){ return null; } return remove(index); } @Override public Object remove(int i) { if (i < 0 || i >= size){ throw new IndexOutOfBoundsException(i+"越界"); } //存储删除的数据 Node pNode = head; //删除头结点 if (i == 0){ head = head.next; size--; return pNode.data; } //找 i-1 结点 for (int j = 0;j < i;j++){ pNode = pNode.next; } //保存删除点数据 Object old = pNode.next.data; //修改 i-1 指针,指向 i+1 结点 pNode.next = pNode.next.next; size--; return old; } @Override public Object replace(int i, Object e) { if (i < 0 || i >= size){ throw new IndexOutOfBoundsException(i+"越界"); } Node pNode = getNode(i); Object old = pNode.data; pNode.data = e; return old; } //返回索引为 i 的 对象 private Node getNode(int i) { if (i < 0 || i >= size){ return null; } if (i == 0){ return head; } Node pNode = head; for (int j = 0;j < i;j++){ pNode = pNode.next; } return pNode; } //返回索引为 i 的 值 @Override public Object get(int i) { if (i < 0 || i >= size){ throw new IndexOutOfBoundsException(i+"越界"); } Node pNode = getNode(i); return pNode.data; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); //定义临时指针来取数据 Node pNode = head; //当到了尾结点时为null结束 while (pNode != null){ sb.append(pNode.data); if (pNode.next != null){ sb.append(",") ; } // 指针下移 pNode = pNode.next; } sb.append("]"); return sb.toString(); } }
public class MySingleLinkTest { public static void main(String[] args) { //创建链表 MySingleLink mySingleLink = new MySingleLink(); //判断 System.out.println(mySingleLink.isEmpty()); System.out.println(mySingleLink.getSize()); //插入元素 mySingleLink.insert(0,"沈"); mySingleLink.insert(1,"云"); mySingleLink.insert(2,"飞"); System.out.println(mySingleLink); //查找索引 int index = mySingleLink.indexOf("飞"); System.out.println(index); //删除 System.out.println(mySingleLink.remove("沈")); System.out.println(mySingleLink); //替换 mySingleLink.replace(0,"替换云"); System.out.println(mySingleLink); //获值 System.out.println(mySingleLink.get(1)); //头插,尾插 System.out.println("_______"); mySingleLink.insertBefore("替换云","沈"); mySingleLink.insertAfter("飞","fei"); mySingleLink.replace(1,"云"); System.out.println(mySingleLink); } }
1.4.3线性表的链式存储之 双向链表
在双向链表中,扩展了结点的结构,每个结点除了存储数据结构外,通过一个引用指向后续结点,还有一个应用指向前驱结点。
package 线性表; /** * @author Rose * @Email 510130303@qq.com * @date 2020/8/5 10:03 * 双向链表 */ public class MyLinkedList implements MyList{ private Node first; //指向头结点 private Node last; //指向尾结点 private int size; //保存元素个数 private class Node { Object data; Node next; //指向后续 Node prev; //指向前驱 public Node(Object data, Node next, Node prev) { this.data = data; this.next = next; this.prev = prev; } } //检查索引是否越界 private void checkIndex(int i) { if (i < 0 || i >size){ throw new IndexOutOfBoundsException(i+"越界"); } } //在头部添加 private void addFirst(Object e) { //头结点指向pNode Node pNode = first; //生成一个新结点 Node newNode = new Node(e,first,null); //修改头结点指向新结点 first = newNode; //如果pNode是空值,那么,新结点 即是 头结点 又是 尾结点 if (pNode == null){ last = newNode; }else { //否则,新结点 是pNode结点的前驱 pNode.prev = newNode; } size++; } //在尾部添加 private void addLast(Object e) { //尾结点指向pNode Node pNode = last; //生成一个新结点 Node newNode = new Node(e,null,last); //修改尾结点指向新结点 last = newNode; //如果pNode是空值,那么,尾结点 即是 头结点 又是 尾结点 if (pNode == null){ first = newNode; }else { //否则,新结点 是pNode结点的后续 pNode.next = newNode; } size++; } //返回索引值的结点 private Node getNode(int i) { Node pNode = first; for (int j = 0;j < i;j++){ pNode = pNode.next; } return pNode; } @Override public void insert(int i, Object e) { //检查索引 checkIndex(i); //i==0,在头部插入 if (i == 0){ addFirst(e); } //i==size,在尾部插入 else if (i == size){ addLast(e); } //找到i结点,在i结点前部插入 else { Node pNode = getNode(i); //找到i结点的前驱 Node prevNode = pNode.prev; //创建新结点,前驱是i结点的前驱,后续是i结点 Node newNode = new Node(e,pNode,prevNode); //修改新结点的前驱后续 prevNode.next = newNode; pNode.prev = newNode; size++; } } @Override public int getSize() { return size; } //返回输入元素的索引值,如果没有返回 -1 @Override public int indexOf(Object e) { int i = 0; if (e == null){ for (Node pNode = first;pNode != null;pNode = pNode.next){ if (pNode.data == null){ return i; } i++; } }else { for (Node pNode = first;pNode != null;pNode = pNode.next){ if (e.equals(pNode.data)){ return i; } i++; } } return -1; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean contains(Object e) { int index = indexOf(e); if (index < 0){ return false; } return true; } //元素p前插入元素e @Override public boolean insertBefore(Object p, Object e) { //获得元素 p 的索引值 int index = indexOf(p); if (index < 0 ){ return false; } //插入 insert(index,e); return true; } //元素p后插入元素e @Override public boolean insertAfter(Object p, Object e) { //获得元素 p 的索引值 int index = indexOf(p); if (index < 0 ){ return false; } insert(index+1,e); return true; } @Override public Object remove(Object e) { int index = indexOf(e); if (index < 0){ return null; } return remove(index); } //删除索引为 i 的元素,返回 i 的data @Override public Object remove(int i) { checkIndex(i); //找到索引 i 的结点 Node pNode = getNode(i); //找到 i 的前驱 Node prevNode = pNode.prev; //找到 i 的后继 Node nextNode = pNode.next; //修改前驱后继的指向 //如果前驱是空,则 i 结点是头结点 if (prevNode == null){ //让下一个结点变成头结点 first = nextNode; }else { prevNode.next = nextNode; } //如果后继是空,则 i 结点是尾结点 if (nextNode == null){ //则上上一个结点变成尾结点 last = prevNode; }else { nextNode.prev = prevNode; } size--; //返回删除的值 return pNode.data; } @Override public Object replace(int i, Object e) { //检查索引是否越界 checkIndex(i); //获得索引所指结点 Node pNode = getNode(i); //存储要被替代的值 Object old = pNode.data; //替换 pNode.data = e; return old; } //返回索引所指的结点的data @Override public Object get(int i) { //检查索引 checkIndex(i); //获得索引所指结点 Node node = getNode(i); return node.data; } //删除头结点 public Object removeFirst(){ return remove(0); } //删除尾结点 public Object removeLast(){ return remove(size-1); } //返回头结点 public Object returnFirst(){ return get(0); } //返回尾结点 public Object returnLast(){ return get(size-1); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); for (Node node = first;node != null;node = node.next){ sb.append(node.data); if (node.next != null){ sb.append(","); } } sb.append("]"); return sb.toString(); } }
package 线性表; public class MyLinkedListTest { public static void main(String[] args) { MyLinkedList myLinkedList = new MyLinkedList(); //插入 myLinkedList.insert(0,"沈"); myLinkedList.insert(1,"云"); myLinkedList.insert(2,"飞"); System.out.println(myLinkedList); //包含,返回索引值 boolean contains = myLinkedList.contains(1); int indexOf = myLinkedList.indexOf("云"); System.out.println(contains+","+indexOf); //删除 myLinkedList.remove("沈"); System.out.println(myLinkedList); //获得索引所指data,替换 Object o = myLinkedList.get(0); System.out.println("获得索引所指data:"+o); myLinkedList.replace(0,"替换云"); System.out.println(myLinkedList); //头尾插入 myLinkedList.insertBefore("替换云","沈"); myLinkedList.insertAfter("飞","飞"); System.out.println(myLinkedList); //返回头尾结点 System.out.println("返回头结点:"+myLinkedList.returnFirst()); System.out.println("返回尾结点:"+myLinkedList.returnLast()); //删除头尾结点 myLinkedList.removeFirst(); myLinkedList.removeLast(); System.out.println("删除头尾结点之后:"+myLinkedList); } }