【备注】2026年3月10日开始学习数据结构与算法,参考B站视频学习。
本节拖拖拉拉,耗时8天才学完,服了自己。
稀疏数组 sparsearray

使用数组来储存棋盘情况
因为该二维数组的很多值是默认值0, 因此记录了很多没有意义的数据.
稀疏数组的意义
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方法是:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模.
- 假设原数组中有n个特殊值,则对应的稀疏数组一般有
n+1行,3 列。其中第一行依次储存原始数组的行数、列数、有多少个不同的值;后面的行数依次记录每个不同的值所在的行数、列数、值。

稀疏数组
思路分析

思路分析图
- 先将二维数组保存为稀疏数组
(1)遍历原始的二维数组,得到有效数组的个数 sum
(2)根据 sum创建稀疏数组 :
1
| int[][] sparseArr = int[sum+1][3];
|
(3)根据稀疏数组的定义,依次填写稀疏数组的值
假设原数组中有n个特殊值,则对应的稀疏数组一般有n+1行,3 列。其中第一行依次储存原始数组的行数、列数、有多少个不同的值;后面的行数依次记录每个不同的值所在的行数、列数、值。
- 稀疏数组还原为二维数组
(1)先根据稀疏数组的第一行,创建原始数组。
(2)然后再读取稀疏数组的后面几行数据,并赋值给原始的二维数组即可。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class sparsearr { public static void main(String[] args){ int[][] arr1 = new int[6][7]; arr1[0][3] = 22; arr1[0][6] = 15; arr1[1][1] = 11; arr1[1][5] = 17; arr1[2][3] = -6; arr1[3][5] = 39; arr1[4][0] = 91; arr1[5][2] = 28;
System.out.println("原始数组"); for(int[] row : arr1){ for(int data : row){ System.out.print(data+"\t"); } System.out.println(); }
int count = 0; for(int i=0 ; i<arr1.length ; i++){ for(int j=0 ; j<arr1[i].length ; j++){ if(arr1[i][j] !=0){ count++; } } } System.out.printf("原始数组中一共有%d个特殊值",count); int[][] arr2 = new int[count+1][3]; arr2[0][0] = 6; arr2[0][1] = 7; arr2[0][2] = count;
int row = 1; for(int m=0; m<arr1.length ;m++){ for(int n=0; n<arr1[m].length ;n++){ if(arr1[m][n] !=0){ arr2[row][0] = m; arr2[row][1] = n; arr2[row][2] = arr1[m][n]; row++; } } }
System.out.println("稀疏数组"); for(int[] r : arr2){ for(int data : r){ System.out.print(data+"\t"); } System.out.println(); } } }
|
队列 Queue
队列介绍
- 队列是一个有序列表,可以用数组或是链表来实现。
- 遵循先入先出(FIFO)的原则。即:先存入队列的数据,要先取出。后存入的要后取出。队列像排队买票
数组队列
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中maxSize 是该队
列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front 及rear 分别记录队列前后端的下标,
front 会随着数据输出而改变,而rear 则是随着数据输入而改变,如图所示

