数据结构和算法

数据结构和算法

数据结构包括线性结构和非线性结构。

线性结构:

  • 最常用的数据结构,特点是数据元素之间一对一的线性关系。
  • 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储结构的线性表称为顺序表,顺序表中的元素是连续的;链式存储的线性表称为链表,链表的存储顺序不一定是连续的,节点中存放着数组元素和相邻元素的地址信息。
  • 常见的有:数组,队列,链表和栈。

非线性结构:

​ 包括:二维数组,多维数组,广义表,树结构,图结构。



1.稀疏数组

应用场景

​ 当一个数组中的元素大部分为0,或者为同一取值时可以用稀疏数组来保存该数组。


处理方法

​ 1.记录数组有几行几列,有多少个不同的值。

​ 2.把不同值的元素记录在一个小规模的数组中,缩小程序的规模。

例如:

在这里插入图片描述

​ 列:行,列,值。

第一行:原数组的行数,原数组的列数,原数组不同值的数量。

第二行:第一个不同值的行数,第一个不同值的列数,第一个不同值。

第三行:第二个不同值的行数,第二个不同值的列数,第二个不同值。


实例编写

在这里插入图片描述

思路:

1.计算稀疏数组中不为0的个数

2.创建稀疏数组

3.将非零数赋给稀疏数组


代码:

//创建一个二维数组
int chessArr1[][]=new int[11][11];
chessArr1[1][2]=1;
chessArr1[2][3]=2;
chessArr1[3][4]=3;

System.out.println("原始的二维数组:");
for (int[] row :chessArr1){
    for (int data:row){
        System.out.printf("%d\t",data);
    }
    System.out.println();
}

//计算稀疏数组中不为0的个数
int num=0;
for (int i=0;i<11;i++){
    for(int j=0;j<11;j++){
        if (chessArr1[i][j]!=0)
            num++;
    }
}

//创建稀疏数组
int a[][]=new int[num+1][3];
a[0][0]=11;
a[0][1]=11;
a[0][2]=num;

int n=0;//记录遍历的非零数,将非零数赋给稀疏数组
for (int i=0;i<11;i++){
    for(int j=0;j<11;j++){
        if (chessArr1[i][j]!=0){
            n++;
            a[n][0]=i;
            a[n][1]=j;
            a[n][2]=chessArr1[i][j];
        }
    }
}

//遍历稀疏数组
System.out.println("稀疏数组为:");
for (int i=0;i<a.length;i++){
    System.out.printf("%d\t%d\t%d\t\n",a[i][0],a[i][1],a[i][2]);
}

结果:

在这里插入图片描述



还原稀疏数组

1.获取原数组的行列数。

2.将稀疏数组中不同值赋给还原的二维数组。

代码:

//还原稀疏数组
int chessArr2[][]=new int[a[0][0]][a[0][1]];

//将稀疏数组中的行列值赋给还原的二维数组
for(int i=1;i<a.length;i++){
    chessArr2[a[i][0]][a[i][1]]=a[i][2];
}

System.out.println("还原后的二维数组:");
for (int[] row :chessArr2){
    for (int data:row){
        System.out.printf("%d\t",data);
    }
    System.out.println();
}

结果:

在这里插入图片描述



2.队列

介绍
  • 队列是一个有序列表,可以用数组或者链表来实现。
  • 遵循先入先出,后存后取的原则。

实现方式

​ 1.若用数组的结构来实现存储队列的结构,则队列数组的声明如下图,maxSize是队列最大容量。

​ 2.因为队列的输出,输入分别是从前后端来处理,因此需要两个变量font及rear分别记录队列前后端的下标,font会随着数据输出而改变,而rear则是随着数据输入而改变。

在这里插入图片描述


实例编写

环形队列

1)尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满时需要注意(rear+1)%maxSize=front【满】。

2)rear==front【空】

3)分析示意图

在这里插入图片描述


代码

环形队列类

class CircleArray{
    private int maxSize;//表示数组的最大容量
    private int front;//队列头
    private int rear;//队列尾
    private int[] arr;//用于存放数据 模拟队列

