线性表
1.定义:零个或多个元素的有限序列。
n:这里指的有效长度的个数,而不是容量
下列情况不是线性表:
线性表的插入:
1.直接在表尾进行插入
2.在表中间插入:
线性表的删除:
1.直接在表尾删除:
2.在表中间进行删除:
线性表接口的一些操作
//在表尾添加元素
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); //获取指定角标的元素
public E set(int index, E element); //修改指定角标index处的值为 elemen并返回原先的值
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);//按照比较器的内容排序
public List<E> subList(int fromIndex,int toIndex);//获取子线性表
实现接口中的方法
public class AarrayList<E> implements LinearTable<E> {
//定义数组容器
private E[] data;
//元素个数
private int size;
//默认容量
private static int DEFAULT_CAPACITY = 10;
//默认构造函数
public AarrayList() {
data = (E[]) new Object[DEFAULT_CAPACITY];
size = 0;
}
//指定默认容量的构造函数
public AarrayList(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("capacity必须要大于0!");
}
DEFAULT_CAPACITY = capacity;
data = (E[]) new Object[DEFAULT_CAPACITY];
size = 0;
}
//指定数组的构造函数:传入一个数组将该数组封装成一个线性表
public AarrayList(E[] arr) {
if (arr == null || arr.length == 0) {
throw new IllegalArgumentException("arr不能为空");
}
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("角标越界");
}
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("角标越界");
}
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("越界异常");
}
return data[index];
}
@Override
public E set(int index, E element) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("越界异常");
}
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)) {
return i;
}
}
return -1;
}
@Override
public boolean contains(E element) {
return false;
}
@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("不能为空");
}
for (int i = 0; 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("角标不合法");
}
AarrayList<E> list=new AarrayList<>();
for (int i = fromIndex; i <=toIndex ; i++) {
list.add(data[i]);
}
return (List<E>) list;
}
public boolean equals(Object o){
if (o==null){
return false;
}
if (this==o){
return true;
}
if(o instanceof AarrayList){
AarrayList<E> other= (AarrayList<E>) o;
if (size!=other.size){
return false;
}
for (int i = 0; i <size ; i++) {
if(data[i].equals(other.data)){
return false;
}
}
return true;
}
return false;
}
@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 可以让当前数据结构 更好的遍历
*/
@Override
public Iterator<E> iterator() {
return new ArrayListIterator();
}
//创建一个属于Arratlist 的迭代器
class ArrayListIterator implements Iterator<E>{
private int cur=0;
@Override
public boolean hasNext() { //判断有下一个元素
return cur<size;
}
@Override
public E next() { //如果有下一个元素 则把当前元素返回 并移至下一元素
return data[cur++];
}
}
}
栈
1.栈是限定仅在表尾进行插入和删除操作的线性表。(也可以说栈是一个特殊的线性表)
2.我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)
3.不含任何数据元素的栈称为空栈
4.栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构
5.栈本身是一个线性表,其数据元素具有线性关系,只不过它是一种特殊的线性表而已
6.栈的插入操作,叫作进栈,也称压栈、入栈 。栈的删除操作,叫作出栈,也称弹栈。
问题:栈既然是一种特殊的线性表,那么可以不可以直接继承线性表?
可以继承线性表,但是没有必要,因为线性表的许多功能,而栈用不到,所以不需要继承。
栈的接口:
public interface stack<E> extends Iterable<E>{
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 AarrayList<E>list;
@Override
public Iterator<E> iterator() {
return list.iterator();
}
public ArrayStack(){
list=new AarrayList<>();
}
public ArrayStack(int capacity){
list=new AarrayList<>(capacity);
}
@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;
}
}
栈的实例:
中缀计算
(只符合计算中只有加减乘除计算) 例:对(10+20/2*3)/2+8 进行计算
第一步:先将(10+20/2*3)/2+8进行格式化 每个字符前后都加一个空格, ( 10 + 20 / 2 * 3 ) / 2 + 8
第二步:创建两个栈,一个栈是专门存放符号,一个栈是专门存放数字。然后遍历。第一次先遍历的是( ,由于“( ” 为栈底,无计算符号,即( 放入符号栈,然后遍历到10,直接放入数字栈,然后遍历到 + ,先判断栈顶(此时栈顶为“( ” ) 没有比+更高一级的运算符(判断没有),则继续遍历到20,20存放在数字栈中,继续遍历到 / 判断 (由于此时栈顶为+,但是+运算符的运算优先级没有/高,所以不进行+运算),则将/ 放入符号栈中,继续遍历到2,直接放入数字栈,继续遍历到 *(判断,此时栈顶为/ ,因为*和/运算级别相同则计算先计算先进入栈的/),计算完成后将计算的值放入数字栈中。将*入栈。
继续遍历到3直接进入数字栈,继续遍历到 ),因为是()让括号中的内容提升优先级,那么遍历到右括号,则将括号内的运算内容全部计算完毕,那么此时符号栈中有*和+,根据优先,先算*号,再算+,然后清楚左括号。右括号不进栈。
然后继续遍历到/(由于栈中没有符号)则/直接入栈,继续遍历到2直接放入数字栈中,在遍历到+,此时栈顶是/(由于/的优先级比+高,所以先计算/)然后将+入栈然后在遍历到8,将8直接放入数字栈中,当全部遍历完,进行最后的计算。返回计算结果。
代码实现:
public class infixcalculwtor {
public static void main(String[] args) {
String expression="(10+20/2*3)/2+8";
expression=insertBlanks(expression);
System.out.println(expression);
int result=evaluateExpression(expression);
System.out.println(result);
}
private static int evaluateExpression(String expression){
//两个辅助栈,一个字符栈,一个数字栈
ArrayStack<Character> operatorStack= new ArrayStack<Character>();
ArrayStack<Integer> numberStack= new ArrayStack<Integer>();
//格式化表达式
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();
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();
}
}
中缀表达式转前缀表达式
前缀表达式: 10 20 2 / 3 * + 2 / 8 + 中缀表达式:(10+20/2*3)/2+8
如何转呢? 还是利用两个栈,一个放计算符,一个放前缀表达式栈。和中缀表达式一样,先将表达式格式化(每个字符两边都加个空格),开始逐个遍历,计算符栈,入栈规则(若栈为空则入栈,若遍历到‘( ’ 则入栈,若比栈顶优先级大,入栈。若比栈顶优先级小,入栈。若同级,则先让栈顶弹出,再入栈。弹出的那个字符再入前缀表达式栈,若遍历到右括号,则将括号内计算符挨个弹出并进入到前缀表达式栈内,左括号不用进表达式栈)。
public static void main(String[] args) {
String expression = "(10+20/2*3)/2+8";
expression = infixToSuffix(expression);
System.out.println(expression);
}
public static String infixToSuffix(String expression) {
//操作符的栈
ArrayStack<String> opStack = new ArrayStack<String>();
//后缀表达式的线性表
ArrayList<String> suffixList = new ArrayList<>();
//格式化表达式
expression = insertBlanks(expression);
String[] tokens = expression.split(" ");
for (String token : tokens) {
//过滤空串
if (token.length() == 0) {
continue;
}
//判断操作符+ - * /
if (isOperator(token)) {
/*
什么时候操作符进栈?
1.如果栈为空
2.如果栈顶是 (
3.如果栈顶是操作符,且优先级比当前token小
什么时候需要将栈顶操作符出栈?
1.栈顶操作符的优先级 >= 当前token
*/
while (true) {
if (opStack.isEmpty() || opStack.peek().equals("(") || priority(opStack.peek()) < priority(token)) {
opStack.push(token);
break;
}
suffixList.add(opStack.pop());
}
} else if (token.equals("(")) {
opStack.push(token);
} else if (token.equals(")")) {
while (!opStack.peek().equals("(")) {
suffixList.add(opStack.pop());
}
opStack.pop();
} else if (isNumber(token)) {
suffixList.add(token);
} else {
throw new IllegalArgumentException("wrong char :" + expression);
}
}
while (!opStack.isEmpty()) {
suffixList.add(opStack.pop());
}
//将数字元素和操作符元素进行拼接
StringBuilder sb = new StringBuilder();
for (int i = 0; i < suffixList.size(); i++) {
sb.append(suffixList.get(i));
sb.append(' ');
}
return sb.toString();
}
private static int priority(String token) {
if (token.equals("+") || token.equals("-")) {
return 0;
}
if (token.equals("*") || token.equals("/")) {
return 1;
}
return -1;
}
private static boolean isNumber(String token) {
return token.matches("\\d+");
}
private static boolean isOperator(String token) {
return token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/");
}
//对原表达式进行格式化处理 给所有的非数字字符两边添加空格
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();
}
十进制转十六制
例如:654321 根据计算每次的余数分别为 1,15,11,15,9.即1,F,B,F,9
每得到一个一个余数入栈,然后在依次弹出。结果为9,F,B,F,1.
public static void main(String[] args) {
int num = 654321;
ArrayStack<String> stack = new ArrayStack<String>();
while (num != 0) {
int a = num % 16;
if (a < 10) {
stack.push(a + " ");
} else {
//10-A 11-B 12-C 13-D 14-E 15-F
//65 66 67 68 69 70
stack.push((char) (a + 55) + " ");
}
num /= 16;
}
StringBuilder sb=new StringBuilder();
while (!stack.isEmpty()){
sb.append(stack.pop());
}
System.out.println(sb.toString());
}
回文问题
例如“ 上海自来水来自海上” 将水前的的字依次入栈,然后在将水以后的字入栈,判断入栈前,若与栈顶相同,则将栈顶出栈,如果栈为空,则是回文。
private static void splution() {
String expression = "上海自来水来自海上";
ArrayStack<Character> stack = new ArrayStack<Character>();
for (int i = 0; i < expression.length(); i++) {
if (expression.length() % 2 == 1 && i == expression.length() / 2){
continue;
}
char c=expression.charAt(i);
if (stack.isEmpty()){
stack.push(c);
}else {
if (c!=stack.peek()){
stack.push(c);
}else {
stack.pop();
}
}
}
System.out.println(stack.isEmpty());
}
括号匹配问题
例如:{ ( ) [ ] } 因为相对应的{ },( ),[ ] 他们的差值不是-1就是-2,所以根据这个特性在利用栈。
String str="{()[]}";
ArrayStack<Character>stack= new ArrayStack<Character>();
for (int i = 0; i <str.length() ; i++) {
char c=str.charAt(i);
if (stack.isEmpty()){
stack.push(c);
}else {
char top=stack.peek();
if (top-c==-1||top-c==-2){
stack.pop();
}else {
stack.push(c);
}
}
}
System.out.println(stack.isEmpty());
}