栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 其他

spark(二)

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spark(二)

spark+数据结构
          • groupBy
          • filter
          • sample
          • distinct
          • coalesce
          • repartition
          • sortBy
          • union
          • intersection
          • subtract
          • zip
          • partitionBy
          • reduceByKey
          • groupByKey
          • aggregateByKey
          • foldByKey
  • 数据结构
    • 链表
    • 单链表
    • 双向链表
    • 单向环形链表
      • 栈的应用
      • 使用数组模拟栈
      • 计算器(中缀表达式)

groupBy

将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样 的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中

filter

➢ 函数说明

将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。 当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出 现数据倾斜。

package operator.transform

import org.apache.spark.{SparkConf, SparkContext}

object FilterTest {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operate")
    val sc = new SparkContext(sparkConf)
    val rdd = sc.textFile("datas/apache.log")
    rdd.filter(line=>{
      val datas=line.split(" ")
      val time=datas(3)
      time.startsWith("17/05/2015")
    }).collect().foreach(println)

    sc.stop()

  }

}
sample

根据指定的规则从数据集中抽取数据

package operator.transform

import org.apache.spark.{SparkConf, SparkContext}

object SampleTest {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operate")
    val sc = new SparkContext(sparkConf)
    val rdd = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10))
    //sample算子需要传递三个参数
    //1、第一个参数表示,抽取数据后是否将数据返回true(返回),false(丢弃)
    //2、第二个参数表示,
    //             如果抽取不放回的场合:数据源中每条数据被抽取的概率
    //             如果抽取放回的场合:表示数据源中的每条数据被抽取的可能次数
    //3、第三个参数表示,抽取数据时随机算法的种子
    //   如果不传入第三个参数,那么使用的是当前系统时间
//    println(rdd.sample(false,0.4,1).collect().mkString(","))

    //
    println(rdd.sample(true,3,1).collect().mkString(","))
  }
}

distinct

将数据集中重复的数据去重

package operator.transform

import org.apache.spark.{SparkConf, SparkContext}

object DistinctTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(1, 2, 3, 4, 1, 2, 3))
    val rdd1 = rdd.distinct()
    rdd1.collect().foreach(println)
  }
}
coalesce

根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率 当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少 分区的个数,减小任务调度成本

package operator.transform

import org.apache.spark.{SparkConf, SparkContext}

object CoalesceTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd=sc.makeRDD(List(1,2,3,4),4)
      
      //默认情况下,不会将数据打乱组合,可能导致数据不均衡
      //val newRDD=rdd.coalesce()
    //如果想要让数据均衡,可以进行shuffle处理
    val newRDD=rdd.coalesce(2,true)

  }

}
repartition

该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的 RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition 操作都可以完成,因为无论如何都会经 shuffle 过程。

package operator.transform

import org.apache.spark.{SparkConf, SparkContext}

object RepartitionTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd=sc.makeRDD(List(1,2,3,4,5),2)
    val newRdd = rdd.repartition(3)
 
  }
}

sortBy

该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理 的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一 致。中间存在 shuffle 的过程

package operator.transform

import java.util.Comparator

import org.apache.spark.{SparkConf, SparkContext}

object SortByTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(6, 5, 4, 3, 2,1),2)
    //默认为升序
    //val sort = rdd.sortBy(num=>num,)
    
    //降序
    val sort = rdd.sortBy(num=>num,false)
    sort.collect().foreach(println)

  }

}

union

对源 RDD 和参数 RDD 求并集后返回一个新的 RDD

intersection

对源 RDD 和参数 RDD 求交集后返回一个新的 RDD

subtract

以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集

zip

将两个 RDD 中的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD 中的元素,Value 为第 2 个 RDD 中的相同位置的元素。

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd1 = sc.makeRDD(List(1,2,3,4,5))
    val rdd2=sc.makeRDD(List(4,5,6,7,8))

    val rdd3 = rdd1.intersection(rdd2)
    println(rdd3.collect().mkString(","))

    val rdd4 = rdd1.union(rdd2)
    println(rdd4.collect().mkString(","))

    val rdd5=rdd1.subtract(rdd2)
    println(rdd5.collect().mkString(","))

    val rdd6=rdd1.zip(rdd2)
    println(rdd6.collect().mkString(","))
  }

