一、数据结构的概述
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合
1.数据结构的逻辑结构
是指数据元素之间的相互关系,是我们想象出来的,并没有实质性的将其存储在计算机中
1)集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系(比如同一片区域的人,他们同处于一个集合,但他们之间是没有其他关系的)
2)线性结构:线性结构中的数据元素之间是一对一的关系
3)树形结构:树形结构中的数据元素之间存在一对多的层次关系
4)图形结构:图形结构的数据元素是多对多的关系
2.数据结构的物理结构
是指数据的逻辑结构在计算机中的存储形式
1)顺序存储结构:开辟一组连续的空间存储数据,通常用数组来实现,数据中的空间本身是连续的,保证了数据之间的关系
2)链式存储结构:开辟一组随机的空间存储数据,通常用节点来实现,节点不仅要存储数据,还要存储下一个节点的位置以保证数据之间的关系
3.顺序存储结构和链式存储结构的区别
1)顺序存储查找数据元素快,链式插入和删除元素快
2)顺序所占用的空间较大,链式占用空间小
二、算法
算法:解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
算法的五个基本特性:输入、输出、有穷性、确定性和可行性。
如何衡量一个算法的好坏呢?
一个好的算法包括以下特征:正确性、可读性、健壮性、时间效率高和存储量低、
1.时间复杂度
定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n)=O(f(n))。它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度。简称时间复杂度。其中f(n)是问题规模n的某个函数
1)常数阶O(1)
int N=100; //执行一次
int sum = (N+1)*N/2; //执行一次
随着N的增大,运行次数不变,所以时间复杂度为O(1)
2)线性阶O(n)
int sum = 0; //执行1次
int N=100; //执行1次
for(int i =1;i<=N;i++){ //执行N+1次
sum+=i; //执行N次
}
上述代码执行了2N+3次,时间复杂度为O(n) (通常可以忽略n的常数,n的系数)
3)对数阶O(logn)
int count = 1;
int N = 100000;
while(count<N){
count = count * 2;
}
假设循坏运行了x次,那么2^x=N,则log2(N)
4)平方阶O(n^2)
int N=1000;
for(int i =0;i<N;i++){ //执行N+1次
for(int j =0;j<N;j++){ //执行N+1次
........ //执行N^2次
}
}
对于n²来说可以忽略常数、幂高项的系数,只保留幂的最高项,因为随着n的增大,它的值会大幅度的增高,所以说系数、常数可以忽略不记。
执行次数函数 | 阶 | 非正式术语 |
---|---|---|
12 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
3n²+2n+1 | O(n²) | 平方阶 |
5log2(n)+20 | O(logn) | 对数阶 |
2n+3nlog2(n)+19 | O(nlogn) | nlogn阶 |
6n³+2n²+3n+4 | O(n³) | 立方阶 |
2^n | O(2^n) | 指数阶 |
注意:我们考虑的时间复杂度都是最坏情况!
三、动态数组
注意:动态数组就是顺序存储结构的具体实现
1.Java内置数组的特点:
- 数组的长度一旦确定则不可更改
- 数组只能存储同一类型的数据
- 数据中每个存储空间大小一致且地址连续
- 数据提供角标的方式访问元素
2.线性表的顺序存储结构
线性表:
1)零个或多个数据元素的有限序列
2)可通过索引访问元素
3)除了第一个元素没有前驱,最后一个元素没有后继,其他元素都有唯一的前驱和后继
3.List 线性表接口定义
/**
* List是线性表的最终父接口
* @author ABC
* * @param <E>
*/
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 元素的存在性布尔类型值
*/
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);
/**
* 删除线性表中的表头元素
* @return 表头元素
*/
public E removeFirst();
/**
* 删除线性表中的表尾元素
* @return 表尾元素
*/
public E removeLast();
/**
* 在线性表中删除指定元素
* @param e 指定元素
*/
public void removeElement(E e);
/**
* 清空线性表
*/
public void clear();
}
4.ArrayList 顺序表类定义
- 定义成员属性
private static int DEFAULT_SIZE=10; //容器的默认容量
private E[] data; //存储数据元素的容器
private int size; //线性表中的有效元素的个数
//data.length表示线性表中的最大容量Capacity
- 定义构造函数
/**
* 创建一个容量默认值为10的一个线性表
*/
public ArrayList(){
this(DEFAULT_SIZE);
}
/**
* 创建一个容量为指定capacity的一个线性表
* @param capacity
*/
public ArrayList(int capacity){
this.data=(E[]) new Object[capacity];
this.size=0;
}
/**
* 将一个数组封装成为一个线性表
* @param arr
*/
public ArrayList(E[] arr){
data = (E[]) new Object[arr.length];
for(int i=0;i<data.length;i++){
data[i]=arr[i];
}
size = data.length;
}
- 定义成员函数
(1)添加元素的思路:添加元素时可分为三种情况:表头、表尾、中间。- 如果添加的位置不正确,则抛出异常
- 如果表长大于数组的长度,则抛出异常或者扩容
- 从最后一个元素开始向前遍历到第i个元素,分别将它们向后移动一个位置
- 将要添加的元素放入i位置
- 表长+1
(2)删除元素的思路:
* 如果删除位置不合理,则抛出异常
* 取出删除元素
* 从删除元素的位置开始遍历到最后一个元素,分别将它们向前移动一个位置
* 表长-1
(3)扩容和缩容思路:当存入较多的数据时,要对数组进行扩容,同理当删除较多的数据时,为了避免浪费空间,要进行缩容。扩容操作和缩容操作都是创建一个新的数组,指定新的长度,将旧数组里面的元素copy到新数组当中。
@Override
public int getSize() {
// TODO Auto-generated method stub
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("add():角标越界");
}
//判断是否已满
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++;
}
/**
* 改变data的长度(扩容、缩容)
* @param newlen 新数组的长度
*/
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():index out of range");
}
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("get():index out of range");
}
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("remove():index out of range");
}
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 removeLast() {
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;
}
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++){
sb.append(data[i]);
if(i == size-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
public int getCapacity(){
return data.length;
}
public void swap(int i, int j){
//判断 i j
E temp = data[i];
data[i] = data[j];
data[j] = temp;
}
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayList){
ArrayList l = (ArrayList) obj;
if(getSize() == l.getSize()){
for(int i = 0;i<getSize();i++){
if(get(i)!=l.get(i)){
return false;
}
}
return true;
}
}
return false;
}