数组模拟队列示意图
- 当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析
- 将尾指针往后移:rear+1 , 当front == rear 【空】
- 若尾指针rear 小于队列的最大下标maxSize-1,则将数据存入rear 所指的数组元素中,否则无法存入数据。
rear == maxSize - 1[队列满]
数组模拟队列-代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| import java.util.Scanner; public class Queue { public static void main(String[] args){ ArrayQueue queue = new ArrayQueue(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.println("取出的数据为"+res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'h': try { int res = queue.headQueue(); System.out.println("队列头的数据为:"+res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; default: break; } } System.out.println("程序结束啦!"); }
static class ArrayQueue{ private int maxSize; private int front; private int rear; private int[] arr; public ArrayQueue(int arrMaxSize){ maxSize = arrMaxSize; arr = new int[maxSize]; front = -1 ; rear = -1;
}
public boolean isFull(){ return rear == maxSize -1; }
public boolean isEmpty(){ return rear==front; }
public void addQueue(int n){ if(isFull()){ System.out.println("队列排满,不能加入数据~"); return; } rear++; arr[rear] = n; }
public int getQueue(){ if(isEmpty()){ System.out.println("队列为空,不能取数据"); throw new RuntimeException("队列空,取不了数据啊!"); } front++; return arr[front]; }
public void showQueue(){ if(isEmpty()){ System.out.println("队列数据为空!"); return; } for(int i=0; i<arr.length ; i++){ System.out.printf("arr[%d] = %d \n",i,arr[i]); } }
public int headQueue(){ if(isEmpty()){ throw new RuntimeException("队列是空的,没有数据!"); } return arr[front + 1]; } }
}
|
数组模拟循环队列
思路分析
对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的
时候需要注意(rear + 1) % maxSize == front 满]
- rear == front [空]
- 分析示意图:

数组模拟循环队列示意图
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| import java.util.Scanner;
public class CircleQueue { public static void main(String[] args){ Circle queue = new Circle(5); char num = ' '; boolean loop = true; Scanner scanner = new Scanner(System.in);
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):查看队列头的数据"); num = scanner.next().charAt(0);
switch (num) { case 's': try { queue.showArr(); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; case 'a': try { System.out.println("请输入一个数:"); int value = scanner.nextInt(); queue.addCircle(value); System.out.println("添加数据成功~"); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'g': try { int e = queue.getQueue(); System.out.println("取出的数据为"+e); } catch (Exception e) { System.out.println(e.getMessage()); } break;
case 'h': try { int e = queue.headArr(); System.out.println("队列头数据为"+e); } catch (Exception e) { System.out.println(e.getMessage()); } break; default: break; } } System.out.println("程序结束啦!"); }
static class Circle{ private int maxSize; private int[] circleArr; private int front; private int rear;
public Circle(int arrmaxSize){ maxSize = arrmaxSize; circleArr = new int[maxSize]; front = rear =0; } public boolean isFull(){ return front == (rear + 1)%maxSize; }
public boolean isEmpty(){ return rear == front ; }
public void addCircle(int a){ if(isFull()){ throw new RuntimeException("队列已排满啦!"); } rear = (rear+1)%maxSize; circleArr[rear] = a; }
public int getQueue(){ if(isEmpty()){ throw new RuntimeException("队列是空的,你咋取数据?"); } front = (front + 1)%maxSize; return circleArr[front]; }
public void showArr(){ if(isEmpty()){ throw new RuntimeException("队列数据是空的啊,我给你展示个啥?"); } int count = (rear - front + maxSize)%maxSize;
for(int i=0 ; i<count ;i++){ int index = (front + 1 + i)%maxSize; System.out.printf("circleArr[%d]=%d",index,circleArr[index]); System.out.println(); } }
public int headArr(){ if(isEmpty()){ throw new RuntimeException("队列数据是空的啊,头数据自然没有啊!"); } return circleArr[(front+1)%maxSize]; } } }
|
单链表
背景及定义
链表是有序的列表,但是它在内存中是存储如下

单链表示意图
小结:
- 链表是以节点的方式来储存,是链式储存.
- 每个节点包含
data 域 , next 域. next域作用: 指向下一节点.
- 如图发现, 链表的各个节点不一定是连续储存.
- 链表分为带头节点和无头节点的链表, 根据实际开发需要自行选择.
单链表(带头节点)逻辑结构示意图如下:

应用实例1 简单添加
使用带 head 头的单项列表实现 水浒英雄排行榜的管理以及对英雄人物的增删改查操作 .
(1) 新添英雄时,直接添加到链表的尾部

思路分析图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| public class SingleLinkedListDemo {
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 heroList = new SingleLinkedList(); heroList.add(hero1); heroList.add(hero2);
heroList.add(hero3);
heroList.add(hero4); heroList.list(); }
static class SingleLinkedList{ private HeroNode head = new HeroNode(0, "", "");
public void add(HeroNode heroNode){ HeroNode temp = head;
while(true){ if(temp.next == null){ break; } temp = temp.next; }
temp.next = heroNode; }
public void list(){ if(head.next == null){ System.out.println("链表为空!"); return; }
HeroNode temp = head.next; while (true) { System.out.println(temp); if(temp.next == null){ break; } temp = temp.next; } } }
static class HeroNode{ public int number; public String name; public String nickname; public HeroNode next;
public HeroNode(int num,String name,String nickname){ this.number = num; this.name = name; this.nickname = nickname; }
@Override public String toString() { return "HeroNode [num=" + number + ", name=" + name + ", nickname=" + nickname + "]"; } } }
|
应用实例2 顺序添加
按英雄编号的顺序插入英雄列表: (若该编号已存在则提示无法插入)
先找到新节点需要插入的位置. 设置一个temp变量, 当temp 的下一节点的编号大于新节点的编号,则说明找到了该位置.
问题处理: temp的下一节点的编号与新节点的编号无非存在三种关系, 大于 等于 小于,
如果 temp.next.number < 新节点.number , 则不用处理, 继续操作temp的下一节点.
如果 temp.next.number = 新节点.number , 提示无法添加.
如果 temp.next.number > 新节点.number , 可以添加.
添加新节点的方法
(1) 将新节点的 next 指向 temp的下一节点: 新节点.next = temp.next
(2) 再将temp的 next 指向 新节点: temp.next = 新节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| public class SingleLinkedListDemo2 {
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 heroList = new SingleLinkedList(); heroList.add(hero1); heroList.add(hero3); heroList.add(hero2); heroList.add(hero4); heroList.list(); }
static class SingleLinkedList{ private HeroNode head = new HeroNode(0, "", "");
public void add(HeroNode heroNode){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ break; } else if (temp.next.number > heroNode.number) { break; } else if(temp.next.number == heroNode.number){ flag = true; break; } temp = temp.next; } if(flag){ System.out.printf("编号%d已存在,无法添加",heroNode.number); } else{ heroNode.next = temp.next; temp.next = heroNode; }
}
public void list(){ if(head.next == null){ System.out.println("链表为空!"); return; }
HeroNode temp = head.next; while (true) { System.out.println(temp); if(temp.next == null){ break; } temp = temp.next; } } }
static class HeroNode{ public int number; public String name; public String nickname; public HeroNode next;
public HeroNode(int num,String name,String nickname){ this.number = num; this.name = name; this.nickname = nickname; }
@Override public String toString() { return "HeroNode [num=" + number + ", name=" + name + ", nickname=" + nickname + "]"; } } }
|
应用实例3 修改节点
修改指定编号节点的信息
- 遍历链表, 找到对应编号的节点
- 直接修改节点的 name 和 nickname 属性
- 找到后立即 break
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
public class SingleLinkedListDemo3 {
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 heroList = new SingleLinkedList(); heroList.add(hero1); heroList.add(hero3); heroList.add(hero2); heroList.add(hero4); heroList.list(); HeroNode newHero = new HeroNode(3,"没有用","智少"); heroList.change(newHero); System.out.println("修改后的列表信息"); heroList.list();
}
static class SingleLinkedList{ private HeroNode head = new HeroNode(0, "", "");
public void add(HeroNode heroNode){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ break; } else if (temp.next.number > heroNode.number) { break; } else if(temp.next.number == heroNode.number){ flag = true; break; } temp = temp.next; } if(flag){ System.out.printf("编号%d已存在,无法添加",heroNode.number); } else{ heroNode.next = temp.next; temp.next = heroNode; }
}
public void change(HeroNode newHeroNode){ HeroNode temp = head.next; boolean flag = false; while(true){ if(temp == null){ break; } if(temp.number == newHeroNode.number){ System.out.println("找到了需要修改的英雄"); temp.name = newHeroNode.name; temp.nickname = newHeroNode.nickname; flag = true; } temp = temp.next; } if (!flag) { System.out.printf("你输入的编号%d有误,列表中未找到\n",newHeroNode.number); }
}
public void list(){ if(head.next == null){ System.out.println("链表为空!"); return; }
HeroNode temp = head.next; while (true) { System.out.println(temp); if(temp.next == null){ break; } temp = temp.next; } } }
static class HeroNode{ public int number; public String name; public String nickname; public HeroNode next;
public HeroNode(int num,String name,String nickname){ this.number = num; this.name = name; this.nickname = nickname; }
@Override public String toString() { return "HeroNode [num=" + number + ", name=" + name + ", nickname=" + nickname + "]"; } } }
|
应用实例4 删除节点
- 找到待删除节点的 前驱节点 temp
- 删除逻辑: temp.next = temp.next.next (跳过待删除的节点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
public class SingleLinkedListDemo4 {
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 heroList = new SingleLinkedList(); heroList.add(hero1); heroList.add(hero3); heroList.add(hero2); heroList.add(hero4); heroList.list(); HeroNode newHero = new HeroNode(3,"没有用","智少"); heroList.change(newHero); System.out.println("修改后的列表信息"); heroList.list(); HeroNode deleteHero = new HeroNode(3,"没有用","智少"); heroList.delete(deleteHero); System.out.println("删除后的列表信息"); heroList.list();
}
static class SingleLinkedList{ private HeroNode head = new HeroNode(0, "", "");
public void add(HeroNode heroNode){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ break; } else if (temp.next.number > heroNode.number) { break; } else if(temp.next.number == heroNode.number){ flag = true; break; } temp = temp.next; } if(flag){ System.out.printf("编号%d已存在,无法添加",heroNode.number); } else{ heroNode.next = temp.next; temp.next = heroNode; }
}
public void change(HeroNode newHeroNode){ HeroNode temp = head.next; boolean flag = false; while(true){ if(temp == null){ break; } if(temp.number == newHeroNode.number){ System.out.println("找到了需要修改的英雄"); temp.name = newHeroNode.name; temp.nickname = newHeroNode.nickname; flag = true; break; } temp = temp.next; } if (!flag) { System.out.printf("你输入的编号%d有误,列表中未找到\n",newHeroNode.number); }
}
public void delete(HeroNode target){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ System.out.println("链表为空!"); break; } if(temp.next.number == target.number){ System.out.println("找到了需要删除的节点"); 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("链表为空!"); return; }
HeroNode temp = head.next; while (true) { System.out.println(temp); if(temp.next == null){ break; } temp = temp.next; } } }
static class HeroNode{ public int number; public String name; public String nickname; public HeroNode next;
public HeroNode(int num,String name,String nickname){ this.number = num; this.name = name; this.nickname = nickname; }
@Override public String toString() { return "HeroNode [num=" + number + ", name=" + name + ", nickname=" + nickname + "]"; } } }
|
应用实例5 获取指定节点
查找单链表中倒数第K个节点
- 先获取链表的总长度 length
- 然后从头开始遍历链表 length - K 个节点,即可得到倒数第 K 个节点.
- 若找到节点,则返回该节点, 否则返回 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
public class SingleLinkedListDemo4 {
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 heroList = new SingleLinkedList(); heroList.add(hero1); heroList.add(hero3); heroList.add(hero2); heroList.add(hero4); heroList.list(); HeroNode newHero = new HeroNode(3,"没有用","智少"); heroList.change(newHero); System.out.println("队列长度为:"+heroList.length(heroList.getHead())); System.out.println("修改后的列表信息"); heroList.list(); HeroNode deleteHero = new HeroNode(3,"没有用","智少"); heroList.delete(deleteHero); System.out.println("删除后的列表信息"); heroList.list(); System.out.println("队列长度为:"+heroList.length(heroList.getHead()));
HeroNode res = heroList.findLastNode(1); System.out.println(res); }
static class SingleLinkedList{ private HeroNode head = new HeroNode(0, "", "");
public HeroNode getHead(){ return head; }
public void add(HeroNode heroNode){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ break; } else if (temp.next.number > heroNode.number) { break; } else if(temp.next.number == heroNode.number){ flag = true; break; } temp = temp.next; } if(flag){ System.out.printf("编号%d已存在,无法添加",heroNode.number); } else{ heroNode.next = temp.next; temp.next = heroNode; }
}
public void change(HeroNode newHeroNode){ HeroNode temp = head.next; boolean flag = false; while(true){ if(temp == null){ break; } if(temp.number == newHeroNode.number){ System.out.println("找到了需要修改的英雄"); temp.name = newHeroNode.name; temp.nickname = newHeroNode.nickname; flag = true; break; } temp = temp.next; } if (!flag) { System.out.printf("你输入的编号%d有误,列表中未找到\n",newHeroNode.number); }
}
public void delete(HeroNode target){ HeroNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ System.out.println("链表为空!"); break; } if(temp.next.number == target.number){ System.out.println("找到了需要删除的节点"); flag = true; break; } temp = temp.next; } if(flag){ temp.next = temp.next.next; } else{ System.out.println("没有找到你想删除的节点!"); } } public int length(HeroNode head){ if(head.next == null){ return 0; } int length = 0 ; HeroNode temp = head.next; while(temp != null){ length++; temp = temp.next; } return length; } public HeroNode findLastNode(int K){ if(head.next == null){ System.out.println("链表为空!"); return null; } HeroNode temp = head.next; int length = length(head); if(K>length || K<=0){ System.out.println("K值不合法"); return null; } for(int i=0; i < length - K;i++){ temp = temp.next; } return temp; }
public void list(){ if(head.next == null){ System.out.println("链表为空!"); return; }
HeroNode temp = head.next; while (true) { System.out.println(temp); if(temp.next == null){ break; } temp = temp.next; } } }
static class HeroNode{ public int number; public String name; public String nickname; public HeroNode next;
public HeroNode(int num,String name,String nickname){ this.number = num; this.name = name; this.nickname = nickname; }
@Override public String toString() { return "HeroNode [num=" + number + ", name=" + name + ", nickname=" + nickname + "]"; } } }
|
应用实例6 链表反转
有点难,要仔细理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| public class ReverseList { public static void main(String[] args){ StudentList myStudentList = new StudentList(); StudentNode student1 =new StudentNode(1,"张三",14); StudentNode student2 =new StudentNode(2,"李四",18); StudentNode student3 =new StudentNode(3,"王二",11); StudentNode student4 =new StudentNode(4,"麻子",19);
myStudentList.add(student1); myStudentList.add(student3); myStudentList.add(student4); myStudentList.add(student2); System.out.println("初始列表"); myStudentList.showList(); System.out.println("反转列表"); myStudentList.reverseList(myStudentList.getHead()); myStudentList.showList();
}
static class StudentList{ private StudentNode head = new StudentNode(0, "null", 0); public StudentNode getHead(){ return head; }
public void add(StudentNode stu){ StudentNode temp = head; boolean flag = false; while(true){ if(temp.next == null){ flag =true; break; } if(temp.next.number > stu.number){ flag = true; break; } if(temp.next.number == stu.number){ break; } temp = temp.next; }
if(flag){ stu.next = temp.next; temp.next = stu; } else{ System.out.println("学号存在相同的,无法添加"); }
}
public void reverseList(StudentNode head){ if(head.next == null || head.next.next == null){ return; } StudentNode rehead = new StudentNode(0,"",0); StudentNode current = head.next; StudentNode nextNode = null; while(true){ if(current == null){ break; } nextNode = current.next; current.next = rehead.next; rehead.next = current; current = nextNode; }
head.next = rehead.next; }
public void showList(){ StudentNode temp = head; while(true){ if(temp.next == null){ break; } temp = temp.next; System.out.println(temp.toString()); } } }
static class StudentNode{ private int number; private String name; private int age;
public StudentNode next;
public StudentNode(int number,String name,int age){ this.age = age; this.name = name; this.number = number; }
@Override public String toString() { return "Student[姓名:" + name + ", 学号: " + number +", 年龄:" + age +" 岁]"; }
} }
|
应用实例7 逆序打印
注:需要掌握栈的知识点。
从尾到头打印单链表
应用实例8 合并链表
合并两个有序单链表,合并之后的链表依然有序。
双向链表