交集、并集、差集要求类型要一致

partitionBy

将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(1, 2, 3, 4, 5))
    val mapRdd: RDD[(Int, Int)] =rdd.map((_,1))
    //RDD=> PairRDDFunction
    //隐式转换(二次编译)
    mapRdd.partitionBy(new HashPartitioner(2)).saveAsTextFile("output")
  }
reduceByKey
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(("a",1),("b",2),("c",3),("a",2)))

    //相同的key的数据进行value数据的聚合操作
    //如果key的数据只有一个,是不会参与运算的
    val reduce = rdd.reduceByKey((x: Int, y: Int) => {
      x + y
    })
    reduce.collect().foreach(println)
    sc.stop()
  }
groupByKey

将数据源的数据根据 key 对 value 进行分组

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(("a",1),("b",2),("c",3),("a",2)))

    //将数据源中的数据,相同key的数据分在一个组中,形成一个对偶元组,元组中的第一个元素就是key,元组中的第二个元素就是相同key的value的集合
    val groupRDD: RDD[(String, Iterable[Int])] = rdd.groupByKey()
    groupRDD.collect().foreach(println)

    val rdd2: RDD[(String, Iterable[(String, Int)])] = rdd.groupBy(_._1)

    sc.stop()

  }

从 shuffle 的角度:reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey 可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的 数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较 高。

从功能的角度:reduceByKey 其实包含分组和聚合的功能。GroupByKey 只能分组,不能聚 合,所以在分组聚合的场合下,推荐使用 reduceByKey,如果仅仅是分组而不需要聚合。那 么还是只能使用 groupByKey

aggregateByKey

将数据根据不同的规则进行分区内计算和分区间计算

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3)),2)

    //存在函数的柯里化,有两个参数列表
    //第一个参数列表 需要传递一个参数,表示初始值
    //   主要用于当碰见第一个key的时候,和value进行分区内计算
    //第二个参数表示需要传递2个参数 分别表示分区内的计算规则和分区间的计算规则
    rdd.aggregateByKey(0)((x,y)=>math.max(x,y),(x,y)=>x+y).collect().foreach(println)

    sc.stop()
  }
foldByKey

当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey

 
数据结构 
链表 

链表是有序的

1、链表式以节点的方式来存储,是链式存储

2、每个节点包含data域,next域:指向下一个节点

3、链表的额各个节点不一定是连续存储

4、链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

单链表

package linkeList;

import java.util.Stack;

public class SinglelinkedListDemo {
    public static void main(String[] args) {
        //创建节点
        HeroNode hero1 = new HeroNode(1, "a", "a");
        HeroNode hero2 = new HeroNode(2, "b", "b");
        HeroNode hero3 = new HeroNode(3, "c", "c");
        HeroNode hero4 = new HeroNode(4, "d", "d");
        HeroNode hero5 = new HeroNode(5, "e", "e");
        HeroNode hero6 = new HeroNode(6, "f", "f");

        SinglelinkedList singlelinkedList = new SinglelinkedList();
        singlelinkedList.add(hero1);
        singlelinkedList.add(hero2);
        singlelinkedList.add(hero3);
        singlelinkedList.add(hero4);
        singlelinkedList.add(hero5);
        singlelinkedList.add(hero6);

        singlelinkedList.del(1);
        singlelinkedList.update(new HeroNode(6, "g", "g"));
        singlelinkedList.list();
        System.out.println(getLength(singlelinkedList.getHead()));
        System.out.println(findLastIndexNode(singlelinkedList.getHead(),2));
        System.out.println("***********");
        reverseList(singlelinkedList.getHead());
        singlelinkedList.list();

        System.out.println("*******反转打印*******");
        reversePrint(singlelinkedList.getHead());

    }

    public static int getLength(HeroNode head){
        int len=0;
        HeroNode temp=head.next;
        while (temp!=null){
            len++;
            temp=temp.next;
        }
        return len;
    }



