文章目录
数据结构和算法
数据结构包括线性结构和非线性结构。
线性结构:
- 最常用的数据结构,特点是数据元素之间一对一的线性关系。
- 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储结构的线性表称为顺序表,顺序表中的元素是连续的;链式存储的线性表称为链表,链表的存储顺序不一定是连续的,节点中存放着数组元素和相邻元素的地址信息。
- 常见的有:数组,队列,链表和栈。
非线性结构:
包括:二维数组,多维数组,广义表,树结构,图结构。
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域(指向下一个节点)。
- 如图,发现链表的各个节点不一定是连续存储。
- 链表分带头结点的链表和不带头结点的链表,根据实际的需求来确定。
单链表实现
查询(遍历)
- 定义一个辅助节点(当做指针)。
- 判断链表是否为空。
- 循环遍历,直到节点的下一个节点为空。
//显示链表【遍历】
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;
}
}
}
添加(创建)
- 先创建一个head头节点,作用就是表示单链表的头。
- 每添加一个节点,就直接加入链表的最后。
//添加节点到单向链表
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;
}
添加(按照编号的顺序)
- 找到新添加的节点的位置,通过辅助变量(指针),通过遍历来搞定。
- 新的节点.next=temp.next。
- 将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;
}
}
修改
- 通过遍历找到需要修改的节点。
- 将修改的信息赋给节点。
//修改节点的信息,根据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);
}
}
删除
- 找到需要删除的节点的前一个节点(temp)。
- temp.next=temp.next.next。
- 被删除的节点,将不会有其他引用,会被垃圾回收机制回收。
//删除节点
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):是一个先入后出的有序列表。元素的插入和删除只能在线性表的同一端进行;允许插入和删除的一端,为变化的一端,称为栈顶,另一端为固定的一端,称为栈底。
应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
- 处理递归的调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数,区域变量等数据存入堆栈中。
- 表达式的转换【中缀表达式转后缀表达式】与求值(实际解决)。
- 二叉树的遍历。
- 图形的深度优先搜索法。
结构图
数组模拟栈
- 使用数组模拟栈。
- 定义一个top来表示栈顶,初始化为-1。
- 入栈:当有数据加入到栈时
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("程序退出");
}
}
单链表模拟栈
- 使用单链表模拟栈。
- 定义一个头结点来表示栈顶,初始化为0。
- 入栈:当有数据加入到栈时(头插法)
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("程序结束");
}
}
结束语:该博客还没写完整,后面会不定时接着该博客下面更新新的数据结构与算法,感谢支持!!!