概述
1.为什么要学习数据结构与算法。
算法,是指解决特定问题的方法,或者说操作步骤。学算法是为了学习解决问题的思路。数据结构,是数据的组织方法及属性。学习数据结构是为了会根据问题需要选择合适的数据组织与表示方法。
- 从功利角度来说:数据结构与算法是编程的核心
- 从实际角度来说:对大脑的逻辑思维和抽象思维有显著的提高
- 从长远角度来说:程序=数据结构+算法
2.需要学习到什么程度?
能写:线性表 单链表 循环链表 栈 队列 二叉树 二叉线索树 二叉搜索树 AVL平衡树 最大堆/最小堆 优先队列 字符串
Trie前缀树 线段树 并查集 哈希表 图的郊接矩阵 图的邻接表 图的深度优先遍历 最小生成树 最短路径 排序算法
搜索算法 背包算法 分治算法 回溯算法 动态规划算法 贪心算法
能懂:B树 B+树 红黑树 有权图算法 以及更高级的数据结构和算法
3.数据结构的概述。
1)什么是数据?
就是文字,图片被计算机使用二进制存储。
2)什么是结构?
数据与数据之间的一种或多种特定的关系。
3)什么是数据结构?
数据结构=数据+数据之间的关系。
4)数据结构主要解决什么样的问题?
将零散的数据整齐的划一,数学提供理论基础,数据结构则是实施方案。
5)为什么要这么做?
将看似分散的数据以某种方式联系起来可以从中找到一些规律
-----------机器学习
这个世界是有一定规律的,如何找到规律,全靠数据
----------大数据
6)我们在学习Java中遇到了那些数据结构?
集合。
数组:一堆连续的变量的组成,这些便是数据结构。
7)数据结构的逻辑结构。
是指数据元素之间的相互关系,是我们想象出来的,并没有实质性的将其存储在计算机中。
- 集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系。
- 线性结构:线性结构中的数据元素之问是一对一的关系。
- 树形结构:树形结构中数据元素之间存在一种一对多的层次关系。
- 图形结构:图形结构的数据元素是多对多的关系。
8)数据结构的物理结构。
是指数据的逻辑结构在计算机中的存储形式
- 顺序存储结构:开辟一组连续的空间存储数据
通常用数组来实现,数组中空间木身是连续的,保证了数据之间的关系
- 链式存储结构:开辟一组随机的空间存储数据
通常用节点来实现,节点不仅要存储数据,还要存储下一个节点的位苦以保证数据
9)计算机中有哪些数据结构的应用?
手机通讯录 函数栈 游戏地图
3.算法概述
1)什么是算法?
是解决特定问题求解步骤的描述在计算机中表现为指令的有序序列并且每条指令表示一个或多个操作。说白了,就是求解一 个问题的步骤。
2)如何评价一个算法的好坏?
设计算法要提供程序运行的效率,这里效率大都指算法的执行时间。
- 事后统计算法
- 事前分析估算法
3)算法时间复杂度度的定义。
常数阶O(1)<对数阶O(log n)<线性阶O(n)<O(nlog n)<平方阶O(n^2)<立方阶O(n^3)<O(2^n)<O(n!)<O(n^n)
动态数组
1)Java内置数组的特点。
数组的长度一旦确定则不可更改。
数组只能存储同一类型的数据。
数组中每个存储空间大小一致月地址连续。
数组提供角标的方式访问元素。
2)Java内置数组的潜在问题。
3)动态数组的封装。
把数组的相关属性和相关行为封装在类中。
4)如何封装动态数组
属性方面:
int size 数组的有效元素个数
int capacity 数组的最大容量data.length
E[] data 数组的存储容器
行为方面:
增() 删() 改() 查() 其他()
2.线性表的顺序存储结构
1)线性表的定义
零个或多个数据元素的有限序列。
2)线性表接口List的定义
代码如下:
package com.p1线性表;
/**
* List是线性表的最终父接口
**/
public interface List<E> {
/**
* 获取线性表中元素的个数(线性表的长度)
* @return 线性表中有效元素的个数
* */
public int getSize();
/**
* 判断线性表是否为空
* @return是否为空的布尔类型
* */
public boolean isEmpty();
/**
* 在线性表指定的index角标处添加元素e
* @param index 指定角标0<=index<=size
* @param e 要插入的元素
* */
public void add(int index,E e);
/**
* 在线性表的表头插入一个元素
* @param e 要插入的元素 指定在角标0处
* */
public void addFirst(E e);
/**
* 在线性表的表尾位置插入一个元素
* @param e 要插入的元素 指定在角标size处
* */
public void addLast(E e);
/**
* 在线性表中获取指定index处的元素
* @param index 指定的角标 0<=index<size
* @return 该角标缩对应的元素
* */
public E get(int index);
/**
* 获取线性表中表头的元素
* @return 表头元素 index=0
* */
public E getFirst();
/**
* 获取线性表中表尾的元素
* @return 表尾元素 index=size-1
* */
public E getLast();
/**
* 修改指定index处的元素为e
* @param index 指定的角标
* @param e 新元素
* */
public void set(int index,E e);
/**
* 判断线性表中是否存在要判断的元素e 默认从前往后找
* @param e 要判断的元素
* @return 返回是否存在元素(true false)
* */
public boolean contains(E e);
/**
* 在线性表中获取元素e的角标
* @param e 要查询的数据
* @return 数据在线性表中的角标
* */
public int find(E e);
/**
* 在线性表中查找要删除的角标对应的元素
* @param index 指定的角标0<=index<size
* @return 删除掉的老元素
* */
public E remove(int index);
/**
* 删除线性表中的表头元素
* @preturn 表头元素
* */
public E removeFirst();
/**
* 删除线性表中的表尾元素
* @return 表尾元素
* */
public E removeLase();
/**
* 在线性表中删除指定的元素
* @param e 指定的元素
* */
public void removeElement(E e);
/**
* 清空线性表
* */
public void clear();
}
3)线性表顺序存储结构的定义
指的是用一段地址连续的存储单元依次存储线性表的数据元素。
4)线性表的顺序存储结构ArrayList的定义
代码如下:
package com.oupeng.p1线性表;
/**
* 用顺序存储结构实现的List—顺序线性表-顺序表
* */
public class ArrayList<E> implements List<E> {
//E 相当于Object 多态 任何数据的引用
private static int DEFAULT_SIZE=10;
private E[] data;//存储数据元素的容器
private int size;//线性表的有效元素的个数
//date.length表示线性表的最大容量Capacity
public ArrayList(){
this(DEFAULT_SIZE);
}
/**
* 创建一个容量为指定capacity的一个线性表
* */
public ArrayList(int capacity){
this.data=(E[]) new Object[capacity];
this.size=0;
}
/**
* 将一个数组封装成为一个线性表
* */
public ArrayList(E[] arr){
// TODO
data=(E[]) new Object[arr.length];
for(int i=0;i<data.length;i++){
data[i]=arr[i];
}
size=data.length;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void add(int index, E e) {
if(index<0 || index>size){
throw new ArrayIndexOutOfBoundsException("方法函数角标越界");
}
//判断是否已满
if(size==data.length){
resize(2*data.length);
}
for(int i=size-1;i>=index;i--){
data[i+1]=data[i];
}
data[index]=e;
size++;
}
/**
* 改变date的长度(扩容或者缩容)
* @param 新数组的长度
* */
private void resize(int newLen){
E[] newData=(E[]) new Object[newLen];
for(int i=0;i<size;i++){
newData[i]=data[i];
}
data=newData;
}
@Override
public void addFirst(E e) {
add(0,e);
}
@Override
public void addLast(E e) {
add(size,e);
}
@Override
public E get(int index) {
if(index<0 || index>size-1){
throw new ArrayIndexOutOfBoundsException("get函数角标越界");
}
return data[index];
}
@Override
public E getFirst() {
return get(0);
}
@Override
public E getLast() {
return get(size-1);
}
@Override
public void set(int index, E e) {
if(index<0 || index>size-1){
throw new ArrayIndexOutOfBoundsException("set函数角标越界");
}
data[index]=e;
}
@Override
public boolean contains(E e) {
if(isEmpty()){
return false;
}
for(int i=0;i<size;i++){
if(data[i]==e){
return true;
}
}
return false;
}
@Override
public int find(E e) {
if(isEmpty()){
return -1;
}
for(int i=0;i<size;i++){
if(data[i]==e){
return i;
}
}
return -1;
}
@Override
public E remove(int index) {
if(index<0 || index >size-1){
throw new ArrayIndexOutOfBoundsException("删除角标越界");
}
E e=get(index);
for(int i=index+1;i<size-1;i++){
data[i-1]=data[i];
}
size--;
//判断是否缩容
//1最短不能缩过默容量
//2有效元素小于容量的1/4
if(data.length>DEFAULT_SIZE && size<=data.length/4){
resize(data.length/2);
}
return e;
}
@Override
public E removeFirst() {
return remove(0);
}
@Override
public E removeLase() {
return remove(size-1);
}
@Override
public void removeElement(E e) {
int index=find(e);
if(index==-1){
throw new IllegalArgumentException("删除元素不存在");
}
remove(index);
}
@Override
public void clear() {
size=0;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append("ArrayList:size="+size+",capacity="+data.length+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
for(int i=0;i<size;i++){
if(i==size-1){
sb.append(data[i]);
sb.append(']');
}else{
sb.append(data[i]);
sb.append(",");
}
}
}
return sb.toString();
}
public int getCapacity(){
return data.length;
}
public void swap(int i,int j){
//i j 判断
if(i < 0 || i > data.length) {
throw new ArrayIndexOutOfBoundsException("swp下标越界");
}
if(j < 0 || j > data.length) {
throw new ArrayIndexOutOfBoundsException("swp下标越界");
}
E temp=data[i]; //定义一个 用来交换的数 初始值为 参数 i 的值
data[i]=data[j]; //把 j 的值 赋给 i
data[j]=temp; // 把拿到 参数 i 的值 给 data[j]
}
public boolean equals(Object obj){
if(obj==null) {
return false;
}
if(obj==this) {
return true;
}
if(obj instanceof ArrayList){ //如果 传入的对象 是 ArrayList 类型
ArrayList<E> list=(ArrayList<E>) obj; // 向下转 类型
if(getSize()==list.getSize()){ // 对比 两个的 size
for(int i=0;i<getSize();i++){ //遍历去比较
if(get(i)!=list.get(i)){
return false;
}
}
return true;
}
}
return false;
}
}
- ArrayList的分析
1、线性表的顺序存储结构----获得元素操作
直接将数组i-1内的下标对应的元素返回
2、线性表的顺序存储结构----添加元素操作O(n)
1.void add(int index, E e)
首先对传入的index进行判断,如果index<0或者index>有效元素(size)
抛异常 throw new ArrayIndexOutOfBoundsException("add函数角标越界");
2.判断数组是否已满
有效元素size==data.length
进行扩容操作 void resize(int newLen) =======>线性表顺序存储结构---扩缩容操作
3.数组为满
将index处之后的所有元素,从(size-1)下标开始往后移动一个
最后移动到index处停止 令data[index]=e;
有效元素的值开始累加1
4.延申
表头插入 void addFirst(E e) 表尾插入 void addLast(E e)
3、线性表的顺序存储结构----删除元素操作O(n)
1.E remove(int index)
与插入算法操作类似 先对index进行判断 未在取值范围
抛异常 throw new ArrayIndexOutOfBoundsException("remove函数角标越界");
2.开始删去index处的元素
使用for循环,对index之后的所有元素,从(index+1)处的元素开始往前一个下标移动
然后有效元素size累减1
3.判断是否需要缩容
最短不能缩过默认容量 && 有效元素的个数小于等于容量的1/4
进行缩容操作 void resize(int newLen) =========>线性表顺序存储结构---扩缩容操作
4.延申
表头删除 E removeFirst(E e) 表尾删除 E removeLast(E e)
4、线性表的顺序存储结构----扩缩容操作O(n)
1.利用传入的长度创建一个新的数组
2.循环遍历将旧数组中的每个元素复制到新数组中
3.让新数组的地址 指向 旧数组的地址
data=newData;
5、线性表的顺序存储结构----重写toString操作
1.这里使用StringBuileder :方便线程不安全
不使用StringBuffer :不方便线程安全
2.先判断线性表是否为空
空打印 []
非空 sb.append('['); 起步
开始遍历线性表 i为最后一个元素时 sb.append(']');
i非最后一个元素时 sb.append(',');
最后返回 sb.toString();