    //1、编写一个方法接受一个head节点,同时接收一个index
    //2、index表示是倒数第index个节点
    //3、先把链表从头到尾遍历,得到链表的总长度getLength
    //4、得到size后,从链表的第一开始遍历(size-index)个,可以得到
    public static HeroNode findLastIndexNode(HeroNode head,int index){
        //判断链表为空,返回null
        if(head.next==null){
            return null;
        }
        //第一个遍历得到链表的长度(节点个数)
        int size=getLength(head);
        //第二次遍历size-index位置,
        //先做一个index的校验
        if(index<=0||index>size){
            return null;
        }
        //定义一个辅助变量
        HeroNode cur=head.next;
        for(int i=0;istack=new Stack();
        HeroNode cur=head.next;
        //将链表的所有节点压入栈
        while(cur!=null){
            stack.push(cur);
            cur=cur.next;//让cur后移,这样就可以压入下一个节点
        }
        //将栈中的节点进行打印,pop出栈
        while(stack.size()>0){
            System.out.println(stack.pop());//先进后出
        }


    }


}

//定义SinglelinkedList
class SinglelinkedList {
    //先初始化一个头节点,头节点不要动
    private HeroNode head = new HeroNode(0, "", "");

    public HeroNode getHead() {
        return head;
    }

    //添加节点到单向链表
    //思路,当不考虑编号的顺序时
    //1、找到当前链表的最后节点
    //2、将最后这个节点的next指向新的节点
    public void add(HeroNode heroNode) {
        //因为head节点不能动,因此需要一个辅助遍历temp
        HeroNode temp = head;
        //遍历链表,找到最后
        while (temp.next != null) {
            //找到链表的最后
            //如果没有找到最后,将temp后移
            temp = temp.next;
        }
        //当退出while循环时,temp就指向了链表的最后
        temp.next = heroNode;
    }

    //第二种添加方式,根据排名插入到指定位置
    //如果有这个排名,则添加失败,并给出提示
    public void addByOrder(HeroNode heroNode) {
        //因为头节点不能动,因此需要通过一个辅助指针(变量)来帮助找到添加的位置
        //因为单链表,因此找的temp是位于添加位置的前一个节点,否则插入不了
        HeroNode temp = head;
        boolean flag = false;//标志添加的标号是否存在,默认为false
        while (true) {
            if (temp.next == null) {
                break;
            }
            if (temp.next.no > heroNode.no) {
                break;
            } else if (temp.next.no == heroNode.no) {
                flag = true;//说明标号存在
                break;
            }
            temp = temp.next;//后移,遍历当前链表

        }
        //判断flag的值
        if (flag) {
            System.out.println("准备插入的编号已存在,不能加入:" + heroNode.no);
        } else {
            //插入到链表中,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }

    }

    public void update(HeroNode newHeroNode) {
        //判断是否为空
        if (head.next == null) {
            System.out.println("链表为空");
        }
        //找到需要修改的节点,根据no编号
        //定义一个辅助变量
        HeroNode temp = head.next;
        boolean flag = false;
        while (true) {
            if (temp == null) {
                break;//已经完成遍历链表
            }
            if (temp.no == newHeroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到需要修改的节点
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        } else {//没有找到

            System.out.println("没有找到编号为" + newHeroNode.no + "的节点");

        }
    }

    //单链表删除一个节点
    public void del(int no) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp=head;
        boolean flag = false;
        while(true){
            if(temp.next==null)break;

            if(temp.next.no==no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){//找到
            temp.next=temp.next.next;
        }else{
            System.out.println("要删除的"+no+"不存在");
        }

    }


    //显示链表
    public void list() {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头节点,不能动,因此需要一个辅助变量来遍历
        HeroNode temp = head.next;
        while (temp != null) {
            //判断是否到链表最后
            //输出节点的信息
            System.out.println(temp);
            //将temp后移
            temp = temp.next;

        }
    }

    public int getLength(){
        int len=0;
        HeroNode temp=head.next;
        while(temp!=null){
            len++;
            temp=temp.next;
        }
        return len;
    }


}


class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode next;

    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + ''' +
                ", nickname='" + nickname + ''' +
                '}';
    }
}

双向链表

单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找

单向链表不能自我删除,需要辅助节点,而双向链表,则可以自我删除

package linkeList;

public class DoublelinkedListDemo {