    public CircleArray(int arrMaxSize){
        maxSize=arrMaxSize;
        arr=new int[maxSize];
    }

    //判断队列是否满
    public boolean isFull(){
        return (rear+1)%maxSize==front;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return rear==front;
    }

    //添加数据到队列
    public void addQueue(int n){
        if(isFull()){
            System.out.println("队列已满,不能添加");
            return;
        }
       //直接将数据加入
        arr[rear]=n;
        //将rear后移,这里考虑取模
        rear=(rear+1)%maxSize;
    }

    //获取队列的数据,出队列
    public int getQueue(){
        //判断队列是否空
        if(isEmpty()){
            throw new RuntimeException("队列空,不能取数据");
        }
        int value=arr[front];
        front=(front+1)%maxSize;
        return value;
    }

    //显示队列的所有数据
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列空的,没有数据");
            return;
        }
        //遍历
        for (int i=front;i<front+size();i++){
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i]);
        }
    }

    //求出当前队列有效数据的个数
    public int size(){
        return  (rear+maxSize-front)%maxSize;
    }

    //显示队列的头数椐
    public int headQueue(){
        //判断
        if(isEmpty()){
            throw new RuntimeException("队列空的,没有数据");
        }
        return arr[front];
    }
}

测试
public class CircleArrayQueueDemo {
    public static void main(String[] args) {
        System.out.println("------模拟环形队列------");
        CircleArray queue=new CircleArray(4);//设置为4,实际队列大小为3
        char key=' ';//接受用户输入
        Scanner scanner=new Scanner(System.in);
        boolean loop=true;
        //输出一个菜单
        while(loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列取出数据");
            System.out.println("h(head):查看队列头的数据");
            key=scanner.next().charAt(0);//接受一个字符
            switch (key){
                case 's':
                    queue.showQueue();
                    break;
                case 'a':
                    System.out.println("输入一个数:");
                    int value=scanner.nextInt();
                    queue.addQueue(value);
                    break;
                case 'g'://取出数据
                    try{
                        int res=queue.getQueue();
                        System.out.printf("取出的数据是%d\n",res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try{
                        int res=queue.headQueue();
                        System.out.printf("队列头的数据是%d\n",res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

效果图:

在这里插入图片描述


  • 添加数据

在这里插入图片描述

  • 显示数据

在这里插入图片描述

  • 取出数据

在这里插入图片描述


  • 取队列头

在这里插入图片描述

因为上面已经取出一个数据,所以队列头变成了原队列中的第二个数据。



3.链表

介绍
  • 链表以节点的方式来存储,是链式存储。
  • 每个节点包含data域,next域(指向下一个节点)。
  • 如图,发现链表的各个节点不一定是连续存储。

在这里插入图片描述

  • 链表分带头结点的链表和不带头结点的链表,根据实际的需求来确定。

在这里插入图片描述


单链表实现
查询(遍历)
  1. 定义一个辅助节点(当做指针)。
  2. 判断链表是否为空。
  3. 循环遍历,直到节点的下一个节点为空。
    //显示链表【遍历】
    public void list(){
        //判断链表为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //因为头结点不能用,所以需要一个辅助遍历temp
        HeroNode temp=head.next;
        while(true){
            //判断是否到链表最后
            if(temp==null){
                break;
            }
            System.out.println(temp);
            temp=temp.next;
        }
    }
}

添加(创建)
  1. 先创建一个head头节点,作用就是表示单链表的头。
  2. 每添加一个节点,就直接加入链表的最后。
//添加节点到单向链表
public void add(HeroNode heroNode){

    //因为头结点不能用,所以需要一个辅助节点temp
    HeroNode temp=head;
    //遍历链表,找到最后
    while(true){
        if(temp.next==null){
            break;
        }
        temp=temp.next;
    }
    //退出while循环时,temp指向链表的最后
    //将这个节点的next指向新的节点
    temp.next=heroNode;
}

添加(按照编号的顺序)
  1. 找到新添加的节点的位置,通过辅助变量(指针),通过遍历来搞定。
  2. 新的节点.next=temp.next。
  3. 将temp.next=新的节点。
//添加(按照编号的顺序)
public void addByOrder(HeroNode heroNode){
    HeroNode temp=head;
    boolean flag=false;//标识添加的编号是否存在,默认为false

    while(true){
        if(temp.next==null){//说明temp节点已在链表的最后
            break;
        }
        if(temp.next.no>heroNode.no){//找到位置,就在temp的后面
            break;
        }else if(temp.next.no==heroNode.no){//添加的编号已经存在
            flag=true;
            break;
        }
        temp=temp.next;//后移,遍历当前链表
    }
    if(flag){//不能添加说明,说明编号存在
        System.out.printf("准备插入的%d英雄已经存在了,不能加人\n",heroNode.no);
    }else{
        //插入到链表中,temp的后面
        heroNode.next=temp.next;
        temp.next=heroNode;
    }
}

修改
  1. 通过遍历找到需要修改的节点。
  2. 将修改的信息赋给节点。
//修改节点的信息,根据no编号修改
public void update(HeroNode heroNode){
    //判断是否为空
    if(head.next==null){
        System.out.println("链表为空");
        return;
    }
    //找到需要修改的节点,根据no编号
    HeroNode temp=head.next;//定义一个辅助节点
    boolean flag=false;//表示是否找到该节点
    while(true){
        if(temp==null){
            break;//已经遍历完链表
        }
        if(temp.no==heroNode.no){
            //找到
            flag=true;
            break;
        }
        temp=temp.next;
    }
    //根据flag判断是否找到要修改的节点
    if(flag){
        temp.name=heroNode.name;
        temp.nickname=heroNode.nickname;
    }else{
        System.out.printf("没有找到编号%d的节点,不能修改\n",heroNode.no);
    }

}

删除
  1. 找到需要删除的节点的前一个节点(temp)。
  2. temp.next=temp.next.next。
  3. 被删除的节点,将不会有其他引用,会被垃圾回收机制回收。
//删除节点
public void delete(int no){
    HeroNode temp=head;
    boolean flag=false;//是否找到要删除的节点
    while (true){
        if(temp.next==null){//链表已遍历到最后
            break;
        }
        if(temp.next.no==no){
            flag=true;
            break;
        }
        temp=temp.next;//temp后移,遍历
    }
    if(flag){//找到
        temp.next=temp.next.next;
    }else{
        System.out.printf("要删除的%d节点不存在\n",no);
    }
}

源码
//单链表
class SingleLinkedList{
    //添加头结点
    private HeroNode head=new HeroNode(0,"","");

    //添加节点到单向链表
    public void add(HeroNode heroNode){

        //因为头结点不能用,所以需要一个辅助节点temp
        HeroNode temp=head;
        //遍历链表,找到最后
        while(true){
            if(temp.next==null){
                break;
            }
            temp=temp.next;
        }
        //退出while循环时,temp指向链表的最后
        //将这个节点的next指向新的节点
        temp.next=heroNode;
    }

    //添加(按照编号的顺序)
    public void addByOrder(HeroNode heroNode){
        HeroNode temp=head;
        boolean flag=false;//标识添加的编号是否存在,默认为false

        while(true){
            if(temp.next==null){//说明temp节点已在链表的最后
                break;
            }
            if(temp.next.no>heroNode.no){//找到位置,就在temp的后面
                break;
            }else if(temp.next.no==heroNode.no){//添加的编号已经存在
                flag=true;
                break;
            }
            temp=temp.next;//后移,遍历当前链表
        }
        if(flag){//不能添加说明,说明编号存在
            System.out.printf("准备插入的%d英雄已经存在了,不能加人\n",heroNode.no);
        }else{
            //插入到链表中,temp的后面
            heroNode.next=temp.next;
            temp.next=heroNode;
        }
    }

    //修改节点的信息,根据no编号修改
    public void update(HeroNode heroNode){
        //判断是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点,根据no编号
        HeroNode temp=head.next;//定义一个辅助节点
        boolean flag=false;//表示是否找到该节点
        while(true){
            if(temp==null){
                break;//已经遍历完链表
            }
            if(temp.no==heroNode.no){
                //找到
                flag=true;
                break;
            }
            temp=temp.next;
        }
        //根据flag判断是否找到要修改的节点
        if(flag){
            temp.name=heroNode.name;
            temp.nickname=heroNode.nickname;
        }else{
            System.out.printf("没有找到编号%d的节点,不能修改\n",heroNode.no);
        }

    }

    //删除节点
    public void delete(int no){
        HeroNode temp=head;
        boolean flag=false;//是否找到要删除的节点
        while (true){
            if(temp.next==null){//链表已遍历到最后
                break;
            }
            if(temp.next.no==no){
                flag=true;
                break;
            }
            temp=temp.next;//temp后移,遍历
        }
        if(flag){//找到
            temp.next=temp.next.next;
        }else{
            System.out.printf("要删除的%d节点不存在\n",no);
        }
    }

    //显示链表【遍历】
    public void list(){
        //判断链表为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //因为头结点不能用,所以需要一个辅助遍历temp
        HeroNode temp=head.next;
        while(true){
            //判断是否到链表最后
            if(temp==null){
                break;
            }
            System.out.println(temp);
            temp=temp.next;
        }
    }
}

//定义HeroNode,每个HeroNode对象就是一个节点
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 + '\'' +
                '}';
    }
}

测试:

public static void main(String[] args) {
        //进行测试
        //先创建节点
        HeroNode hero1 = new HeroNode(1, "松江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3= new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");

        //创建链表
        SingleLinkedList singleLinkedList=new SingleLinkedList();
//        singleLinkedList.add(hero1);
//        singleLinkedList.add(hero2);
//        singleLinkedList.add(hero3);
//        singleLinkedList.add(hero4);


        singleLinkedList.addByOrder(hero1);
        singleLinkedList.addByOrder(hero4);
        singleLinkedList.addByOrder(hero2);
        singleLinkedList.addByOrder(hero3);

        singleLinkedList.list();

        HeroNode newHeroNode=new HeroNode(2,"小卢","玉麒麟");
        singleLinkedList.update(newHeroNode);

        System.out.println("修改后的链表:");

        //遍历
        singleLinkedList.list();

        singleLinkedList.delete(1);
        System.out.println("删除后的链表:");
        singleLinkedList.list();
    }
}

效果图:

在这里插入图片描述



双链表实现

在这里插入图片描述

代码
//双向链表
class DoubleLinkedList{
    //初始化一个头结点
    private Heronode head = new Heronode(0, "", "");

    public Heronode getHead() {
        return head;
    }

    //添加
    public void add(Heronode heronode) {

        //因为头结点不能用,所以需要一个辅助节点temp
        Heronode temp = head;
        //遍历链表,找到最后
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //退出while循环时,temp指向链表的最后
        //将这个节点的next指向新的节点
        //将新节点的pre指向该节点
        temp.next = heronode;
        heronode.pre=temp;
    }

    //添加(按照编号的顺序)
    public void addByOrder(Heronode heronode) {
        Heronode temp = head;
        boolean flag = false;//标识添加的编号是否存在,默认为false

        while (true) {
            if (temp.next == null) {//说明temp节点已在链表的最后
                break;
            }
            if (temp.next.no > heronode.no) {//找到位置,就在temp的后面
                break;
            } else if (temp.next.no == heronode.no) {//添加的编号已经存在
                flag = true;
                break;
            }
            temp = temp.next;//后移,遍历当前链表
        }
        if (flag) {//不能添加说明,说明编号存在
            System.out.printf("准备插入的%d英雄已经存在了,不能加人\n", heronode.no);
        } else {
            //插入到链表中,temp的后面
            heronode.next = temp.next;
            if(temp.next!=null) {//考虑temp为节点末端的情况
                temp.next.pre = heronode;
            }
            temp.next = heronode;
            heronode.pre=temp;

        }
    }


    //删除节点
    public void delete(int no) {
        //判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空,无法删除");
            return;
        }

        Heronode temp = head.next;
        boolean flag = false;//是否找到要删除的节点
        while (true) {
            if (temp == null) {//链表已遍历到最后
                break;
            }
            if (temp.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;//temp后移,遍历
        }
        if (flag) {//找到
            temp.pre.next = temp.next;
            if(temp.next!=null) {//考虑temp为节点末端的情况
                temp.next.pre = temp.pre;
            }
        } else {
            System.out.printf("要删除的%d节点不存在\n", no);
        }
    }


    //修改节点的信息
    public void update(Heronode heronode) {
        //判断是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点,根据no编号
        Heronode temp = head.next;//定义一个辅助节点
        boolean flag = false;//表示是否找到该节点
        while (true) {
            if (temp == null) {
                break;//已经遍历完链表
            }
            if (temp.no == heronode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到要修改的节点
        if (flag) {
            temp.name = heronode.name;
            temp.nickname = heronode.nickname;
        } else {
            System.out.printf("没有找到编号%d的节点,不能修改\n", heronode.no);
        }

    }

    //显示链表【遍历】
    public void list() {
        //判断链表为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头结点不能用,所以需要一个辅助遍历temp
        Heronode temp = head.next;
        while (true) {
            //判断是否到链表最后
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
}


//d定义HeroNode,每个HeroNode对象就是一个节点
//和单链表相比多一个pre指向前一个节点
class Heronode{
    public int no;
    public String name;
    public String nickname;
    public Heronode next;//指向后一节点
    public Heronode pre;//指向前一个节点

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

    @Override
    public String toString() {
        return "linklist.Heronode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

测试
public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        Heronode hero1 = new Heronode(1, "松江", "及时雨");
        Heronode hero2 = new Heronode(2, "卢俊义", "玉麒麟");
        Heronode hero3= new Heronode(3, "吴用", "智多星");
        Heronode hero4 = new Heronode(4, "林冲", "豹子头");


        DoubleLinkedList doubleLinkedList=new DoubleLinkedList();

        //添加
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);

        //添加(按顺序)
//        doubleLinkedList.addByOrder(hero1);
//        doubleLinkedList.addByOrder(hero4);
//        doubleLinkedList.addByOrder(hero3);
//        doubleLinkedList.addByOrder(hero2);


        //查询【遍历】
        doubleLinkedList.list();

        //修改
        Heronode heronode=new Heronode(4,"公孙胜","入云龙");

        doubleLinkedList.update(heronode);
        System.out.println("修改后的链表");
        doubleLinkedList.list();


        //删除
        doubleLinkedList.delete(3);
        System.out.println("删除后的链表");
        doubleLinkedList.list();


    }
}

效果图:

在这里插入图片描述



4.约瑟夫问题

问题描述

在这里插入图片描述


问题分析

​ 该问题可以用单向环形链表来解决,从头结点开始记,删除第m个节点,然后从被删除的后一个节点开始,依次删除第m个节点,直到最后一个节点。

例如当m=2时:

在这里插入图片描述


代码编写
  • 创建单向环形链表

    1.创建第一个节点,让first指向该节点,并形成环形。

    2.将新的节点加入到环形链表中。

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

  • 遍历环形链表

    1.定义一个辅助指针(变量)curBoy,指向first节点。

    2.通过while循环遍历,当curBoy.next==first结束。

//遍历环形链表
public void showBoy(){
    //判断链表是否为空
    if(first==null){
        System.out.println("没有任何小孩");
        return;
    }
    //因为first不能动,因此我们任然使用一个辅助指针完成遍历
    Boy curBoy=first;
    while(true){
        System.out.printf("小孩的编号%d\n",curBoy.getNo());
        if(curBoy.getNext()==first){//说明已经遍历完毕
            break;
        }
        curBoy=curBoy.getNext();//curBoy后移
    }

}

  • 删除节点

    1.创建辅助指针(变量)helper,指向环形链表的最后一个节点。

    2.让first和helper指针同时移动m-1次。

    3.删除first指向的节点:

  ​		  first=first.next;

    	  helper.next=first;
 //删除节点(出圈)

    /**
     *
     * @param startNo  从第几个开始数
     * @param countNum 数几下
     * @param nums 开始有几个小孩在圈中
     */
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){ //将helper指向最后一个节点
            break;
        }
        helper=helper.getNext();
    }


    //出圈
    while(true){
        if(helper==first){//圈中只有一个人
            break;
        }
        //让helper和first移动m-1次
	    for(int j=0;j<startNo-1;j++){
	        first=first.getNext();
	        helper=helper.getNext();
	    }
        System.out.printf("小孩%d出圈\n",first.getNo());
        first=first.getNext();
        helper.setNext(first);
    }
    System.out.printf("最后留在圈中的小孩编号%d\n",helper.getNo());
}

源码
//创建一个环形的单向链表
class CircleSingleLinkedList{
    //创建first节点,当前没有编号
    private Boy first=null;

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

        //遍历环形链表
        public void showBoy(){
            //判断链表是否为空
            if(first==null){
                System.out.println("没有任何小孩");
                return;
            }
            //因为first不能动,因此我们任然使用一个辅助指针完成遍历
            Boy curBoy=first;
            while(true){
                System.out.printf("小孩的编号%d\n",curBoy.getNo());
                if(curBoy.getNext()==first){//说明已经遍历完毕
                    break;
                }
                curBoy=curBoy.getNext();//curBoy后移
            }

        }

        //删除节点(出圈)

    /**
     *
     * @param startNo  从第几个开始数
     * @param countNum 数几下
     * @param nums 开始有几个小孩在圈中
     */
        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){ //将helper指向最后一个节点
                    break;
                }
                helper=helper.getNext();
            }

            //出圈
            while(true){
                if(helper==first){//圈中只有一个人
                    break;
                }
             //让helper和first移动m-1次
			    for(int j=0;j<startNo-1;j++){
			        first=first.getNext();
			        helper=helper.getNext();
			    }
                System.out.printf("小孩%d出圈\n",first.getNo());
                first=first.getNext();
                helper.setNext(first);
            }
            System.out.printf("最后留在圈中的小孩编号%d\n",helper.getNo());
        }

}



//创建Boy类,表示节点
class Boy{
    private int no;//编号
    private Boy next;//指向下一个节点

