链表和递归解决死亡八皇后和迷宫问题

单向链表

实现思路:

  1. 创建一个Node结点,有data和next结点。
  2. 创建一个链表类,里面存一个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的那个人出列,然后又从下一个位置继续,直到全部出列。

步骤:

  1. 首先先构建一个单向的循环链表。
  2. 开始报数出队列,有一个辅助结点指向头结点的上一个结点。
  3. 辅助结点与头结点都向下移动到开始报数的位置。
  4. 然后头结点和辅助结点开始报数,报数结束将头结点的下一个结点赋值给头结点,在把辅助结点的下一个结点赋值为头结点。
  5. 一直循环,直到辅助结点等于头结点。

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个棋子,行列对角线上没有重复的棋子。

思路:

  1. 使用一个一维数组模拟棋盘,数组下标代表二维数组的行,值为列。
  2. 遍历整个一维数组,从每个位置开始尝试下棋,判断是否符合规则,符合则递归尝试继续下下一个棋子。
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();
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是 C 语言代码,使用链表递归方式建立二叉树: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *left; struct Node *right; } Node; Node* createNode(int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } Node* createBinaryTree() { int data; scanf("%d", &data); if (data == -1) { return NULL; } Node *root = createNode(data); printf("Enter left child of %d: ", data); root->left = createBinaryTree(); printf("Enter right child of %d: ", data); root->right = createBinaryTree(); return root; } void preorderTraversal(Node *root) { if (root == NULL) { return; } printf("%d ", root->data); preorderTraversal(root->left); preorderTraversal(root->right); } int main() { Node *root = NULL; printf("Enter the root node: "); root = createBinaryTree(); printf("Preorder traversal of binary tree: "); preorderTraversal(root); printf("\n"); return 0; } ``` 在这个代码中,我们定义了一个结构体 `Node`,表示二叉树的节点。节点包含数据 `data`,以及指向左子树和右子树的指针 `left` 和 `right`。`createNode` 函数用于创建一个新节点。`createBinaryTree` 函数使用递归方式创建二叉树。每次读入一个数据,如果数据为 -1,则表示当前节点没有子节点,返回 NULL;否则创建一个新节点,然后递归调用 `createBinaryTree` 函数创建左子树和右子树,并将它们连接到当前节点上。`preorderTraversal` 函数用于前序遍历二叉树。在 `main` 函数中,我们首先读入根节点,然后调用 `preorderTraversal` 函数遍历整个二叉树。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dichotomy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值