Java 八大数据结构之数组,栈,队列
一:介绍
数据结构是计算机存储,组织数据的方式,指相互之间存在一种或多种特定的数据元素的集合
二:数组(Array)
数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型(Object类型数组除外)
数组申明
方式一:
数据类型 [] 数组名称=new 数据类型[数组长度];
//声明一个数组长度为5,只能存放int类型的数据
int[] num=new int[5];
//给数组第二个元素赋值
num[1]=12;
//输出第一个元素和第二个元素,结果0,12默认没赋值的是0
System.out.println(num[0]);
System.out.println(num[1]);
//结果
System.out: 0
System.out: 12
String[] ss=new String[4];
ss[0]="Rocky";
System.out.println(ss[0]);
System.out.println(ss[1]);
//结果
System.out: Rocky
System.out: null
//可以看到:int:默认是0,String:默认是null,boolean:默认是flase
方式二:
数据类型 [] 数组名称 = {数组元素1,数组元素2,......}
int[] ss= {1,4,3,5,7,3};
for (int i=0;i<ss.length;i++){
System.out.println(ss[i]);
}
自定义一个数组的实现,实现数组的增删改查
public class MyArray {
//定义一个数组
private int[] intArray;
//定义数组的实际有效长度
private int elems;
//定义数组的最大长度
private int length;
//默认构造一个长度为50的数组
public MyArray() {
elems = 0;
length = 50;
intArray = new int[length];
}
//构造函数,初始化一个长度为length 的数组
public MyArray(int length) {
elems = 0;
this.length = length;
intArray = new int[length];
}
//获取数组的有效长度
public int getSize() {
return elems;
}
//添加数组
//假设操作人是不会添加重复元素的,如果有重复元素对于后面的操作都会有影响
public boolean add(int value) {
//添加元素默认是从角标elems,0开始添加
if (elems == length) {
return false;
} else {
intArray[elems] = value;
elems++;
}
return true;
}
//根据下标获取元素,查找下标值在数组下标有效范围内,返回下标所表示的元素,查找下标超出数组下标有效值,提示访问下标越界
public int get(int i) {
if (i < 0 || i > elems) {
System.out.println("访问下标越界");
}
return intArray[i];
}
//查找元素,查找的元素如果存在则返回下标值,如果不存在,返回 -1
public int find(int searchValue) {
int i;
for (i = 0; i < elems; i++) {
if (intArray[i] == searchValue) {
break;
}
}
if (i == elems) {
return -1;
}
return i;
}
//删除元素,果要删除的值不存在,直接返回 false;否则返回true,删除成功
public boolean delete(int value) {
int k = find(value);
if (k == -1) {
return false;
} else {
if (k == elems - 1) {
elems--;
} else {
//删除元素最关键的是遍历元素,并替换删除元素之后坐标的数值
for (int i = k; i < elems - 1; i++) {
intArray[i] = intArray[i + 1];
}
elems--;
}
return true;
}
}
//修改数据,修改成功返回true,修改失败返回false
public boolean modify(int oldValue, int newValue) {
int i = find(oldValue);
if (i == -1) {
System.out.println("需要修改的数据不存在");
return false;
} else {
intArray[i] = newValue;
return true;
}
}
/**
* 遍历显示元素
*/
public void display() {
for (int i = 0; i < elems; i++) {
System.out.print(intArray[i] + " ");
}
System.out.println();
}
}
调用
//创建自定义封装数组结构,数组大小为4
MyArray array=new MyArray(4);
//添加4个元素
array.add(3);
array.add(5);
array.add(6);
array.add(7);
//显示数据
array.display();
//根据下标为0的元素
int i = array.get(0);
System.out.println(i);
//删除7的元素
array.delete(7);
//将元素6修改为12
array.modify(6, 12);
array.display();
//结果
System.out: 3 5 6 7
System.out: 3
System.out: 3 5 12
数组一旦创建后,大小就固定了,不能动态扩展数组的元素个数。如果初始化你给一个很大的数组大小,那会白白浪费内存空间,如果给小了,后面数据个数增加了又添加不进去了。
二维数组
定义:二维数组其实就是一个元素为一维数组的数组;
//方式一
int a[][] = new int[3][4];
int[][] b = new int[3][4];//一般使用
int[] c[] = new int[3][3];
b[0][0]=1;
b[0][1]=5;
System.out.print(b[0][0]+",");
System.out.print(b[0][1]+",");
System.out.print(b[3][6]+","); //java.lang.ArrayIndexOutOfBoundsException: length=3; index=3
//结果
System.out: 1,5,
//方式二
int d[][] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
//遍历结果
for(int x=0;x<d.length;x++){
for(int y=0;y<d[x].length;y++){
System.out.print(d[x][y]+",");
}
System.out.println();
}
//结果
System.out: 1,3,5,7,
System.out: 9,11,13,15,
System.out: 17,19,21,23,
//方式三
int e[][] = new int[3][];
e[0] = new int[2];
e[1] = new int[2];
e[2] = new int[2];
三:栈(Stack)
栈(英语:stack)又称为堆栈或堆叠,栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。
栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)
由于堆叠数据结构只允许在一端进行操作,因而按照后进先出(LIFO, Last In First Out)的原理运作。栈也称为后进先出表。
自定义实现一个栈,可以出栈,入栈,可以扩容,可以访问栈顶数据
public class MyStack {
//存储元素的数组,声明为Object类型能存储任意类型的数据
private Object[] elementData;//数组
private int maxSize;//栈最大数量
private int top;//栈顶的指针
public MyStack() {
this.elementData = new Object[10];
this.top = -1;
this.maxSize = 10;
}
public MyStack(int initialCapacity) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("栈初始容量不能小于0: " + initialCapacity);
}
this.elementData = new Object[initialCapacity];
this.maxSize = initialCapacity;
top = -1;
}
//压入数据
public Object push(Object value) {
//是否需要扩容
isGrow(top + 1);
elementData[++top] = value;
return value;
}
//弹出栈顶数据
public Object pop() {
Object obj = peek();
remove(top);
return obj;
}
//访问栈顶数据
public Object peek() {
if(top == -1){
throw new EmptyStackException();
}
return elementData[top];
}
//判断栈是否为空
public boolean isEmpty() {
return (top == -1);
}
//删除栈顶元素
public void remove(int top){
//栈顶元素置为null
elementData[top] = null;
this.top--;
}
//判断栈是否满了
public boolean isFull() {
return (top == maxSize - 1);
}
/**
* 是否需要扩容,如果需要,则扩大一倍并返回true,不需要则返回false
*
* @param minCapacity
* @return
*/
public boolean isGrow(int minCapacity) {
int oldCapacity = maxSize;
//如果当前元素压入栈之后总容量大于前面定义的容量,则需要扩容
if (minCapacity >= oldCapacity) {
//定义扩大之后栈的总容量
int newCapacity = 0;
//栈容量扩大两倍(左移一位)看是否超过int类型所表示的最大范围
if ((oldCapacity << 1) - Integer.MAX_VALUE > 0) {
newCapacity = Integer.MAX_VALUE;
} else {
newCapacity = (oldCapacity << 1);//左移一位,相当于*2
}
this.maxSize = newCapacity;
int[] newArray = new int[maxSize];
elementData = Arrays.copyOf(elementData, maxSize);
return true;
} else {
return false;
}
}
}
调用
MyStack stack = new MyStack(3);
stack.push(2);
System.out.println(stack.peek());
stack.push("Rocky");
stack.push(true);
stack.push("fight");//超过了3个自动扩容
//访问栈顶数据
System.out.println(stack.peek());
stack.pop();
stack.pop();
stack.pop();
System.out.println(stack.peek());
//判断栈是否为空
boolean aa=stack.isEmpty();
System.out.println(aa);
//结果
System.out: 2
System.out: fight
System.out: 2
System.out: false
//利用栈实现字符翻转
MyStack myStack = new MyStack();
String str = "HelloWorld";
char[] cha = str.toCharArray();
for(char c : cha){
myStack.push(c);
}
while(!myStack.isEmpty()){
System.out.println(myStack.pop());
}
四:队列(Queue)
队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
1.与栈不同的是,队列中的数据不总是从数组的0下标开始的,移除一些队头front的数据后,队头指针会指向一个较高的下标位置
2.我们再设计时,队列中新增一个数据时,队尾的指针rear 会向上移动,也就是向下标大的方向。移除数据项时,队头指针 front 向上移动。那么这样设计好像和现实情况相反,比如排队买电影票,队头的买完票就离开了,然后队伍整体向前移动。在计算机中也可以在队列中删除一个数之后,队列整体向前移动,但是这样做效率很差。我们选择的做法是移动队头和队尾的指针。
自定队列实现增删
3.如果向第②步这样移动指针,相信队尾指针很快就移动到数据的最末端了,这时候可能移除过数据,那么队头会有空着的位置,然后新来了一个数据项,由于队尾不能再向上移动了,那该怎么办呢?
4.为了避免队列不满却不能插入新的数据,我们可以让队尾指针绕回到数组开始的位置,这也称为“循环队列”。
弄懂原理之后实现:
public class MyQueue {
private Object[] queArray;
//队列总大小
private int maxSize;
//前端指针,队头指针
private int front;
//后端指针,队尾指针
private int rear;
//队列中元素的实际数量
private int nItems;
public MyQueue(int size){
maxSize=size;
queArray=new Object[maxSize];
front=0;
rear=-1;
nItems=0;
}
//队列新增数据
public void insert(int value){
if (isFull()){
System.out.println("队列已满!!!");
}else {
//如果队尾部指向顶了,那么循环回来,执行队列的第一个元素
if (rear==maxSize-1){
rear=-1;
}
//队尾指针加1,然后在队尾指针处插入新的数据
queArray[++rear] = value;
nItems++;
}
}
//移除数据
public Object remove(){
Object removeValue = null ;
if (!isEmpty()){
removeValue=queArray[front];
//把队头数据置为空
queArray[front]=null;
//队头指针+1
front++;
if (front==maxSize){
//如果队头为总容量大小,置为0
front=0;
}
nItems--;
return removeValue;
}
return removeValue;
}
//返回队列的大小
public int getSize(){
return nItems;
}
//查看对头数据
public Object peekFront(){
return queArray[front];
}
//判断队列是否为空
public boolean isEmpty(){
return (nItems ==0);
}
//判断队列是否满了
public boolean isFull(){
return (nItems == maxSize);
}
}
调用
MyQueue queue = new MyQueue(3);
queue.insert(1);
queue.insert(2);
queue.insert(3);//queArray数组数据为[1,2,3]
System.out.println(queue.peekFront()); //1
queue.remove();//queArray数组数据为[null,2,3]
System.out.println(queue.peekFront()); //2
queue.insert(4);//queArray数组数据为[4,2,3]
//为什么是[4,2,3]不是[2,3,4]
//应为队列队尾满了以后会循环到前面去
/* 如果队尾部指向顶了,那么循环回来,执行队列的第一个元素
if (rear==maxSize-1){
rear=-1;
//这个决定的,队尾的指针变了,到了最后了,就会循环回去,但是队头的指针是没有变动的
}*/
System.out.println(queue.peekFront()); //2,应为队头指针没有变动
queue.remove();//queArray数组数据为[4,null,3]
queue.remove();//queArray数组数据为[4,null,null]
queue.insert(5);//queArray数组数据为[4,5,null]
queue.insert(6);//queArray数组数据为[4,5,6]
queue.insert(7);//队列已满,queArray数组数据为[4,5,6]
System.out.println(queue.peekFront()); //4
//结果
System.out: 1
System.out: 2
System.out: 2
System.out: 队列已满!!!
System.out: 4
请大家先看看什么是前缀表达式,中缀表达式,后缀表达式:这三种表达式其实就是算术表达式的三种写法,以 3+4-5为例
前缀表达式:操作符在操作数的前面,比如 +-543(计算机从右向左扫描)
中缀表达式:操作符在操作数的中间,这也是人类最容易识别的算术表达式 3+4-5
后缀表达式:操作符在操作数的后面,比如 34+5-(计算机从左向右扫描)
END:我们都是阴沟里的虫子,但总还是得有人仰望星空。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。