双向链表示意图
从示意图可以看出来,双向链表对比单链表只是每个节点都多了一个pre功能,next是指向下一节点,那么以此类推pre就是指向上一节点。
基本操作
分析双向链表的遍历、添加、修改、删除节点的代码实现
与单链表没啥区别。只不过既可以从前往后遍历也可以从后往前遍历。
若添加的节点是加到链表的尾部,
先找到链表的最后一个节点
temp.next = newNode;
newNode.pre = temp;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
- 修改
与单链表没啥区别。
- 删除
1. 因为是双向链表,因此可以直接删除 temp 节点
2. 找到需要删除的节点 temp
3. ```java temp.pre.next = temp.next; temp.next.pre = temp.pre;
|
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| 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 heroList = new DoubleLinkedList(); heroList.add(hero1); heroList.add(hero2); heroList.add(hero3); heroList.add(hero4); heroList.showList(); heroList.change(1); heroList.showList(); heroList.delete(4); heroList.showList(); } public static class DoubleLinkedList{ private HeroNode head = new HeroNode(0, null); public HeroNode getHead(){ return head; } public void showList(){ HeroNode temp = head;
while (temp.next != null) { temp = temp.next; System.out.println(temp.toString()); } } public void add(HeroNode newNode){ HeroNode temp = head; while(temp.next != null){ temp = temp.next; }
temp.next = newNode; newNode.pre = temp ; } public void change(int a){ HeroNode temp = head; boolean flag = false; while (true) { if(temp.number == a){ System.out.println("找到了需要修改的节点"); flag = true; break; } if(temp.next == null){ break; } temp = temp.next; }
if(flag == true){ System.out.println("修改之前节点信息为:"+temp.toString()); temp.name = "牛逼" ; System.out.println("修改节点信息成功"); System.out.println("修改后节点信息为:"+temp.toString()); } else{ System.out.println("没找到你想修改的节点"); } } public void delete(int a){ HeroNode temp = head; if(a<0){ System.out.println("请输入正确的节点编号"); } boolean flag = false; while (true) { if(temp.number == a){ flag = true; break; } if(temp.next == null){ System.out.println("你想删除的节点不存在"); break; } temp = temp.next; } if (flag) { temp.pre.next = temp.next; if(temp.next != null) { temp.next.pre = temp.pre; } System.out.println("删除节点成功!"); } } } public static class HeroNode{ private String name; private int number; public HeroNode next; public HeroNode pre;
public HeroNode(int number,String name){ this.name = name; this.number = number; }
@Override public String toString() { return "HeroNode["+number+", name: "+ name + "]"; } } }
|
环形链表
约瑟夫问题 Josepfu
设编号为1、2,… n 的 n 个人围坐在一圈,若约定从编号为 k 的那个人开始从1开始报数,数到 m 的那个人出列,然后从刚刚出列的那个人的下一位继续从1开始报数,数到 m 的那个人又出列,以此类推,直到所有人出列,由此产生一个出队编号的序列。

约瑟夫问题示意图
例如上图有5位小朋友,假设从1号小朋友开始数2个数,来开始游戏。
第一次出列的是2号小朋友,此时继续从3号小朋友开始数2个数;
第二次出列的是4号小朋友,此时继续从5号小朋友开始数2个数;
第三次出列的是1号小朋友,此时继续从3号小朋友开始数2个数;
第四次出列的是5号小朋友,此时只剩下3号一位小朋友了;
第5次出列的是3号小朋友。
代码分析

约瑟夫问题的代码思路1

约瑟夫问题的代码思路2
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
public class Josepfu { public static void main(String[] args){ CircleSingleLinkedList BoyList = new CircleSingleLinkedList(); BoyList.addBoy(25); BoyList.showList(); BoyList.start(25, 1, 2); } }
class CircleSingleLinkedList{ private Boy first = null; public void addBoy(int 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 ; } curBoy.setNext(boy); curBoy = boy; curBoy.setNext(first); } } public void showList(){ Boy temp = first; while (true) { System.out.println(temp.toString()); if(temp.getNext() == first){ break; } temp = temp.getNext(); } }
public void start(int nums,int k,int n){ Boy temp = first; while (true) { if(temp.getNext() == first){ break; } temp = temp.getNext(); } for(int i=0; i<k-1;i++){ first = first.getNext(); temp = temp.getNext(); } while (true) { if(temp == first){ break; } for(int j=0;j<n-1;j++){ first = first.getNext(); temp = temp.getNext(); } System.out.printf("小孩【 %d 】出圈 \n",first.getNo()); first = first.getNext(); temp.setNext(first) ; } System.out.println("最后出圈的小孩【" + first + "】" ); } }
class Boy{ private int no; private Boy next; public Boy(int no){ this.no = no; } public int getNo(){ return no; } public Boy getNext() { return next; } public void setNext(Boy next) { this.next = next; } @Override public String toString() { return "Boy[" + this.getNo() +"]"; } }
|
总结对比:数组和链表
| 特性 |
数组栈 |
链表栈 |
| 栈满判断 |
必须(固定容量) |
不需要(动态扩容) |
| 栈空判断 |
必须 |
必须 |
| 内存分配 |
连续内存,提前分配 |
分散内存,按需分配 |
| 空间效率 |
可能浪费(固定长度) |
更高效(用多少占多少) |
总结
- 链表栈不需要设置栈满判断,因为链表节点是动态创建的,没有固定容量限制,只要系统内存充足就可以一直入栈。
- 数组栈必须设置栈满判断,因为数组长度固定,元素数量达到数组长度后无法继续入栈。
- 无论哪种栈,栈空判断都是必须的(出栈 / 遍历前判断),避免空指针异常,提升程序健壮性。
对对对