线性结构
一、线性结构是最常见的数据结构,特点是数据元素之间存在一对一的线性关系。
二、线性结构有两种不同的存储结构,即顺序存储结构和链式存储结)。顺序存储的线性表叫做顺序表,顺序中的存储元素是连续的;链式存储的线性表叫做链表,其中的存储元素不一定是连续的,元素结点中存放数据元素以及相邻元素的地址信息。
三、线性结构常见的有:数组,队列,链表和栈
非线性结构
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构
稀疏数组
当一个数组大部分都为0,或者同一个值得数组时,可以用稀疏数组来保存该数组。
处理方法:
记录数组一共有几行几列,有多少个不同的
二维数组转稀疏数组的步骤
1、遍历原始的二维数组,得到有效数据的个数sum
2、根据sum就可以创建稀疏数组sparseArr int [sum+1][3]
3、将二维数组的有效数据存入到稀疏数组
稀疏数组转为原始二维数组的思路
1、先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组。
2、在读取稀疏数组后几行的数据,并赋给原始的二维数组即可。
public static void main(String[] args) {
//创建一个原始的二位数组11*11
//0表示没有棋子,1表示黑子2,表示白子
int chessArr1[][]=new int[11][11];
chessArr1[1][2]=1;
chessArr1[2][3]=2;
//输出原始的二维数组
for(int[]row :chessArr1) {
for(int data:row) {
System.out.printf("%d\t",data);
}
System.out.println();
}
//将二维数组转为稀疏数组
//1、先遍历二维数组得到非0数据的个数
int sum=0;
for(int i=0;i<chessArr1.length;i++) {
for(int j=0;j<chessArr1[0].length;j++) {
if(chessArr1[i][j]!=0) {
sum++;
}
}
}
System.out.println("sum"+sum);
//创建一个稀疏数组
int sparseArr1[][]=new int[sum+1][3];
//给稀疏数组赋值
//稀疏数组的第一行分别存放行数,列数,非0数据个数
sparseArr1[0][0]=11;
sparseArr1[0][1]=11;
sparseArr1[0][2]=sum;
//遍历二维数组,将非0的值放入稀疏数组中
int count=0;
for(int i=0;i<chessArr1.length;i++) {
for(int j=0;j<chessArr1[0].length;j++) {
if(chessArr1[i][j]!=0) {
count++;
sparseArr1[count][0]=i;
sparseArr1[count][1]=i;
sparseArr1[count][2]=chessArr1[i][j];
}
}
}
//输出稀疏数组
for(int[]row : sparseArr1) {
for(int data: row) {
System.out.printf("%d\t",data);
}
System.out.println();
}
//稀疏数组转为二维数组
//定义一个新数组,读取稀疏数组数据,从稀疏数组的第二行开始读取,不给二维数组即可
int chessArr2[][]=new int[sparseArr1[0][1]][sparseArr1[0][1]];
for(int i=1;i<sparseArr1.length;i++) {
chessArr2[sparseArr1[i][0]][sparseArr1[i][1]]=sparseArr1[i][2];
}
//输出转化后的二维数组
System.out.println("转化后的二维数组");
for(int [] row :chessArr2) {
for(int data:row) {
System.out.printf("%d\t",data);
}
System.out.println();
}
}
队列
队列是一个有序列表,可以用数组或者是链表来实现
遵循先入先出的原则,即:先存入队列的数据,要先取出。后存入的要后去除
队列本身是有序列表,若使用数组的结构来存储队列的数据。则队列数组的声明如下图,其中maxsSize是该队列的最大容量
因为队列的输出,输入分别是从前端和后端来处理的,因此需要两个变量front及rear分别记录队列前后端的下表,front会随着数据输出而改变,而rear则是随着数据输入而改变
链表
1、链表是以节点的方式来储存,是链式存储
2、每个节点包含data域,next域(指向下一个节点)
3、链表的各个节点不一定是连续储存
4、链表分为带头节点的链表和头节点的链表,根据实际的需求来确定
单链表
public static void main(String[] args) {
// 创建节点
HeroNode hero1=new HeroNode(1, "lhg", "前端");
HeroNode hero2=new HeroNode(2, "wyf", "java后端");
HeroNode hero3=new HeroNode(3, "ljk", "python");
HeroNode hero4=new HeroNode(4, "lmy", "Unity");
//创建单链表
SinlLinkedList list=new SinlLinkedList();
//按顺序添加节点
// list.add(hero1);
// list.add(hero2);
// list.add(hero3);
// list.add(hero4);
System.out.println("乱序插入节点 ");
list.addOrder(hero3);
list.addOrder(hero4);
list.addOrder(hero1);
list.addOrder(hero2);
list.List();
System.out.println("修改节点");
HeroNode newhero=new HeroNode(4, "girl","C#");
list.update(newhero);
//显示单链表
list.List();
System.out.println("删除1");
list.delete(1);
list.List();
}
class SinlLinkedList {
private HeroNode head=new HeroNode(0, "", "");
//添加结点到单向链表
//1.找到当前链表的最后结点
//2.将最后这个结点的next指向新的结点
public void add(HeroNode heroNode) {
//因为head不能动,因此我们要遍历一个辅助遍历temp
HeroNode temp=head;
while(true) {
if(temp.next==null) {
break;}
//指向新的结点
temp=temp.next;
}
temp.next=heroNode;
}
//按照编号插入节点
public void addOrder(HeroNode heroNode) {
boolean flag=false;
HeroNode temp=head;
while(true) {
if(temp.next==null) { //说明temp已经在链表的最后,可以添加
break;
}
if(temp.next.no>heroNode.no) {//说明heroNode处于temp和temp.next之间,可以添加
break;
}else if(temp.next.no==heroNode.no) { //说明该添加的编号已经存在,不可以添加
flag=true;
break;
}
temp=temp.next; //节点后移
}
if(flag==true) {
System.out.println("已经存在"+heroNode.no);
}else {
//插入链表之中
heroNode.next=temp.next;
temp.next=heroNode;
}
}
//修改节点
public void update(HeroNode heroNode) {
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
//定义一个辅助变量temp
HeroNode temp=head;
boolean flag = false;
while(true) {
if(temp.next==null) { //遍历完毕
break;
}
if(temp.next.no==heroNode.no) {
flag=true;
break;
}
temp=temp.next;
}
if(flag) {
temp.next.name=heroNode.name;
temp.next.nickname=heroNode.nickname;
}else {
System.out.println("没有找到");
}
}
//删除节点
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;
}
if(flag) {
temp.next=temp.next.next;
}else {
System.out.println("没有找到该节点");
}
}
//显示列表
public void List() {
//判断链表是否为空
if(head.next==null) {
System.out.println("链表为空");
}
HeroNode temp=head.next;
while (true) {
//判断是否到链表的最后
if(temp==null) {
break;
}
System.out.println(temp);
//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;
}
//显示,重写toString方法
public String toString(){
return "HeroNode[no="+no+",name="+name+",nickname="+nickname+"]";
}
}
双向链表
public static void main(String[] args) {
// TODO Auto-generated method stub
HeroNode2 hero1=new HeroNode2(1, "lhg", "前端");
HeroNode2 hero2=new HeroNode2(2, "wyf", "java后端");
HeroNode2 hero3=new HeroNode2(3, "ljk", "python");
HeroNode2 hero4=new HeroNode2(4, "lmy", "Unity");
DoubleLinkList list=new DoubleLinkList();
list.add(hero1);
list.add(hero2);
list.add(hero3);
list.add(hero4);
System.out.println("添加打印输出");
list.show();
System.out.println("修改最后一个节点");
HeroNode2 newhero=new HeroNode2(4, "aaaa", "Usdasdasdas");
list.update(newhero);
list.show();
System.out.println("删除最后一个节点");
list.delete(4);
list.show();
}
class DoubleLinkList{
//先初始化一个头结点,头节点不要动,不存放具体的数据
private HeroNode2 head=new HeroNode2(0, "", "");
//返回头节点
public HeroNode2 getHead() {
return head;
}
//遍历双向链表
public void show() {
if(head.next==null)System.out.println("链表为空");
HeroNode2 temp=head.next;
while (true) {
if(temp==null)break; //判断是否到最后
System.out.println(temp);
temp=temp.next;
}
}
//添加结点
public void add(HeroNode2 heroNode2) {
HeroNode2 temp=head;
while(true) {
if(temp.next==null)break;
temp=temp.next;
}
temp.next=heroNode2;
heroNode2.pre=temp;
}
//修改节点
public void update(HeroNode2 heroNode2) {
if(head.next==null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp=head;
boolean flag=false;
while(true) {
if(temp.next.no==heroNode2.no) {
flag=true;
break;
}
temp=temp.next;
}
if(flag) {
temp.next.name=heroNode2.name;
temp.next.nickname=heroNode2.nickname;
}else{
System.out.println("没有找到该节点");
}
}
//删除节点
public void delete(int no) {
if(head.next==null) {
System.out.println("输入为空");
return;
}
HeroNode2 temp=head.next;
boolean flag=false;
while(true){
if(temp==null) break;
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;
}
}
}
}
//定义双向链表
class HeroNode2{
public int no;
public String name;
public String nickname;
public HeroNode2 next; //指向下一个节点
public HeroNode2 pre; //指向前一个节点
// 构造器
public HeroNode2(int no,String name,String nickname) {
this.no=no;
this.name=name;
this.nickname=nickname;
}
//显示,重写toString方法
public String toString(){
return "HeroNode2[no="+no+",name="+name+",nickname="+nickname+"]";
}
}
单向环形列表
构建一个环形链表的思路
1.先创建第一个节点,让first指向该节点,并形成环形
2.后面每创建一个新的节点,就把该节点,加入到已有的环形链表中即可
遍历环形链表
1.先让一个辅助指针,指向first节点
2.然后通过一个while循环遍历该环形链表即可
public static void main(String[] args) {
CircleSingleLinkedList list=new CircleSingleLinkedList();
list.add(5);
list.showBoy();
}
class CircleSingleLinkedList {
//创建一个first节点,当前没有标号
private Boy first=new Boy(-1);
//添加小孩节点,构建成一个环形的链表
public void add(int nums) {
//nums 做一个数据校验
if(nums<1) {
System.out.println("nums的值不正确");
}
Boy curBoy=null; //辅助指针,帮助构建环形链表
for(int i=1;i<=nums;i++) {
Boy boy=new Boy(i);
if(i==1) { //第一个初始化
first=boy;
first.setNext(first);
curBoy=first;
}else {
curBoy.setNext(boy); //curBoy指向下一个
boy.setNext(first); //刚添加的节点指向第一个
curBoy=boy;
}
}
}
//遍历当前的环形列表
public void showBoy() {
//判断链表是否为空
if(first==null) {
System.out.println("链表为空");
return;
}
Boy curBoy=first;
while(true) {
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if(curBoy.getNext()==first) {
break;
}
curBoy=curBoy.getNext();
}
}
}
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 boy) {
this.next=boy;
}
}
约瑟夫环
思路一:
一、需求创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
二、报数时,helper和first指针同时移动m-1次
三、first指向的人出圈
first=first.next;
helper.next=first;
具体代码
public static void main(String[] args) {
CircleSingleLinkedList list=new CircleSingleLinkedList();
list.add(5);
list.showBoy();
list.countBoy(1, 2, 5);
}
class CircleSingleLinkedList {
//创建一个first节点,当前没有标号
private Boy first=new Boy(-1);
//添加小孩节点,构建成一个环形的链表
public void add(int nums) {
//nums 做一个数据校验
if(nums<1) {
System.out.println("nums的值不正确");
}
Boy curBoy=null; //辅助指针,帮助构建环形链表
for(int i=1;i<=nums;i++) {
Boy boy=new Boy(i);
if(i==1) { //第一个初始化
first=boy;
first.setNext(first);
curBoy=first;
}else {
curBoy.setNext(boy); //curBoy指向下一个
boy.setNext(first); //刚添加的节点指向第一个
curBoy=boy;
}
}
}
//遍历当前的环形列表
public void showBoy() {
//判断链表是否为空
if(first==null) {
System.out.println("链表为空");
return;
}
Boy curBoy=first;
while(true) {
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if(curBoy.getNext()==first) {
break;
}
curBoy=curBoy.getNext();
}
}
//计算小孩出圈的顺序
//starNo表示从第几个人开始数
//countNum表示数几下
//nums表示最初有多少个人在圈中
public void countBoy(int startNo,int countNum,int nums) {
if(startNo<1||startNo>nums||first==null) {
System.out.println("输入错误");
return;
}
Boy helper=first; //辅助指针
while(true) { //helper指向尾指针
if(helper.getNext()==first) {
break;
}
helper=helper.getNext();
}
//第一次报数之前,first和helper移动starNo-1次
for(int i=1;i<startNo;i++) {
helper=helper.getNext();
first=first.getNext();
}
//循环操作,直至剩下一人
while (true) {
if(first==helper) {
break;
}
for(int j=0;j<countNum-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",first.getNo());
}
}
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 boy) {
this.next=boy;
}
}