- 线性表
(1) 线性表是由n(n>=0)个数据元素所构成的有限序列。
(2) 对于同一个线性表,其每一个数据元素的虽然有所不同,但必须具有相同的数据类型;同时,数据元素之间具有一种线性或“一对一”的逻辑关系:
- 第一个数据元素没有前驱,这个数据元素称为开始结点;
- 最后一个数据元素没有后继,这个数据元素称为终端结点;
- 除了首尾数据元素之外,其他数据元素有且仅有一个前驱和一个后继。
(3) 上述的逻辑关系就是线性结构。线性表就是一种线性结构。
(4) 线性表的基本操作:
- clear():将一个已经存在的线性表置空
- isEmpty():判断线性表是否为空。若为空,返回true;不为空,返回false;
- length():求线性表中的元素个数,并返回其值
- get(i):读取并返回线性表中的第i个元素的值,其中0<= i <=length() -1
- insert( i,x):在线性表的第i个元素之前插入一个值为x的数据元素。其中0<= i <=length(),当i=0时,在表头插入;当i=length()时,在表尾插入
- remove(i ):删除并返回线性表中第i个元素。其中0<= i <=length() -1
- indexOf( x ):返回线性表中首次出现指定元素位序号,不包含此元素则返回 -1
创建接口:
public interface List<E> extends Iterable<E>{
//默认在表尾添加一个元素
public void add(E element);
//在指定角标处添加元素
public void add(int index, E element);
//删除指定元素
public void remove(E element);
//删除指定角标处的元素 并返回原先的值
public E remove(int index);
//获取指定角标处的元素
public E get(int index);
//修改指定角标index处的值为element 并返回原先的值
public E set(int index, E element);
//获取线性表中的元素个数
public int size();
//查看元素第一次出现的角标位置(从左到右)
public int indexOf(E element);
//判断是否包含元素
public boolean contains(E element);
//判断线性表是否为空
public boolean isEmpty();
//清空线性表
public void clear();
//按照比较器的内容进行排序
public void sort(Comparator<E> c);
//获取子线性表 原线性表中[fromIndex, toIndex)这个部分
public List<E> subList(int fromIndex, int toIndex);
}
线性表的简单操作实现
package 线性结构;
import 数据结构接口.List;
import java.util.Comparator;
import java.util.Iterator;
//自定义的线性表的顺序存储方式
public class ArrayList<E> implements List<E> {
//数组的容器 data.length 指的就是当前数组的容量
private E[] data;
//元素的个数 size == 0 线性表为空 size == data.length 线性表满了
//size 新元素默认尾部添加时要去的角标
private int size;
//默认容量
private static int DEFAULT_CAPACITY = 10;
//创建一个默认容量为10的线性表
public ArrayList() {
data = (E[]) new Object[DEFAULT_CAPACITY];
size = 0;
}
//创建一个指定容量的线性表
public ArrayList(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("capacity must > 0");
}
DEFAULT_CAPACITY = capacity;
data = (E[]) new Object[DEFAULT_CAPACITY];
size = 0;
}
//传入一个数组 将该数组封装成为一个线性表
public ArrayList(E[] arr) {
if (arr == null || arr.length == 0) {
throw new IllegalArgumentException("arr can not be null");
}
data = (E[]) new Object[DEFAULT_CAPACITY];
for (int i = 0; i < arr.length; i++) {
add(arr[i]);
}
}
@Override
public void add(E element) {
add(size, element);
}
@Override
public void add(int index, E element) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("add index out of range");
}
//判断线性表是否是满状态
if (size == data.length) {
resize(2 * data.length);
}
//向后移动元素
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
//将新元素插入到指定位置
data[index] = element;
size++;
}
//扩容/缩容 操作 不应该向外界提供
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 remove(E element) { //删除指定元素 只删除一次 && 删除所有的指定元素
int index=indexOf(element);
if (index!=-1){
remove(index);
}
}
@Override
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("remove index out of range");
}
//先保存要删除的值
E ret = data[index];
//移动元素
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
//什么时候缩容
//1.有效元素是容量的1/4
//2.当前容量不得小于等于默认容量
if (size == data.length / 4 && data.length > DEFAULT_CAPACITY) {
resize(data.length / 2);
}
return ret;
}
@Override
public E get(int index) {
if (index<0||index>=size){
throw new IllegalArgumentException("get index out of range");
}
return data[index];
}
@Override
public E set(int index, E element) {
if (index<0||index>=size){
throw new IllegalArgumentException("set index out of range");
}
E ret=data[index];
data[index]=element;
return ret;
}
@Override
public int size() {
return size;
}
//添加一个函数,获取线性表中数组的容量
private int getCapacity(){
return data.length;
}
@Override
public int indexOf(E element) {
for (int i=0;i<size;i++){
if (data[i].equals(element)){
/*
== 比的是啥?主要看等号的两边是啥
== 两边是基本数据类型的话 比的是值
byte short int long
float double
char boolean
== 两边是引用数据类型的话 比的是地址
数组 字符串 其他的类对象
*/
return i;
}
}
return -1;
}
@Override
public boolean contains(E element) {
return indexOf(element)!=-1;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void clear() {
data= (E[]) new Object[DEFAULT_CAPACITY];
size= 0;
}
@Override
public void sort(Comparator<E> c) {
if (c==null){
throw new IllegalArgumentException("comparator can not be null");
}
for (int i = 1; i<size; i++){
E e=data[i];
int j=0;
for (j=i; j > 0 && c.compare ( data [ j -1 ],e)>0; j--) {
data[j] = data[j-1];
}
data[j]=e;
}
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
if (fromIndex<0|| toIndex >= size || fromIndex > toIndex){
throw new IllegalArgumentException("must 0<= formIndex <= toIndex <= size-1");
}
ArrayList<E> list =new ArrayList<>();
for (int i = fromIndex; i <= toIndex;i++){
list.add(data[i]);
}
return list;
}
@Override
public boolean equals(Object o) {
//1.判空
if (o == null) {
return false;
}
//2.判自己
if (this == o ){
return true;
}
//3.判类型
if ( o instanceof ArrayList){
//4.按照自己的逻辑进行比较
ArrayList<E> other = (ArrayList<E>) o;
//5.先比有效元素的个数
if (size != other.size) {
return false;
}
//6.有效元素个数相等的情况下逐个比较元素
for ( int i = 0;i < size; i++) {
if ( !data [i].equals( other.data[i]) ) {
return false;
}
}
return true;
}
return false;
}
/*
[1, 2, 3, 4, 5, 6]
[]
Arrays.toString(arr);
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[') ;
if ( isEmpty() ) {
sb.append(']');
} else {
for (int i = 0; i < size; i++) {
sb.append(data[i]);
if (i == size - 1) {
sb.append(']');
} else {
sb.append(',');
sb.append(' ');
}
}
}
return sb.toString() ;
}
//获取当前这个数据结构/容器 的 迭代器
//通过迭代器对象 更方便挨个取出每一个元素
//同时 实现了Iterable 可以让当前的数据结构/容器 被foreach循环遍历
@Override
public Iterator<E> iterator() {
return new ArrayListIterator();
}
// 创建一个属于ArrayList的迭代器
class ArrayListIterator implements Iterator<E> {
private int cur = 0;
@Override
public boolean hasNext() {
return cur < size;
}
@Override
public E next() {
return data[cur++];
}
}
}
测试:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
System.out.println(list);
Random random = new Random();//引入Random函数生成随机数生成数据元素
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(100));
}
System.out.println(list);
for (int i = 0; i < 10; i++) {//扩容测试
list.add(0, i);
}
System.out.println(list);
list.sort(new Comparator<Integer>() {//引入比较器进行排序测试
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
for (Integer num : list) {
System.out.println(num);
}
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
}
}
//输出结果
[]
[15, 47, 24, 40, 90, 35, 87, 84, 18, 17]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 47, 24, 40, 90, 35, 87, 84, 18, 17]
[90, 87, 84, 47, 40, 35, 24, 18, 17, 15, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
- 栈
(1)栈是一种特殊的线性表,栈中的数据元素以及数据元素之间的逻辑关系和线性表相同,两者之间的区别主要在: - 线性表的插入和删除操作可以在任意指定位置进行,而栈只允许在末尾进行。
(2)将插入行为称为入栈,删除行为称为出栈。
(3) 栈的基本操作与线性表基本相同: - 取栈顶元素peek():读取栈顶元素并返回其值,若为空,则返回null
- 入栈操作push():将数据元素压入栈顶
- 出栈操作pop():删除并返回栈顶元素
(4)代码实现
创建接口
public interface Stack<E> extends Iterable {
public int size();
public boolean isEmpty();
//入栈 出栈一个元素 再线性表的表尾添加一个元素
public void push(E element);
//出栈 弹出一个元素 在线性表的末尾删除一个元素
public E pop();
//查看当前栈顶元素
public E peek();
public void clear();
}
栈的简单实现
public class ArrayStack<E> implements Stack<E> {
private ArrayList<E> list;
public ArrayStack() {list = new ArrayList<>();}//引入线性表类
public ArrayStack(int capacity) {list = new ArrayList<>(capacity);}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void push(E element) {
list.add(element);
}
@Override
public E pop() {
return list.remove(list.size() - 1 );
}
@Override
public E peek() {
return list.get(list.size() - 1 );
}
@Override
public void clear() {
list.clear();
}
@Override
public String toString() {
return list.toString();
}
@Override
public boolean equals(Object o) {
if (o == null ) {
return false;
}
if (this == o) {
return true;
}
if (o instanceof ArrayStack) {
ArrayStack other = (ArrayStack) o;
return this.list.equals(other.list);
}
return false;
}
}
测试
public static void main(String[] args) {
ArrayStack<Integer> stack01 = new ArrayStack<>();
ArrayStack<Integer> stack02 = new ArrayStack<>(15);
for (int i = 1; i <= 12; i++) {
stack01.push(i);
stack02.push(i);
}
System.out.println(stack01);
System.out.println(stack02);
System.out.println(stack01.equals(stack02));
System.out.println(stack01.pop());//删除栈顶元素并返回其值
System.out.println(stack01);
System.out.println(stack01.peek());
}
}
//输出结果
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
true
12
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
11
- 中缀计算
中缀表达式:是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
与前缀或后缀记法不同的是,中缀记法中括号是必需的。计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。

例如有这样一个字符串表达式:"(10+20/2*3)/2+8"
首先对它的运算符和提高优先级符号"()“的左右进行添加” "(空格)的操作,然后在运用java的split方法切割 ,就能实现对表达式的符号数字的分类识别,最后再用两个ArrayStack存放操作符和数字,最后根据不同的操作符与操作符栈栈顶遇到的几种情况分类,最后再书写每次计算,弹两个数字和一个操作符,结果放入数字栈的方法实现一个式子的计算。
public class InfixCalculator {
public static void main(String[] args) {
String expression = "(10+20/2*3)/2+8";
try {
int result = evaluateExpression(expression);
System.out.println(result);
}
catch (Exception e) {
e.printStackTrace();
System.out.println("Wrong expression : "+expression);
}
}
private static int evaluateExpression(String expression) {
//需要两个辅助栈
ArrayStack<Character> operatorStack = new ArrayStack<>();
ArrayStack<Integer> numberStack = new ArrayStack<>();
//格式化表达式
expression = insertBlanks(expression);
String[] tokens = expression.split(" ");
for (String token : tokens) {
//过滤空串
if (token.length() == 0) {
continue;
}
//遍历到“+”“-”
else if (token.equals("+") || token.equals("-")) {
while (!operatorStack.isEmpty() && (operatorStack.peek() == '+' || operatorStack.peek() == '-'
|| operatorStack.peek() == '*' || operatorStack.peek() == '/')) {
//如果之前是其他的+ - * / 就需要弹栈并计算
processAnOperator(numberStack,operatorStack);
}
//如果操作符栈为空 或者为 (
operatorStack.push(token.charAt(0));
}
//遍历到 * /
else if (token.equals("*") || token.equals("/")) {
while (!operatorStack.isEmpty() && (operatorStack.peek() == '*' || operatorStack.peek() == '/')) {
//如果之前是其他的 * / 就需要弹栈并计算
processAnOperator(numberStack,operatorStack);
}
//如果操作符栈为空 或者为 (
operatorStack.push(token.charAt(0));
}//遍历到(
else if (token.equals("(")) {
operatorStack.push(token.charAt(0));
}//遍历到 )
else if (token.equals(")")) {
//只要操作符栈顶不是 ( 就挨个弹栈计算即可
while (operatorStack.peek() != '(') {
processAnOperator(numberStack,operatorStack);
}
//清除 (
operatorStack.pop();
} //遍历到数字
else {
numberStack.push(new Integer(token));
}
}
//处理最后的操作符
while (!operatorStack.isEmpty()) {
processAnOperator(numberStack,operatorStack);
}
return numberStack.pop();
}
//操作符栈弹栈一个元素 数字栈弹栈两个数字 进行计算 并将新的结果进栈到数字栈
private static void processAnOperator(ArrayStack<Integer> numberStack, ArrayStack<Character> operatorStack) {
char op = operatorStack.pop();
int num1 = numberStack.pop();
int num2 = numberStack.pop();
//num2 op num1
if (op == '+') {
numberStack.push(num2+num1);
} else if (op == '-') {
numberStack.push(num2-num1);
} else if (op == '*') {
numberStack.push(num2*num1);
} else {
numberStack.push(num2/num1);
}
}
//对原表达式进行格式化处理 给所有的非数字字符两边添加空格
private static String insertBlanks(String expression) {
StringBuilder sb = new StringBuilder();
for (int i = 0;i < expression.length (); i++ ) {
char c = expression.charAt(i);
if (c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/') {
sb.append(' ');
sb.append(c);
sb.append(' ');
} else {
sb.append(c);
}
}
return sb.toString();
}
}
//输出结果
28
本文介绍了线性表和栈这两种基本的数据结构。线性表由相同类型的数据元素组成,支持多种基本操作如插入、删除等。栈作为线性表的一种特殊形式,仅允许在一端进行插入和删除操作。
342

被折叠的 条评论
为什么被折叠?



