1.静态数组与动态数组
静态数组的特点:
数组的长度一旦确定则不可更改
数组只能存储同一类型的数据
数组中每个存储空间地址是连续且相等的
数组提供角标的方式访问元素
缺点:
长度不可变,数据量大了怎么办?扩容可以解决(创建新数组)
地址连续且提供角标,访问很快,但移动元素只能挨个挨个移
最重要的,数组只有length这个属性,没有其他的方法!
动态数组,主要是解决其缺点3:
将数组本身和在其上的相关操作进行封装,生成一个类
今后只需创建该类的对象,就可以进行对其所包装的数组进行增删改查操作
为什么要说动态数组的,其实动态数组就是顺序存储结构的具体实现!
2.动态数组——线性表的顺序存储结构
线性表
零个或多个数据元素的有限序列
可通过索引访问元素
除了第一个元素没有前驱,最后一个元素没有后继,其他元素都有唯一的前驱和后继
是否允许存储重复元素(看需求)
是否允许存储空值NULL(看需求)
是否允许元素有序(这必须),有序指元素的进场顺序和退场顺序一致
是否允许元素顺序(看需求),顺序指元素在该数据结构内部是否已经被排序
用动态数组实现的线性表也称为顺序表
List 线性表接口定义
1.定义接口
public interface List<E> //E代表任意数据类型(数字、字符串等)支持泛型
2.定义功能
public int getSize(); //获取列表中元素的个数
public boolean isEmpty(); //判断当前列表是否为空
public void add(int index,E e); //在列表中指定角标index处添加元素e
public void addFirst(E e); //在列表中第一个位置添加元素e
public void addLast(E e); //在列表中最后一个位置添加元素e
public E get(int index); //获取列表中指定角标index处的元素
public E getFirst(); //获取列表中第一个位置的元素,E代表返回这个元素
public E getLast(); //获取列表中最后一个位置的元素
public E remove(int index); //删除列表中指定角标位置的元素并返回
public E removeFirst(); //删除列表中第一个位置的元素
public E removeLast(); //删除列表中最后一个位置的元素
public void set(int index,E e); //修改列表中指定角标index位置处的元素为新元素e
public boolean contains(E e); //判断列表中是否包含指定元素e
public int find(E e); //在列表中查找指定元素e的角标
public void removeElement(E e); //在列表中删除指定元素e
public void clear(); //清空列表
ArrayList 顺序表类定义
- 定义类
public class ArrayList<E> implements List<E> //ArrayList是List的一种具体实现
- 定义成员属性
private E[] data; //用于存储数据,data.length表示容器的容量(capacity)
private int size; //表示顺序表中当前有效元素的个数,
size==0表示顺序表为空表
- 定义构造函数
public ArrayList() //默认创建容量为N的容器(N自行定义),在此默认为10
public ArrayList(int capacity) //创建指定容量为capacity的容器
public ArrayList(E[] arr) //将一个数组初始化为一个顺序表
- 定义成员函数
实现List接口函数
public int getCapacity() //获取顺序表的最大容量
private void resize(int newLen) //改变顺序表长度为新长度newLen
public void swap(int i,int j) //交换顺序表中指定角标i,j的两个元素
public String toString() //返回该顺序表的字符串形式
package 数据结构与算法;
//动态数组实现的线性表->顺序表
/* List
* ——ArrayList
* 增删O(n) 改查O(1)
* 1.元素是否排序? 没有
* 2.元素是否允许重复?允许
* 3.元素是否允许空值?允许
* 4.元素是否有序?元素的进场顺序和出场顺序一致
* */
public class ArrayList<E> implements List<E>{
private E[] data; //容器-存储元素 data.length 最大容量
private int size; //当前列表中元素的个数 有效长度
private static final int DEFAULT_CAPACITY=10;
public ArrayList(){ //默认构造函数中创建一个容量为10的列表
this(DEFAULT_CAPACITY);
}
public ArrayList(int capacity){ //构造函数中创建一个指定容量为capacity的列表
if(capacity<0){ //非法角标
capacity=DEFAULT_CAPACITY;
}
this.data=(E[]) new Object[capacity]; //强转
this.size=0;
}
public void add(int index, E e) { //指定角标添加指定元素
if(index<0||index>size) { //应为多了一个元素所以要考虑有效容量
throw new IllegalArgumentException("角标非法"); //非法参数异常
}
if(size==data.length) { //如果有效长度等于最大容量
resize(data.length*2); //重置容量
}
for(int i=size;i>index;i--) {
data[i]=data[i-1];
}
data[index]=e;
size++;
}
public void addFirst(E e) {
add(0,e);
}
public void addLast(E e) {
add(size,e);
}
public E remove(int index) { //删除指定角标元素并返回新的数组
if(index<0||index>=size) {
throw new IllegalArgumentException("角标非法");
}
E res=data[index]; //取出指定角标元素
for(int i=index;i<size-1;i++) {
data[i]=data[i+1];
}
size--; //删除了元素所以减
if(size==data.length/4&&data.length>DEFAULT_CAPACITY) { //同时满足大于一定长度时缩容
resize(data.length/2);
}
return res;
}
public E removeFirst() {
return remove(0);
}
public E removeLast() {
return remove(size-1);
}
@Override
public void removeElement(E e) {
int index=find(e);
if(index!=-1) {
remove(index);
}
}
private void resize(int newLen) { //创建一个新的数组即扩容
E[] newData=(E[]) new Object[newLen];
for(int i=0;i<Math.min(data.length,newData.length);i++) { //扩容取决于小数组谁小遍历谁
newData[i]=data[i];
}
data=newData;
}
public int getSize() {
return this.size;
}
public boolean isEmpty() {
return this.size==0;
}
public E get(int index) {
if(index<0||index>=data.length) {
throw new IllegalArgumentException("角标非法");
}
return data[index];
}
public E getFirst() {
return get(0);
}
public E getLast(){
return get(size-1);
}
public void set(int index, E e) {
if(index<0||index>=size) {
throw new IllegalArgumentException("角标非法");
}
data[index]=e;
}
public int find(E e) {
for(int i=0;i<size;i++) {
if(data[i]==e) {
return i;
}
}
return -1;
}
public boolean contains(E e) {
return find(e)!=-1; //不等于-1说明存在返回一个true
}
public int getCapacity(){ //获取当前链表的最大容量
return data.length;
}
public void clear() { //清空数组
this.data=(E[]) new Object[DEFAULT_CAPACITY]; //相当于重建一个数组
this.size=0; //重置size为0
}
public void swap(int i,int j){
if(i<0||i>=size||j<0||j>=size) {
throw new IllegalArgumentException("角标非法");
}
E temp=data[i];
data[i]=data[j];
data[j]=temp;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
if(isEmpty()) {
sb.append(String.format("ArrayList:[] %d/%d \n",size,data.length));//String.format字符串格式化
}else {
sb.append("ArtrayList:[");
for(int i=0;i<size;i++) {
if(i==size-1) {
sb.append(data[i]+"]");//最后一个元素
}else {
sb.append(data[i]+",");
}
}
sb.append(String.format("%d/%d \n",size,data.length));
}
return sb.toString();
}
}
3.动态数组——栈的顺序存储结构
栈
栈本质上就是一种特殊的线性表
栈是限定仅在表尾进行插入和删除操作的线性表,先进后出的结构
元素插入叫进栈,元素删除叫出栈
是否允许存储重复元素(看需求)
是否允许存储空值NULL(看需求)
是否允许元素有序(这必须),有序指元素的进场顺序和退场顺序一致
是否允许元素顺序(不需要),顺序指元素在该数据结构内部是否已经被排序
用动态数组实现的线性表也称为顺序栈
Stack 栈接口定义
1.接口定义
public interface Stack<E>
2.功能定义
public int getSize(); //获取栈中元素的个数
public boolean isEmpty(); //判断栈是否为空
public void push(E e); //将指定元素e进栈
public E pop(); //出栈一个元素
public E peek(); //获取当前栈顶元素
public void clear(); //清空栈
ArrayStack 顺序栈类定义
- 定义类
public class ArrayStack<E> implements Stack<E>
- 定义成员属性
private ArrayList<E> list //栈本质上就是一种线性表,所以此处用顺序表实现一个栈
- 定义构造函数
public ArrayStack() //创建栈且默认容量为10
public ArrayStack(int capacity) //创建栈且指定容量capacity
- 定义成员函数
实现Stack接口函数
public int getCapacity() //获取栈的最大容量
public String toString() //返回该顺序栈的字符串形式
4.动态数组——双端栈的顺序存储结构
双端栈:是指从一个线性表的两端当做栈底进行分别的入栈和出栈操作
ArrayStackDoubleEnd 顺序双端栈类定义
1.定义类
public class ArrayStackDoubleEnd<E> implements Stack<E>
2.定义成员变量
public static final int L=0; //表示左边的栈
public static final int R=1; //表示右边的栈
private E[] data; //本质还是一个线性表
private int left; //左边栈的栈顶 开始为1
private int right; //右边栈的栈顶 开始为data.length
3.定义构造函数
public ArrayStackDoubleEnd() //创建默认容量为
DEFAULT_SIZE的栈
public ArrayStackDoubleEnd(int capacity)//创建容量为指定capacity的栈
4.定义成员函数
实现Stack接口函数
public void push(int which,E e) //向指定栈which进栈指定元素e
public E pop(int which) //指定栈which出栈一个元素
private void resize(int newLen) //改变栈的容量为newLen
public int getCapacity() //获取栈容量
public int getSize(int which) //获取指定栈which的元素个数
public E peek(int which) //获取指定站which的栈顶元素
public boolean isEmpty(int which) //判断指定栈which是否为空
public String toString() //返回该栈的字符串形式
public void clear(int which); //清空指定栈which
package 数据结构与算法;
import java.util.Arrays;
public class ArrayStackDoubleEnd<E> implements Stack<E>{
private E[] data; //元素容器
private int left; //左端栈的栈顶 -1
private int right; //右端栈的栈顶 data.length
public static final int L=0; //左端栈标记
public static final int R=1; //右端栈标记
private int size; //栈中元素的总个数
public ArrayStackDoubleEnd() {
this(10);
}
public ArrayStackDoubleEnd(int capacity){
data=(E[]) new Object[capacity];
left=-1;
right=data.length;
size=0;
}
public void push(int which,E e){
//满 size==data.length left+1=right
if(size==data.length){
throw new IllegalArgumentException("栈已满!");
//resize
}
if(which==L){
data[++left]=e;
}else{
data[--right]=e;
}
size++;
}
public E pop(int which){
if(isEmpty(which)){
throw new IllegalArgumentException("栈已空!");
}
size--;
//resize
if(which==L){
return data[left--];
}else{
return data[right++];
}
}
public int getCapacity(){
return data.length;
}
public int getSize(int which){
if(which==L){
return left+1;
}else{
return data.length-right;
}
}
public E peek(int which){
if(isEmpty(which)){
throw new IllegalArgumentException("栈为空!");
}
if(which==L){
return data[left];
}else{
return data[right];
}
}
public boolean isEmpty(int which){
if(which==L){
return left==-1;
}else{
return right==data.length;
}
}
public void clear(int which){
if(which==L){
left=-1;
}else{
right=data.length;
}
}
@Override
public void push(E e) {
if(size==data.length){
throw new IllegalArgumentException("栈已满!");
}
if(getSize(L)<=getSize(R)){
push(L,e);
}else{
push(R,e);
}
}
@Override
public E pop() {
if(getSize(L)>=getSize(R)){
return pop(L);
}else{
return pop(R);
}
}
@Override
public E peek() {
if(getSize(L)>=getSize(R)){
return peek(L);
}else{
return peek(R);
}
}
@Override
public boolean isEmpty() {
return left==-1&&right==data.length&&size==0;
}
@Override
public int getSize() {
return size;
}
@Override
public void clear() {
left=-1;
right=data.length;
size=0;
}
@Override
public String toString() {
return Arrays.toString(data);
}
}
5.动态数组——队列的顺序存储结构
队列
队列本质上就是一种特殊的线性表
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,先进先出的结构
元素插入叫入队,元素删除叫出队
是否允许存储重复元素(看需求)
是否允许存储空值NULL(看需求)
是否允许元素有序(这必须),有序指元素的进场顺序和退场顺序一致
是否允许元素顺序(不需要),顺序指元素在该数据结构内部是否已经被排序
用动态数组实现的线性表也称为顺序队列
Queue 队列接口定义
1.接口定义
public interface Queue<E>
2.功能定义
public int getSize(); //获取队列的元素个数
public boolean isEmpty(); //判断队列是否为空
public void enqueue(E e); //将指定元素e入队
public E dequeue(); //出队一个元素
public E getFront(); //获取队首元素
public E getRear(); //获取队尾元素
public void clear(); //清空队列
ArrayQueue 顺序队列类定义
- 定义类
public class ArrayQueue<E>implements Queue<E
> - 定义成员变量
private ArrayList<E> list
- 定义构造函数
public ArrayQueue()
public ArrayQueue(int capacity)
- 定义成员函数
实现Queue接口函数
public int getCapacity()
public String toString()
6.动态数组——循环队列的顺序存储结构
顺序队列本质上是由顺序表实现的,但是每次出队一个元素,都需要将后续所有元素前
移,影响效率。
能否在出队时保持其他元素位置不变?可以,但是这样子的话,出队的元素多了,其顺
序表中当前队首元素之前会有很多闲置空间。
如何利用闲置空间?此处就需要将顺序表收尾相连,形成循环表。
我们把这种队列的这种头尾相接的顺序存储结构称为循环队列
ArrayQueueLoop 循环顺序队列类定义
1.定义类
public class ArrayQueueLoop<E> implements Queue<E>
2.定义成员变量
E[] data; //循环队列的元素容器,此时不能够再用顺序表实现,顺序表没有循环功能。
int size; //循环队列中元素的个数
int front; //队首指针 当rear==front时表示空队列,当(rear+1)%data.length==front时表示满队列
int rear; //队尾指针 当列表不为满时,rear始终指向一个空闲空间,这个空间需要考虑是否被计算在容量里!
3.定义构造函数
public ArrayQueueLoop()
public ArrayQueueLoop(int capacity)
4.定义成员函数
实现Queue接口函数
public int getCapacity()
private void resize(int newLen)
public String toString()