单向链表
实现思路:
- 创建一个Node结点,有data和next结点。
- 创建一个链表类,里面存一个head结点。
具体JAVA代码实现:
// 结点类
class Node<E> {
// 存放的数据
public E data;
// 指向下一个结点
public Node<E> next;
public Node(E data) {
this.data = data;
}
public Node() {
super();
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
class SingleLinkedList<E> {
// 链表的头结点,并初始化为无数据的头结点
private Node<E> head = new Node<E>();
// 链表的大小
private int size;
// 添加数据到链表的方法
public boolean add(E data) {
// 这里采用尾插法,在结点尾插入新数据,并且头结点只做为访问结点,不存放数据
Node<E> temp = head;
while (temp.next != null) {
temp = temp.next;
}
// 找到next为null的结点,赋值为新结点
temp.next = new Node<E>(data);
size++;
return true;
}
// 展示链表中的数据
public void show() {
if (head.next == null) {
System.out.println("链表为null");
return;
}
Node<E> temp = head;
// 遍历链表,展示数据
while (temp.next != null) {
temp = temp.next;
System.out.println(temp);
}
}
// 获取链表的长度
public int size() {
return size;
}
// 修改数据
public boolean update(int index, E updateData) {
if (index > size) {
return false;
}
// 根据索引修改数据
Node<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.data = updateData;
return true;
}
// 删除节点
public boolean delete(int index) {
if (index > size) {
return false;
}
Node<E> temp = head;
// 根据索引删除对应的结点
// 需要找到待删除结点的父结点
for (int i = 0; i < index - 1; i++) {
temp = temp.next;
}
// 将父结点的next指向待删除结点的next
temp.next = temp.next.next;
size--;
return true;
}
// 获取节点
public Node<E> getNode(int index) {
if (index > size) {
return null;
}
Node<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp;
}
// 链表反转
public void reverse() {
if (size == 1) {
return;
}
// 定义一个临时的头结点
Node<E> tempHead = new Node<E>();
// 在定义一个临时的结点
Node<E> temp = tempHead;
// 从结点个数倒叙遍历,把临时结点的next指向链表的最后一个结点
// 在把该结点赋值给temp结点,将temp结点的next置null
for (int i = size; i > 0; i--) {
temp.next = getNode(i);
temp = temp.next;
temp.next = null;
}
head = tempHead;
}
}
单向循环链表解决约瑟夫问题
约瑟夫问题:一群人围成一个圈,从编号为k的人的位置往下报数,报到m的那个人出列,然后又从下一个位置继续,直到全部出列。
步骤:
- 首先先构建一个单向的循环链表。
- 开始报数出队列,有一个辅助结点指向头结点的上一个结点。
- 辅助结点与头结点都向下移动到开始报数的位置。
- 然后头结点和辅助结点开始报数,报数结束将头结点的下一个结点赋值给头结点,在把辅助结点的下一个结点赋值为头结点。
- 一直循环,直到辅助结点等于头结点。
JAVA代码实现:
// 创建了一个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;
}
}
// 创建一个环形链表
class CircleSingleLinkedList {
// 创建一个first节点
private Boy first;
// 添加小孩节点,构建环形链表
public void addBoy(int nums) {
if (nums < 1) {
System.out.println("添加节点数量不正确");
return;
}
// 临时结点
Boy temp = null;
// 根据传如的数量,创建对应的结点数
for (int i = 1; i <= nums; i++) {
// 以i为编号创建小孩结点
Boy boy = new Boy(i);
if (i == 1) {
// 当第一次进来时,直接初始化first,然后将first的next指向first,构成循环
first = boy;
first.setNext(first);
// 将临时结点初始为first
temp = first;
} else {
// 将temp的下一个结点指向新节点,在将新节点指向first节点,在把新节点赋值给temp
// 实现效果:
// 第一次: 1 -> 1
// 第二次: 1 -> 2 -> 1
temp.setNext(boy);
boy.setNext(first);
temp = boy;
}
}
}
// 遍历当前的环形链表
public void show() {
if (first == null) {
return;
}
Boy temp = first;
System.out.println(temp.getNo());
while (temp.getNext() != first) {
temp = temp.getNext();
System.out.println(temp.getNo());
}
}
/**
* 根据用户的输入,实现出圈顺序的计算
*
* @param startNo 开始编号
* @param countNum 报数
* @param nums 最大的人数编号
*/
public void countBoy(int startNo, int countNum, int nums) {
// 进行参数的合法性检验
if (first == null || startNo < 1 || startNo > nums) {
return;
}
// 创建一个辅助指针,该指针就是first结点的上一个结点
Boy helper = first;
// 一直循环到辅助结点的下一个结点是first结点
while (helper.getNext() != first) {
helper = helper.getNext();
}
// 移动到开始的编号位置
for (int i = 0; i < startNo - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
// 当辅助结点不是first结点时一直循环
while (helper != first) {
// 开始进行报数,把辅助结点和first向下移
for (int i = 0; i < countNum - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
System.out.println(first.getNo());
// 报数完成后,移除出圈的结点
// 将first结点指向它的下一个结点,辅助结点指向first结点
first = first.getNext();
helper.setNext(first);
}
// 最后的出圈编号
System.out.println(first.getNo());
}
}
双向链表
双向链表和单向链表的区别就是在next结点的基础上,增加了一个pre前驱结点指向前一个结点。
JAVA代码实现:
// 结点
class Node2<E> {
public E data;
public Node2<E> next;
// 新增的前驱节点
public Node2<E> pre;
public Node2(E data) {
this.data = data;
}
public Node2() {
super();
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
// 双向链表
class DoubleLinkedList<E> {
//头结点
private Node2<E> head = new Node2<E>();
// 结点个数
private int size;
// 添加数据到链表的方法
public boolean add(E data) {
// 尾插法插入数据,头结点不存数据
Node2<E> temp = head;
while (temp.next != null) {
temp = temp.next;
}
// 把temp节点的next指向新节点,在将新节点的pre指向temp
temp.next = new Node2<E>(data);
temp.next.pre = temp;
size++;
return true;
}
// 展示链表中的数据
public void show() {
if (head.next == null) {
System.out.println("链表为null");
return;
}
Node2<E> temp = head;
while (temp.next != null) {
temp = temp.next;
System.out.println(temp);
}
}
// 获取链表的长度
public int size() {
return size;
}
// 修改数据
public boolean update(int index, E updateData) {
if (index > size) {
return false;
}
Node2<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.data = updateData;
return true;
}
// 删除节点 单向链表需要找到删除节点的父结点,双向链表则是直接可以找到删除结点进行删除
public boolean delete(int index) {
if (index > size) {
return false;
}
Node2<E> temp = head;
// 根据传入的索引找到删除的结点
for (int i = 0; i < index; i++) {
temp = temp.next;
}
// 将当前结点的父结点的next指向当前结点的下一个结点
// temp.pre是父节点 temp.next是当前结点的子结点
temp.pre.next = temp.next;
// 如果当前结点的下一个节点不为null
if (temp.next != null) {
// 在设置他的前驱结点为temp结点的父节点
temp.next.pre = temp.pre;
}
temp = null;
size--;
return true;
}
// 获取节点
public Node2<E> getNode(int index) {
if (index > size) {
return null;
}
Node2<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp;
}
}
递归
递归:方法自身调用自身,实现简化程序目的。
使用递归解决迷宫问题
有一个地图,上面有障碍物,从开始位置到终点即为结束。使用二维数组进行模拟,1代表障碍物,设置起始位置和终点位置,使用程序找到终点。
模拟数组如下:
1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
起点位置:1,1 终点位置6,5
// 初始化地图
public static void main(String[] args) {
// 先创建一个二维数组,模拟迷宫
int[][] map = new int[8][7];
// 使用1表示墙
// 上下全部变1
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
map[3][1] = 1;
map[3][2] = 1;
for (int[] is : map) {
for (int is2 : is) {
System.out.print(is2 + " ");
}
System.out.println();
}
setWay(map, 1, 1);
System.out.println("-----------------------------");
for (int[] is : map) {
for (int is2 : is) {
System.out.print(is2 + " ");
}
System.out.println();
}
/**
* 找路的方法 0代表可以走 1代表障碍物 2代表走过的路 3代表走不通
*
* @param map 地图
* @param i 地图中的行位置
* @param j 地图中列位置
* @return 是否能前进
*/
public static boolean setWay(int[][] map, int i, int j) {
// 如果到达终点,直接返回true,2代表成功找到
if (map[6][6] == 2) {
return true;
} else {
// 如果当前位置可以走,进入
if (map[i][j] == 0) {
// 把当前位置设置为走过
map[i][j] = 2;
// 前进的策略,先往下走,下走不通在往右走,下面同理
if (setWay(map, i + 1, j)) {
System.out.println(i + " " + j);
return true;
} else if (setWay(map, i, j + 1)) {
System.out.println(i + " " + j);
return true;
} else if (setWay(map, i - 1, j)) {
System.out.println(i + " " + j);
return true;
} else if (setWay(map, i, j - 1)) {
System.out.println(i + " " + j);
return true;
// 走不通走这个,找不到代表没有出路
} else {
System.out.println(i + " " + j);
map[i][j] = 3;
return false;
}
} else {
// 否则返回false
return false;
}
}
}
递归解决八皇后问题
八皇后:在8*8的棋盘上,下满8个棋子,行列对角线上没有重复的棋子。
思路:
- 使用一个一维数组模拟棋盘,数组下标代表二维数组的行,值为列。
- 遍历整个一维数组,从每个位置开始尝试下棋,判断是否符合规则,符合则递归尝试继续下下一个棋子。
public class Queue8 {
// 定义一个max有多少个皇后
int max = 8;
// 定义数组array 保存放置位置的结果
int[] array = new int[max];
public static void main(String[] args) {
Queue8 queue = new Queue8();
queue.check(0);
}
// 放置第n个皇后
private void check(int n) {
// 如果开始下最后一个棋子,证明已经找完
if (n == max) {
// 调用打印输出
print();
return;
}
// 循环一维数组,在当前位置下棋
for (int i = 0; i < max; i++) {
// 在当前位置下棋
array[n] = i;
// 判断是否冲突
if (judge(n)) {
// 不冲突的话再次调用下棋的方法
check(n + 1);
}
}
}
/**
* 判断放置第n个时 判断皇后位置是否冲突
*
* @param n 第n个皇后
* @return
*/
private boolean judge(int n) {
for (int i = 0; i < n; i++) {
// 第一个条件判断是不是在同一列上,数组的值是第几列,因为是一维数组,不存在行重复
// 第二个条件是判断是不是在同一对角线上,|行的差值|等于|列的差值|即为同一对角线
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
// 输出数组的方法
private void print() {
for (int i : array) {
System.out.print(i + " ");
}
System.out.println();
}
}