线性结构与非线性结构
线性结构
1.概述
元素数据之间存在一对一线性关系,分为两种不同的存储结构,顺序存储结构和链式存储结构
顺序存储的线性表称为顺序表,顺序表中的元素在地址上是连续的。链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
2.常见的线性结构:数组,队列,链表,栈
非线性结构
1.常见非线性结构:二维数组,多维数组,广义表,树结构,图结构
-----------------------------------------------------并不好看分割线----------------------------------------------------------
稀疏数组
1.使用场景:当一个数组中大部分元素为0或者为相同的值,可以使用稀疏数组来保存该数组
2.处理方式:首先记录数组有几行几列,有多少个不同的值,然后,把具有不同值的行与列记录在一个小规模数组中,达到缩小数组的目的
3.分析:
二维数组–>稀疏数组
a.遍历原有二维数组找到有效数据的个数记录为sum
b.根据sum可以创建稀疏数组sparseArr int[sum+1][3]
c.将二维数组中的有效数据存储进入稀疏数组
稀疏数组–>二维数组
a.读取稀疏数组第一行,根据第一行创建二维数组
b.将稀疏数组中的数据填入二维数组中
4.代码实现
package sparsearray;
import java.lang.reflect.Array;
public class SparseArray {
public static void main(String[] args) {
//二维数组变成稀疏数组
//创建一个二维数组
int array[][] = new int[11][11];
array[1][2] = 1;
array[2][3] = 2;
array[3][4] = 2;
//遍历看看二维数组的样子
for(int arr1[] : array){
for(int data : arr1){
System.out.printf("%d\t",data);
}
System.out.println();
}
//获取二维数组的行和列
int hang = array.length;
int lie = array[0].length;
//获取该二维数组不是0的个数
int sum = 0;
for(int i=0;i<hang;i++){
for(int j=0;j<lie;j++){
if(array[i][j]!=0){
sum++;
}
}
}
//创建稀疏数组
int sparseArray[][] = new int[sum+1][3];
sparseArray[0][0]=hang;
sparseArray[0][1]=lie;
sparseArray[0][2]=sum;
//向稀疏数组中添加值
int count = 0;
for(int i=0;i<hang;i++){
for(int j=0;j<lie;j++){
if(array[i][j]!=0){
count++;
sparseArray[count][0] = i;
sparseArray[count][1] = j;
sparseArray[count][2] = array[i][j];
}
}
}
//遍历稀疏数组
for(int i=0;i<sparseArray.length;i++){
System.out.printf("%d\t%d\t%d\t\n",sparseArray[i][0],sparseArray[i][1],sparseArray[i][2]);
}
//稀疏数组变成二维数组
//创建二维数组
int array1[][] = new int[sparseArray[0][0]][sparseArray[0][1]];
//向二维数组中添加值
for(int i =1;i<sparseArray.length;i++){
array1[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
//打印恢复后的二维数组
System.out.println("恢复后的二维数组");
for(int arr1[] : array1){
for(int data : arr1){
System.out.printf("%d\t",data);
}
System.out.println();
}
}
}
-----------------------------------------------------并不好看分割线----------------------------------------------------------
队列
1.概述:是一个有序列表,可以使用数组或者链表来实现,遵循先进先出原则
2.用数组模拟队列
package queue;
public class ArrayQueue {
private int maxLength;//队列的最长长度
private int front;//队列头
private int rear;//队列尾
private int[] arr;//队列本身
//初始化队列
public ArrayQueue(int length){
maxLength = length;
front = -1;//初始化参数,让队列头处于队列有数据的前一个
rear = -1;//初始化参数,让队列尾就是队列的最后一个数据
arr = new int[maxLength];
}
//判断队列是否为空
public boolean isEmpty(){
return front==rear;
}
//判断队列是否满
public boolean isFull(){
return rear == maxLength-1;
}
//给队列增加一个元素
public void add(int num){
if(isFull()){
throw new RuntimeException("队列已满,不能添加");
}
rear++;
arr[rear] = num;
}
//获取队列中的数据,并出队列
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
front++;
return arr[front];
}
//查看队列中的数据
public void showQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
for(int i=0;i<arr.length;i++){
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
//获取队列中的头元素
public int showFirst(){
System.out.println("头元素"+arr[front+1]);
return arr[front+1];
}
//获取队列中的尾元素
public int showLast(){
System.out.println("尾元素"+arr[rear]);
return arr[rear];
}
}
演示代码
package queue;
public class ArrayQueueTest {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(3);
queue.add(10);
queue.showFirst();
queue.showLast();
System.out.println();
queue.add(20);
queue.showFirst();
queue.showLast();
System.out.println();
queue.getQueue();
queue.showFirst();
queue.showLast();
System.out.println();
queue.add(30);
queue.showFirst();
queue.showLast();
System.out.println();
queue.add(40);
}
}
-----------------------------------------------------并不好看分割线----------------------------------------------------------
环形队列(数组模拟)
1.环形队列实在队列的基础上进行啦修改实现的
2.思路分析:
a.front变量:初始值为0,指向队列的第一个元素,也就是arr[front]就是队列的第一个元素
b.rear变量:初始值为0,指向队列的最后一个元素的后一个位置,比如队列有3个元素,初始front,rear都为0,当加入一个元素,front还为0,rear为1,当rear为2时,认为队列已满(因为希望空出一个空间作为约定)
c.队列满时的条件:(rear+1)% maxSize = front
d.队列为空时的条件:rear==front
e.队列中的有效的数据个数:(rear+maxSize-front)%maxSize
3.代码实现
package queue;
public class CircleArrayQueue {
private int maxLength;//队列的最长长度
private int front;//队列头,指向队列的第一个元素
private int rear;//队列尾,指向队列的最后一个元素的后一个位置
private int[] arr;//队列本身
//初始化
public CircleArrayQueue(int length){
maxLength = length;
arr = new int[maxLength];
front = 0;
rear = 0;
}
//判断队列是否为空
public boolean isEmpty(){
return front==rear;
}
//判断队列是否满
public boolean isFull(){
return (rear+1) % maxLength == front;
}
//给队列增加一个元素
public void add(int num){
if(isFull()){
throw new RuntimeException("队列已满,不能添加");
}
arr[rear] = num;
rear = (rear+1)%maxLength;
}
//获取队列中的数据,并出队列
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
int var = arr[front];
System.out.println(var);
front = (front+1)%maxLength;
return var;
}
//查看队列中的数据
public void showQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
int goodLength = (rear+maxLength-front)%maxLength;
for(int i=front;i<front+goodLength;i++){
System.out.printf("arr[%d]=%d\n",i%maxLength,arr[i%maxLength]);
}
}
//获取队列中的头元素
public int showFirst(){
System.out.println("头元素"+arr[front]);
return arr[front];
}
//获取队列中的尾元素
public int showLast(){
System.out.println("尾元素"+arr[(rear+maxLength-1)%maxLength]);
return arr[(rear+maxLength-1)%maxLength];
}
}
package queue;
public class CircleArrayQueueTest {
public static void main(String[] args) {
CircleArrayQueue queue = new CircleArrayQueue(3);
queue.add(10);
queue.showQueue();
System.out.println("-----------------------------------");
queue.add(20);
queue.showQueue();
System.out.println("-----------------------------------");
queue.getQueue();
queue.showQueue();
System.out.println("-----------------------------------");
queue.add(30);
queue.showQueue();
System.out.println("-----------------------------------");
queue.getQueue();
queue.showQueue();
System.out.println("-----------------------------------");
queue.add(40);
queue.showQueue();
System.out.println("-----------------------------------");
}
}
-----------------------------------------------------并不好看分割线----------------------------------------------------------
单向链表
1.概述:
a.链表是以节点的方式存储的
b.每个节点包含data域,next域:指向下一个节点
c.链表的各个节点不一定是连续存储的
d.链表分为带头结点的链表和没有头节点的链表,根据实际需求来确定
2.增删改查代码实现
package linkedlist;
public class SingleLinkedListDemo {
public static void main(String[] args) {
/*HeroNode heroNode1 = new HeroNode(1,"1","1");
HeroNode heroNode2 = new HeroNode(2,"2","2");
HeroNode heroNode3 = new HeroNode(3,"3","3");
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.addHeroNode(heroNode1);
singleLinkedList.addHeroNode(heroNode2);
singleLinkedList.addHeroNode(heroNode3);
singleLinkedList.findHeroNode();*/
HeroNode heroNode4 = new HeroNode(4,"4","4");
HeroNode heroNode5 = new HeroNode(5,"5","5");
HeroNode heroNode6 = new HeroNode(6,"6","6");
SingleLinkedList singleLinkedListByOrder = new SingleLinkedList();
singleLinkedListByOrder.addByOrder(heroNode5);
singleLinkedListByOrder.addByOrder(heroNode4);
singleLinkedListByOrder.addByOrder(heroNode6);
singleLinkedListByOrder.update(new HeroNode(4,"44","44"));
singleLinkedListByOrder.delete(4);
singleLinkedListByOrder.delete(6);
singleLinkedListByOrder.findHeroNode();
}
}
class SingleLinkedList{
//头节点
private HeroNode head = new HeroNode(0,"","");
//增加一个节点
public void addHeroNode(HeroNode heroNode){
HeroNode lastHeroNode = findLastHeroNode();
lastHeroNode.next = heroNode;
}
//找到最后一个节点
public HeroNode findLastHeroNode(){
//辅助节点
HeroNode help = head;
while (true){
if(help.next==null){
break;
}
help = help.next;
}
return help;
}
//遍历链表
public void findHeroNode(){
HeroNode hn = head.next;
if(hn==null){
return;
}
while (true){
if(hn==null){
break;
}
System.out.println(hn);
hn = hn.next;
}
}
//按照no排序插入链表
public void addByOrder(HeroNode heroNode){
HeroNode help = head;
while(true){
if(help.next==null){
break;
}
if(help.next.no>heroNode.no){
break;
}else if(help.next.no==heroNode.no){
System.out.println("啊重复啦");
return;
}
help = help.next;
}
heroNode.next=help.next;
help.next = heroNode;
}
//修改一个节点
public void update(HeroNode heroNode){
HeroNode help = head;
while(true){
if(help.next==null){
break;
}
if(heroNode.no==help.next.no){
HeroNode acc = help.next.next;
if(acc==null){
help.next=heroNode;
}else{
help.next=heroNode;
help.next.next=acc;
}
break;
}
help = help.next;
}
}
//删除一个节点
public void delete(int no){
HeroNode help = head;
while(true){
if(help.next==null){
break;
}
if(help.next.no == no){
help.next = help.next.next;
break;
}
help = help.next;
}
}
}
class HeroNode{
public int no;
public String name;
public String nikeName;
public HeroNode next;
public HeroNode(int no, String name, String nikeName) {
this.no = no;
this.name = name;
this.nikeName = nikeName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nikeName='" + nikeName + '\'' +
'}';
}
}
-----------------------------------------------------并不好看分割线----------------------------------------------------------
双向链表
1.与单向链表的区别
a.单向链表只能一个方向查找,而双向链表可以向前,向后查找
b.双向链表可以实现自我删除
package linkedlist;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addSecretary(new Secretary(1,"小冰",22,173,8));
doubleLinkedList.addSecretary(new Secretary(2,"小文",21,167,9));
doubleLinkedList.addSecretary(new Secretary(3,"小欣",24,169,7));
System.out.println("修改前");
doubleLinkedList.getSecretarys();
doubleLinkedList.updateSecertarys(2,10);
System.out.println("修改后");
doubleLinkedList.getSecretarys();
System.out.println("删除后");
doubleLinkedList.deleteSecertarys(3);
doubleLinkedList.getSecretarys();
}
}
class DoubleLinkedList{
private Secretary head = new Secretary(0,"",0,0,0);
public Secretary getHead() {
return head;
}
//增加(添加到末尾)
public void addSecretary(Secretary secretary){
//找到最后一个节点得辅助节点
Secretary temp = head;
while(true){
if(temp.next == null){
break;
}
temp = temp.next;
}
temp.next = secretary;
secretary.pre = temp;
}
//遍历链表
public void getSecretarys(){
Secretary temp = head;
if(temp.next == null){
return;
}
while(true){
if(temp == null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
//修改
public void updateSecertarys(int no,int score){
//辅助节点
Secretary secretary = head;
if(secretary.next == null){
return;
}
while(true){
if(secretary.next.no == no){
secretary.next.score = score;
break;
}
secretary = secretary.next;
}
}
//删除节点
public void deleteSecertarys(int no){
//辅助节点
Secretary secretary = head;
while(true){
if(secretary.no == no){
secretary.pre.next = secretary.next;
if(secretary.next != null){
secretary.next.pre = secretary.pre;
}
break;
}
secretary = secretary.next;
}
}
}
class Secretary{
public int no;
public String name;
public int age;
public int height;
public int score;
public Secretary next;
public Secretary pre;
public Secretary(int no, String name, int age, int height, int score) {
this.no = no;
this.name = name;
this.age = age;
this.height = height;
this.score = score;
}
@Override
public String toString() {
return "Secretary{" +
"no=" + no +
", name='" + name + '\'' +
", age=" + age +
", height=" + height +
", score=" + score +
'}';
}
}
-----------------------------------------------------并不好看分割线----------------------------------------------------------
约瑟夫问题:假设1,2,3…n的n个人围坐一圈,约定编号为k的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的人又出列,以此类推,直到所有人都出列,由此产生一个出队编号的序列
解决约瑟夫问题使用单向环形链表
package linkedlist;
public class Josepfu {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.showBoys();
circleSingleLinkedList.jse(1,2);
}
}
class CircleSingleLinkedList{
private Boy first = null;
//增加
public void addBoy(int num){
if(num<2){
System.out.println("至少2个");
return;
}
Boy preBoy = null;
Boy thisBoy = null;
for(int i=1;i<num+1;i++){
Boy boy = new Boy(i);
if(i==1){
first = boy;
preBoy = boy;
preBoy.setBoy(boy);
}else{
thisBoy = boy;
preBoy.setBoy(boy);
thisBoy.setBoy(first);
preBoy = boy;
}
}
}
//循环遍历
public void showBoys(){
Boy boy = first;
while(true){
System.out.println(boy);
if(boy.getBoy() == first){
break;
}
boy = boy.getBoy();
}
}
//约瑟夫问题
public void jse(int startNo,int number){
//两个辅助节点,一个指向当前节点,一个指向当前节点的前一个
Boy start = first;
Boy helper = first;
//这个循环是让helper是start的前一个
while(true){
if(helper.getBoy() == first){
break;
}
helper = helper.getBoy();
}
//根据从那个开始,确定start和helper的位置
for(int i=1;i<startNo;i++){
helper = helper.getBoy();
start = start.getBoy();
}
while(true){
if(start == helper){
break;
}
for(int i=1;i<number;i++){
helper = helper.getBoy();
start = start.getBoy();
}
System.out.println("出圈:"+start);
helper.setBoy(start.getBoy());
start = start.getBoy();
}
}
}
class Boy{
private int no;
private Boy boy;
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getBoy() {
return boy;
}
public void setBoy(Boy boy) {
this.boy = boy;
}
@Override
public String toString() {
return "Boy{" +
"no=" + no +
'}';
}
}