    public static void main(String[] args) {

        HeroNode2 hero1 = new HeroNode2(1, "a", "a");
        HeroNode2 hero2 = new HeroNode2(2, "b", "b");
        HeroNode2 hero3 = new HeroNode2(3, "c", "c");
        HeroNode2 hero4 = new HeroNode2(4, "d", "d");

        DoublelinkedList doublelinkedList=new DoublelinkedList();
        doublelinkedList.add(hero1);
        doublelinkedList.add(hero2);
        doublelinkedList.add(hero3);
        doublelinkedList.add(hero4);

        doublelinkedList.del(2);
        doublelinkedList.update(new HeroNode2(3,"c","c1"));

        doublelinkedList.list();

    }
}

class DoublelinkedList {

    private HeroNode2 head = new HeroNode2(0, "", "");

    public HeroNode2 getHead() {
        return head;
    }

    //对于双向链表,可以直接找到要删除的节点
    //找到后,自我删除,即可
    public void del(int no) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode2 temp = head.next;
        boolean flag = false;
        while (true) {
            if (temp == null) break;//已经找到链表的最后的next

            if (temp.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {//找到
            temp.pre.next = temp.next;
            if (temp.next != null) temp.next.pre = temp.pre;
        } else {
            System.out.println("要删除的" + no + "不存在");
        }

    }

    public void update(HeroNode2 newHeroNode) {
        //判断是否为空
        if (head.next == null) {
            System.out.println("链表为空");
        }
        //找到需要修改的节点,根据no编号
        //定义一个辅助变量
        HeroNode2 temp = head.next;
        boolean flag = false;
        while (true) {
            if (temp == null) {
                break;//已经完成遍历链表
            }
            if (temp.no == newHeroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到需要修改的节点
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        } else {//没有找到

            System.out.println("没有找到编号为" + newHeroNode.no + "的节点");

        }
    }

    public void add(HeroNode2 heroNode) {
        //因为head节点不能动,因此需要一个辅助遍历temp
        HeroNode2 temp = head;
        //遍历链表,找到最后
        while (temp.next != null) {
            //找到链表的最后
            //如果没有找到最后,将temp后移
            temp = temp.next;
        }
        //当退出while循环时,temp就指向了链表的最后
        temp.next = heroNode;
        heroNode.pre = temp;
    }


    public void list() {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头节点,不能动,因此需要一个辅助变量来遍历
        HeroNode2 temp = head.next;
        while (temp != null) {
            //判断是否到链表最后
            //输出节点的信息
            System.out.println(temp);
            //将temp后移
            temp = temp.next;

        }
    }


}

class HeroNode2 {
    public int no;
    public String name;
    public String nickname;
    public HeroNode2 next;//指向下一个节点,默认为null
    public HeroNode2 pre;//指向前一个节点,默认为null

    public HeroNode2(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "HeroNode2{" +
                "no=" + no +
                ", name='" + name + ''' +
                ", nickname='" + nickname + ''' +
                '}';
    }
}
单向环形链表

约瑟夫环问题

设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

package linkeList;

public class Josepfu {
    public static void main(String[] args) {
        CircleSinglelinkedList circleSinglelinkedList = new CircleSinglelinkedList();
        circleSinglelinkedList.addBoy(5);
        circleSinglelinkedList.countBoy(1,2,5);


    }
}

//创建一个环形的单向链表
class CircleSinglelinkedList {
    //创建一个first节点,当前没有编号
    private Boy first = new Boy(-1);

    //添加节点,构建成一个环形链表
    public void addBoy(int nums) {
        if (nums < 1) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = first;//辅助指针,帮助构建环形链表
        //使用for来创建环形链表
        for (int i = 1; i <= nums; i++) {
            //根据编号创建节点
            Boy boy = new Boy(i);
            //如果是第一个小孩
            curBoy.setNext(boy);
            curBoy = curBoy.getNext();


//            if (i == 1) {
//                first = boy;
//                first.setNext(first);
//                curBoy = first;
//            } else {
//                curBoy.setNext(boy);
//                boy.setNext(first);
//                curBoy = boy;
//            }
        }
        curBoy.setNext(first.getNext());
        first = first.getNext();

    }

    //遍历当前环行链表
    public void showBoy() {
        //判断链表是否为空
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        Boy curBoy = first;
        while (true) {
            System.out.println("编号:" + curBoy);
            curBoy = curBoy.getNext();
            if (curBoy == first) {
                break;
            }
        }

    }
    //根据用户的输入,计算小孩出圈的顺序

    
    public void countBoy(int startNo, int countNum, int nums) {
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("参数输入有误,请重新输入");
            return;
        }
        //创建辅助指针,先将指针指向环型链表的最后这个节点
        Boy helper = first;
        while (true) {
            if (helper.getNext() == first) {
                break;
            }
            helper = helper.getNext();
        }
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        while(true){
            if(helper==first){//圈中只有一个节点
                break;
            }
            //
            for (int i = 0; i = 0; i--) {
            System.out.println("stack[" + i + "]" + "=" + stack[i]);
        }
    }

}

计算器(中缀表达式)

使用栈完成表达式的计算 思路

  1. 通过一个 index 值(索引),来遍历我们的表达式

  2. 如果我们发现是一个数字, 就直接入数栈

  3. 如果发现扫描到是一个符号, 就分如下情况

3.1 如果发现当前的符号栈为空,就直接入栈

3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先大于栈中的操作符, 就直接入符号栈.

4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.

5. 最后在数栈只有一个数字,就是表达式的结果

package stack;

public class Calculator {
    public static void main(String[] args) {
        String expression = "43+2*6-2";
        //创建两个栈,数栈和符号栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);
        //定义需要的相关变量
        int index = 0;//用于扫描
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';//将每次扫描得到char保存到ch
        String keepNum = "";//用于拼接多位数
        //开始while循环的扫描expression
        while (true) {
            //依次得到expression的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            //判断ch是什么,然后相应的处理
            if (operStack.isOper(ch)) {//如果是运算符
                //判断当前的符号栈是否为空
                if (!operStack.isEmpty()) {
                    //如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或等于栈中的操作符,就需要从数栈中pop出两个数
                    //在从符号中pop出一个符号,将得到结果,入数栈,然后将当前的操作符入符号栈
                    if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1, num2, oper);
                        //把运算的结果入数栈
                        numStack.push(res);
                        //然后将当前的操作符入符号栈
                        operStack.push(ch);
                    } else {
                        //如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
                        operStack.push(ch);
                    }
                } else {
                    //如果为空直接入栈
                    operStack.push(ch);
                }

            } else {
                //如果是数,则直接入数栈
                // numStack.push(ch-48);
                //1、当处理多位数时,不能发现是一个数据立即入栈,因为可能是多位数
                //2、当处理数,需要向expression的表达式的index后再看一位,如果时数就进行扫描,如果有符号就入栈
                //3、需要定义一个变量字符串,用于拼接
                //处理多位数
                keepNum += ch;

                //判断下一个字符是不是数字,如果时数字,就继续扫描,如果是运算符,则入栈
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                } else {
                    if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        //如果后一位是运算符,则入栈
                        numStack.push(Integer.parseInt(keepNum));
                        //
                        keepNum = "";
                    }
                }

            }
            //让index+1,并判断是否扫描到expression最后
            index++;
            if (index >= expression.length()) {
                break;
            }
        }
        //当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
        while (true) {
            //如果符号为空,则计算到最后的结果,数栈中只有一个数字结果
            if (operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }
        System.out.println(expression + "=" + numStack.pop());


    }
}

class ArrayStack2 extends ArrayStack {

    public ArrayStack2(int maxSize) {
        super(maxSize);
    }

    //返回当前栈顶的值,但是不是真正的pop
    public int peek() {
        return stack[top];
    }

    //返回运算符的优先级,优先级是程序员来确定,优先级使用数字来表示
    //数字越大,则优先级越高
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '_') {
            return 0;
        } else {
            return -1;//假定目前的表达式只有+、-、*、/
        }
    }

    //判断是不是一个运算符
    public boolean isOper(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/';
    }

    //计算方法
    public int cal(int num1, int num2, int oper) {
        int res = 0;//res 用于存放计算的结果
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;//注意顺序
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/279218.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号