    public Boy(int no){
        this.no=no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}

测试
public class Josepfu {
    public static void main(String[] args) {

        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);
        circleSingleLinkedList.showBoy();
        circleSingleLinkedList.countBoy(1,2,5);
    }

}

在这里插入图片描述



5.栈

介绍

​ 栈(stack):是一个先入后出的有序列表。元素的插入和删除只能在线性表的同一端进行;允许插入和删除的一端,为变化的一端,称为栈顶,另一端为固定的一端,称为栈底。

在这里插入图片描述


应用场景
  • 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
  • 处理递归的调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数,区域变量等数据存入堆栈中。
  • 表达式的转换【中缀表达式转后缀表达式】与求值(实际解决)。
  • 二叉树的遍历。
  • 图形的深度优先搜索法。
结构图

在这里插入图片描述


数组模拟栈
  1. 使用数组模拟栈。
  2. 定义一个top来表示栈顶,初始化为-1。
  3. 入栈:当有数据加入到栈时
top++;
stack[top]=data;

​ 4.出栈:删除数据时

int value=stack[top];
top--;
return value;

代码

//定义一个ArrayStack表示栈
class ArrayStack{
    private int maxSize;//栈的大小
    private int[] stack;//数组模拟栈
    private int top=-1;//栈顶

    //构造器
    public ArrayStack(int maxSize){
       this.maxSize=maxSize;
       stack=new int[this.maxSize];
    }

    //栈满
    public boolean isFull(){
        return top==maxSize-1;
    }

    //栈空
    public boolean isEmpty(){
        return top==-1;
    }

    //入栈-push
    public void push(int value){
        if(isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top]=value;
    }

    //出栈-pop
    public int pop(){
        //先判断栈是否空
        if(isEmpty()){
            throw new RuntimeException("栈空");
        }
        int value=stack[top];
        top--;
        return value;
    }

    //栈的遍历
    public void list(){
        if(isEmpty()){
            System.out.println("栈空,没有数据");
        }
        //从栈顶开始显示数据
        for(int i=top;i>=0;i--){
            System.out.printf("stack[%d]=%d\n",i,stack[i]);
        }
    }

}

测试

public class ArrayStackDemo {
    public static void main(String[] args) {

        ArrayStack stack=new ArrayStack(4);
        String key="";
        boolean loop=true;//控制是否退出菜单
        Scanner scanner=new Scanner(System.in);
        while(loop){
            System.out.println("show:显示栈");
            System.out.println("exit:退出程序");
            System.out.println("push:添加数据到栈");
            System.out.println("pop:从栈取出数据");
            System.out.println("请输入你的选择:");
            key=scanner.next();
            switch (key){
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("输入一个数");
                    int value=scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res=stack.pop();
                        System.out.printf("出栈的数据是%d\n",res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }

}

单链表模拟栈
  1. 使用单链表模拟栈。
  2. 定义一个头结点来表示栈顶,初始化为0。
  3. 入栈:当有数据加入到栈时(头插法)
 node.next=temp.next;//node:插入的数据(结点);temp:辅助节点(Node temp=head;) 
 temp.next=node;

​ 4.出栈:删除数据时

temp.next=temp.next.next;

代码

//栈
class LinkListStack{
    public Node head=new Node(0);

    public void push(Node node){//压栈
        Node temp=head;//辅助节点
        boolean flag=false;//标记是否找到插入的位置

        while(true) {
            if (temp.no== 0) {
                break;
            }
            if(temp.next.no>node.no){
                break;
            }else if(temp.next.no== node.no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){
            System.out.printf("准备插入的%d已经存在,不能重复添加\n",node.no);
        }else{
            node.next=temp.next;
            temp.next=node;
        }
    }

    public void list(){//遍历
        if(head.next==null){
            System.out.println("栈内为空");
            return;
        }
        Node temp=head.next;
        while(temp!=null){
            System.out.println(temp);
            temp=temp.next;
        }
    }

    public void pop(){//出栈
        Node temp=head;
        if(temp.next==null){
            System.out.println("栈已空");
            return;
        }
        System.out.printf("数据%d出栈\n",temp.next.no);
        temp.next=temp.next.next;
    }
}

//节点
class Node{
    public int no;
    public Node next;

    public Node(int no){
        this.no=no;
    }

    @Override
    public String toString() {
        return "Node{" +
                "no=" + no +
                ", next=" + next +
                '}';
    }
}

测试

public class LinklistStackDemo {
    public static void main(String[] args) {
        LinkListStack linkListStack=new LinkListStack();

        boolean flag=true;//判断循环结束 true:进行,false:结束
        String key="";
        Scanner scanner = new Scanner(System.in);

        while(flag) {
            System.out.println("往栈类添加数据(a)");
            System.out.println("遍历(l)");
            System.out.println("出栈(d)");
            System.out.println("结束程序(e)");
            System.out.println("请输入你的操作:");
            key=scanner.next();
            switch (key) {
                case "a":
                    System.out.println("请输入你要添加的数据");
                    int value=scanner.nextInt();
                    Node node=new Node(value);
                    linkListStack.push(node);
                    break;
                case "l":
                    linkListStack.list();
                    break;
                case "d":
                    linkListStack.pop();
                    break;
                case "e":
                    scanner.close();
                    flag=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序结束");
    }
}

结束语:该博客还没写完整,后面会不定时接着该博客下面更新新的数据结构与算法,感谢支持!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值