栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

算法与数据结构笔记三(图,前缀树,贪心算法)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

算法与数据结构笔记三(图,前缀树,贪心算法)

图,前缀树,贪心算法

5.0 图

5.1 图的宽度优先遍历5.2 图的深度优先遍历5.3 拓扑排序算法5.4 prim算法5.5 Kruskal算法5.6 Dijkstra算法(迪杰斯特拉) 6. 前缀树(哈夫曼编码)

6.1 什么是前缀树 7. 贪心算法

7.1 会议问题7.2 解题套路7.3 字典序排序7.4 切金条(哈夫曼编码)7.5 花费和利润7.6 中位数 8.1 N皇后问题

5.0 图

图的存储方式:

    邻接表(以点为单位,把自己的相邻的点表示出来)

    邻接矩阵如何表达图?生成图?有向图?无向图?

描述图

描述点
描述边

如何实现接口函数

5.1 图的宽度优先遍历

跟二叉树的宽度优先遍历有什么区别? 树是没有环的,而图是可能有环的。
图的宽度优先遍历
1,利用队列实现
2,从源节点开始依次按照宽度进队列,然后弹出
3,每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
4,直到队列变空
== 一个点出来后在处理==

5.2 图的深度优先遍历

广度优先遍历
1,利用栈实现
2,从源节点开始把节点按照深度放入栈,然后弹出
3,每弹出一个点,把该节点下一个没有进过栈的邻接点放入栈
4, 直到栈变空
一个点进去时再处理

5.3 拓扑排序算法

适用范围:要求有向图,且有入度为0的节点,且没有环
import包,编译
寻找入度为0的点,然后寻找下一个入度为0 的点。

5.4 prim算法

适用范围:要求无向图
生成最小生成树:一定要保证点的联通性,并且整体边的权值最小

从哪里出发无所谓,所有边都没有被解锁,从第一个点出发,然后进行解锁相邻边,向权重最低的边出发,到达第二个点,在解锁相邻边,依次类推。(如果权重最低的边两个都遍历过了,就找下一个权重最低的边)

package class06;

import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

// undirected graph only
public class Code05_Prim {

	public static class EdgeComparator implements Comparator {

		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight - o2.weight;
		}

	}

	public static Set primMST(Graph graph) {
		// 解锁的边进入小根堆
		PriorityQueue priorityQueue = new PriorityQueue<>(
				new EdgeComparator());
				
		HashSet set = new HashSet<>();  // 考察过的点
		
		Set result = new HashSet<>();     //  一次挑选的边在result里
		
		for (Node node : graph.nodes.values()) {  // 处理森林问题,不连通的情况
			// 随便挑一个点
			// node是开始点
			if (!set.contains(node)) {
				set.add(node);
				for (Edge edge : node.edges) { //由一个点,解锁所有相连的边
					priorityQueue.add(edge); //  放在优先级队列里
				}
				while (!priorityQueue.isEmpty()) {
					Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边
					Node tonode = edge.to;    // 可能的一个新的点
					if (!set.contains(toNode)) {  // 不含有的时候,就是新的点。
						set.add(toNode);
						result.add(edge);
						for (Edge nextEdge : toNode.edges) {
							priorityQueue.add(nextEdge);
						}
					}
				}
			}
		}
		return result;
	}


5.5 Kruskal算法

1:20:00
适用范围:要求无向图
生成最小生成树:一定要保证点的联通性,并且整体边的权值最小
以边的角度出发,从最小的边出发,加边,然后判断把边加上能不能形成环,形成环的边就不加。(避圈法)

查并集


5.6 Dijkstra算法(迪杰斯特拉)

Dijkstra算法
适用范围:可以有权值为负数的边,不能有累加和为负数的环,单元最短路径算法。
2:04:00
一定要规定出发点,到目标点的最短距离。

先生成:
每一次在这张表中,选距离最短的点,然后该点更新到各点的最短距离,然后该点距离就固定且不用了。然后再挑剩下的距离最短的点,更新该点到各点的最小距离(min(15,3+2))

package class06;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

// no negative weight
public class Code06_Dijkstra {

	public static HashMap dijkstra1(Node head) {
		//从head出发到所有点的最小距离
		//key:从head出发到达key
		//value:从head出发到达key的最小距离
		//如果在表中,没有T的记录,含义是从head出发到T这个点的距离为正无穷
		HashMap distanceMap = new HashMap<>();
		distanceMap.put(head, 0);
		//已经求过距离的节点,存在selectedNodes中,以后再也不碰
		HashSet selectedNodes = new HashSet<>();
		Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
		while (minNode != null) {
			int distance = distanceMap.get(minNode);
			for (Edge edge : minNode.edges) {
				Node tonode = edge.to;
				if (!distanceMap.containsKey(toNode)) {
					distanceMap.put(toNode, distance + edge.weight);
				}
				distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), 
								distance + edge.weight));
			}
			selectedNodes.add(minNode);
			minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
		}
		return distanceMap;
	}
	
	
	// 选最小的距离的参数,但是不能是已经选过的点
	public static Node getMinDistanceAndUnselectedNode(HashMap distanceMap, 
			HashSet touchedNodes) {
		Node minNode = null;
		int minDistance = Integer.MAX_VALUE;
		for (Entry entry : distanceMap.entrySet()) {
			Node node = entry.getKey();
			int distance = entry.getValue();
			if (!touchedNodes.contains(node) && distance < minDistance) {
				minNode = node;
				minDistance = distance;
			}
		}
		return minNode;
	}

小根堆方法

系统实现的堆 入堆后 改变不了值。 必须自己手动写
0:12:00

public static class NodeRecord {
		public Node node;
		public int distance;

		public NodeRecord(Node node, int distance) {
			this.node = node;
			this.distance = distance;
		}
	}


	// 自己改的堆
	public static class NodeHeap {
		private Node[] nodes; // 所有的节点放在数组里面
		private HashMap heapIndexMap; // 任何一个节点在数组上的位置是 value值,  进来了弹出去了标为-1
		private HashMap distanceMap; //   到head的值
		private int size;  // 一共有多少个节点

		public NodeHeap(int size) {
			nodes = new Node[size];
			heapIndexMap = new HashMap<>();
			distanceMap = new HashMap<>();
			this.size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public void addOrUpdateOrIgnore(Node node, int distance) {
			if (inHeap(node)) {
				distanceMap.put(node, Math.min(distanceMap.get(node), distance));
				insertHeapify(node, heapIndexMap.get(node)); // 往上交换 交换到小了,只能往上
			}
			if (!isEntered(node)) {// 从来没有进来过
				nodes[size] = node;
				heapIndexMap.put(node, size);
				distanceMap.put(node, distance);
				insertHeapify(node, size++);
			}
			// 进来过却不在堆上 ,啥都不做
		}

		public NodeRecord pop() {
			NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
			swap(0, size - 1);// 把堆上最后一个元素拿到堆顶
			heapIndexMap.put(nodes[size - 1], -1); // 原本的头节点改为 -1
			distanceMap.remove(nodes[size - 1]);
			nodes[size - 1] = null;
			heapify(0, --size);
			return nodeRecord;
		}

		private void insertHeapify(Node node, int index) {
			while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
				swap(index, (index - 1) / 2);
				index = (index - 1) / 2;
			}
		}

		private void heapify(int index, int size) {
			int left = index * 2 + 1;
			while (left < size) {
				int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
						? left + 1 : left;
				smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
				if (smallest == index) {
					break;
				}
				swap(smallest, index);
				index = smallest;
				left = index * 2 + 1;
			}
		}

		private boolean isEntered(Node node) { // node进没进来过
			return heapIndexMap.containsKey(node);
		}

		private boolean inHeap(Node node) { // 是否在堆上
			return isEntered(node) && heapIndexMap.get(node) != -1;
		}

		private void swap(int index1, int index2) {  // 在堆上两个节点要换位置,heapIndexMap,nodes都要交换
			heapIndexMap.put(nodes[index1], index2);
			heapIndexMap.put(nodes[index2], index1);
			Node tmp = nodes[index1];
			nodes[index1] = nodes[index2];
			nodes[index2] = tmp;
		}
	}

	//改进后的dihkstra算法
	// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
	public static HashMap dijkstra2(Node head, int size) {
		NodeHeap nodeHeap = new NodeHeap(size);
		nodeHeap.addOrUpdateOrIgnore(head, 0); // 之前有记录  变小了就更新 
		HashMap result = new HashMap<>();
		while (!nodeHeap.isEmpty()) {
			NodeRecord record = nodeHeap.pop();
			Node cur = record.node;
			int distance = record.distance;
			for (Edge edge : cur.edges) {
				nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
			}
			result.put(cur, distance);
		}
		return result;
	}
6. 前缀树(哈夫曼编码) 6.1 什么是前缀树

没有增加,有就复用。

package class07;

public class Code01_TrieTree {

	public static class TrieNode {
		public int path;  // 经过了几次, 
		public int end;   // 是否是结尾的节点,是几个结尾的节点
		public TrieNode[] nexts;   
		// HashMap nexts;
		// TreeMap nexts;
		public TrieNode() {
			path = 0;
			end = 0;
			nexts[0] == null
			//没有走向‘a’的路
			//nexts[0]!=nul1有走向‘a'的路
			//。。。
			//nexts[25]!=nul1有走向‘z’的路
			nexts = new TrieNode[26];
		}
	}

	public static class Trie {
		private TrieNode root;

		public Trie() {
			root = new TrieNode();
		}

		public void insert(String word) {
			if (word == null) {
				return;
			}
			char[] chs = word.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) { // 开始从左往右遍历字符
				index = chs[i] - 'a'; // 由字符,对应应走哪条路
				if (node.nexts[index] == null) {
					node.nexts[index] = new TrieNode();
				}
				node = node.nexts[index];
				node.path++;
			}
			node.end++;
		}

		public void delete(String word) {
			if (search(word) != 0) {  // 确定树中确实加入过word,才删除
				char[] chs = word.toCharArray();
				TrieNode node = root;
				int index = 0;
				for (int i = 0; i < chs.length; i++) {
					index = chs[i] - 'a';
					if (--node.nexts[index].path == 0) {// path为0, 后续节点都要删掉。
						// 只有java可以这么写,
						node.nexts[index] = null;
						return;
					}
					node = node.nexts[index];
				}
				node.end--;
			}
		}
		// word这个单词之前加入过几次
		public int search(String word) {
			if (word == null) {
				return 0;
			}
			char[] chs = word.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
					return 0;
				}
				node = node.nexts[index];
			}
			return node.end;
		}
		// 所有加入过的字符中,有几个是以pre这个字符串作为前缀的
		public int prefixNumber(String pre) {
			if (pre == null) {
				return 0;
			}
			char[] chs = pre.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
					return 0;
				}
				node = node.nexts[index];
			}
			return node.path;
		}
	}

	public static void main(String[] args) {
		Trie trie = new Trie();
		System.out.println(trie.search("zuo"));
		trie.insert("zuo");
		System.out.println(trie.search("zuo"));
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.insert("zuo");
		trie.insert("zuo");
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.insert("zuoa");
		trie.insert("zuoac");
		trie.insert("zuoab");
		trie.insert("zuoad");
		trie.delete("zuoa");
		System.out.println(trie.search("zuoa"));
		System.out.println(trie.prefixNumber("zuo"));

	}

}

7. 贪心算法

贪心算法

在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫作贪心算法。

也就是说,不从整体最优上加以考虑,所做出的是在某种意义上的局部最优解。

局部最优 -?->整体最优

7.1 会议问题

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次。
找贪心策略。(时间优先不行,最短策略也不行)哪个会议结束时间早,就先安排

package class07;

import java.util.Arrays;
import java.util.Comparator;

public class Code04_BestArrange {

	public static class Program {
		public int start;
		public int end;

		public Program(int start, int end) {
			this.start = start;
			this.end = end;
		}
	}

	public static class ProgramComparator implements Comparator {

		@Override
		public int compare(Program o1, Program o2) {
			return o1.end - o2.end;
		}

	}

	public static int bestArrange(Program[] programs, int start) {
		Arrays.sort(programs, new ProgramComparator());
		int result = 0;
		// 从左往右依次遍历所有的会议
		for (int i = 0; i < programs.length; i++) {
			if (start <= programs[i].start) {
				result++;
				start = programs[i].end;
			}
		}
		return result;
	}

	public static void main(String[] args) {

	}

}

7.2 解题套路

贪心算法的在笔试时的解题套路
1,实现一个不依靠贪心策略的解法X,可以用最暴力的尝试
2,脑补出贪心策略A、贪心策略B、贪心策略C…
3,用解法X和对数器,去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确
4,不要去纠结贪心策略的证明

7.3 字典序排序

策略必须具有传递性,局部最优

a字符串+b字符串 和 b字符串+a字符串进行比较
证明:1:36:00


之证明了具有传递性,接下来证明按照这样的方法排序是由效的。

package class07;

import java.util.Arrays;
import java.util.Comparator;

public class Code02_LowestLexicography {

	public static class MyComparator implements Comparator {
		@Override
		public int compare(String a, String b) {
			return (a + b).compareTo(b + a);
		}
	}

	public static String lowestString(String[] strs) {
		if (strs == null || strs.length == 0) {
			return "";
		}
		Arrays.sort(strs, new MyComparator());
		String res = "";
		for (int i = 0; i < strs.length; i++) {
			res += strs[i];
		}
		return res;
	}

	public static void main(String[] args) {
		String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" };
		System.out.println(lowestString(strs1));

		String[] strs2 = { "ba", "b" };
		System.out.println(lowestString(strs2));

	}

}

7.4 切金条(哈夫曼编码)


package class07;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Code03_LessMoneySplitGold {

	public static int lessMoney(int[] arr) {
		PriorityQueue pQ = new PriorityQueue<>();
		for (int i = 0; i < arr.length; i++) {
			pQ.add(arr[i]);
		}
		int sum = 0;
		int cur = 0;
		while (pQ.size() > 1) {
			cur = pQ.poll() + pQ.poll();
			sum += cur;
			pQ.add(cur);
		}
		return sum;
	}

	public static class MinheapComparator implements Comparator {

		@Override
		public int compare(Integer o1, Integer o2) {
			return o1 - o2; // < 0  o1 < o2  负数
		}

	}

	public static class MaxheapComparator implements Comparator {

		@Override
		public int compare(Integer o1, Integer o2) {
			return o2 - o1; // <   o2 < o1
		}

	}

	public static void main(String[] args) {
		// solution
		int[] arr = { 6, 7, 8, 9 };
		System.out.println(lessMoney(arr));

		int[] arrForHeap = { 3, 5, 2, 7, 0, 1, 6, 4 };

		// min heap
		PriorityQueue minQ1 = new PriorityQueue<>();
		for (int i = 0; i < arrForHeap.length; i++) {
			minQ1.add(arrForHeap[i]);
		}
		while (!minQ1.isEmpty()) {
			System.out.print(minQ1.poll() + " ");
		}
		System.out.println();

		// min heap use Comparator
		PriorityQueue minQ2 = new PriorityQueue<>(new MinheapComparator());
		for (int i = 0; i < arrForHeap.length; i++) {
			minQ2.add(arrForHeap[i]);
		}
		while (!minQ2.isEmpty()) {
			System.out.print(minQ2.poll() + " ");
		}
		System.out.println();

		// max heap use Comparator
		PriorityQueue maxQ = new PriorityQueue<>(new MaxheapComparator());
		for (int i = 0; i < arrForHeap.length; i++) {
			maxQ.add(arrForHeap[i]);
		}
		while (!maxQ.isEmpty()) {
			System.out.print(maxQ.poll() + " ");
		}

	}

}

7.5 花费和利润


堆,
局部:花费最小,利润最大 。 全局: 则最优

复习一下:返回正数,第二个参数排前面;返回负数,第一个参数排前面
leetcode 502

package class07;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Code05_IPO {
	public static class Node {
		public int p;
		public int c;

		public Node(int p, int c) {
			this.p = p;
			this.c = c;
		}
	}

	public static class MinCostComparator implements Comparator {

		@Override
		public int compare(Node o1, Node o2) {
			return o1.c - o2.c;
		}

	}

	public static class MaxProfitComparator implements Comparator {

		@Override
		public int compare(Node o1, Node o2) {
			return o2.p - o1.p;
		}

	}

	public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
		Node[] nodes = new Node[Profits.length];
		for (int i = 0; i < Profits.length; i++) {
			nodes[i] = new Node(Profits[i], Capital[i]);
		}

		PriorityQueue minCostQ = new PriorityQueue<>(new MinCostComparator());
		PriorityQueue maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
		// 所有i项目扔到被锁池中,花费组织小根堆
		for (int i = 0; i < nodes.length; i++) {
			minCostQ.add(nodes[i]);
		}
		for (int i = 0; i < k; i++) { // 进行K轮
			// 力所能及的项目,全解锁
			while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
				maxProfitQ.add(minCostQ.poll());
			}
			if (maxProfitQ.isEmpty()) {
				return W;
			}
			W += maxProfitQ.poll().p;
		}
		return W;
	}

}

7.6 中位数

一个数据流中,随时可以取得中位数
堆的题的应用和贪心没关系
大根堆小根堆配合

    第一个数进大根堆r然后判断下一个数是否小于等于大根堆,如果满足进大根堆,不满足进小根堆比较两个根堆的size是否相差为2,相差为二的话,则从size较大的堆里堆顶弹出一个,进size较少的堆里,效果:较小的一半在大根堆里,较大的一半在小根堆里
package class07;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Code06_MadianQuick {

	public static class MedianHolder {
		private PriorityQueue maxHeap = new PriorityQueue(new MaxHeapComparator());
		private PriorityQueue minHeap = new PriorityQueue(new MinHeapComparator());

		private void modifyTwoHeapsSize() {
			if (this.maxHeap.size() == this.minHeap.size() + 2) {
				this.minHeap.add(this.maxHeap.poll());
			}
			if (this.minHeap.size() == this.maxHeap.size() + 2) {
				this.maxHeap.add(this.minHeap.poll());
			}
		}

		public void addNumber(int num) {
			if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
				maxHeap.add(num);
			} else {
				minHeap.add(num);
			}
			modifyTwoHeapsSize();
		}

		public Integer getMedian() {
			int maxHeapSize = this.maxHeap.size();
			int minHeapSize = this.minHeap.size();
			if (maxHeapSize + minHeapSize == 0) {
				return null;
			}
			Integer maxHeapHead = this.maxHeap.peek();
			Integer minHeapHead = this.minHeap.peek();
			if (((maxHeapSize + minHeapSize) & 1) == 0) {
				return (maxHeapHead + minHeapHead) / 2;
			}
			return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;
		}

	}

	public static class MaxHeapComparator implements Comparator {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 > o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	public static class MinHeapComparator implements Comparator {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 < o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	// for test
	public static int[] getRandomArray(int maxLen, int maxValue) {
		int[] res = new int[(int) (Math.random() * maxLen) + 1];
		for (int i = 0; i != res.length; i++) {
			res[i] = (int) (Math.random() * maxValue);
		}
		return res;
	}

	// for test, this method is ineffective but absolutely right
	public static int getMedianOfArray(int[] arr) {
		int[] newArr = Arrays.copyOf(arr, arr.length);
		Arrays.sort(newArr);
		int mid = (newArr.length - 1) / 2;
		if ((newArr.length & 1) == 0) {
			return (newArr[mid] + newArr[mid + 1]) / 2;
		} else {
			return newArr[mid];
		}
	}

	public static void printArray(int[] arr) {
		for (int i = 0; i != arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		boolean err = false;
		int testTimes = 200000;
		for (int i = 0; i != testTimes; i++) {
			int len = 30;
			int maxValue = 1000;
			int[] arr = getRandomArray(len, maxValue);
			MedianHolder medianHold = new MedianHolder();
			for (int j = 0; j != arr.length; j++) {
				medianHold.addNumber(arr[j]);
			}
			if (medianHold.getMedian() != getMedianOfArray(arr)) {
				err = true;
				printArray(arr);
				break;
			}
		}
		System.out.println(err ? "Oops..what a fuck!" : "today is a beautiful day^_^");

	}

}

8.1 N皇后问题



常数优化
2:49:00

package class08;

public class Code09_NQueens {

	public static int num1(int n) {
		if (n < 1) {
			return 0;
		}
		int[] record = new int[n]; // record[i] -> i行的皇后 ,放在了第几列
		return process1(0, record, n);
	}
	// 潜台词: record[0,,,i-1]的皇后,任何两个皇后一定不共行,不共列,不共斜线
	// 目前来到了第i行,
	// record[0,,,i-1] 表示之前的行, 放过了皇后的位置
	// 整体一共有n行
	// 返回值是摆完所有的皇后, 合理的摆法有多少种
	public static int process1(int i, int[] record, int n) {
		if (i == n) { // 终止行, 比最后一行再往下一行
			return 1;
		}
		int res = 0;
		for (int j = 0; j < n; j++) { // 当前行再i行,尝试i行所有的列j、
			//当前i行的皇后,放在j列,会不会和之前(0..i-1)的皇后,共行共列或者共斜线,
			//如果是,认为无效
			//如果不是,认为有效
			if (isValid(record, i, j)) {
				record[i] = j;
				res += process1(i + 1, record, n);
			}
		}
		return res;
	}
	
	// record[0...i-1]你需要看,record[i...]不需要看
	// 返回i行皇后,放在了j列,是否有效
	public static boolean isValid(int[] record, int i, int j) {
		for (int k = 0; k < i; k++) { // 之前的某个k行的皇后
			if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {
				return false;
			}
		}
		return true;
	}
	
	
	
	
	// 请不要超过32皇后问题
	public static int num2(int n) {
		if (n < 1 || n > 32) {
			return 0;
		}
		int upperLim = n == 32 ? -1 : (1 << n) - 1; 
		// 生成二进制的数  8的数 0000‘’‘1111 1111 只使用位信息
		// 右侧哪些区域是能进行限制的
		return process2(upperLim, 0, 0, 0);
	}
	
	//colLim列的限制,1的位置不能放皇后,0的位置可以
	//leftDiaLim左斜线的限制,1的位置不能放皇后,0的位置可以
	//rightDialim右斜线的限制,1的位置不能放皇后,0的位置可以
	// 限制可以进行左移,右移进行限制。
	public static int process2(int upperLim, int colLim, int leftDiaLim,
			int rightDiaLim) {
		if (colLim == upperLim) { // base case 所有的皇后都合法, 皇后填满了
			return 1; // 发现一种有效的
		}
		int pos = 0;
		int mostRightOne = 0;
		
		// 所有候选皇后的位置,都在pos上了,  用1表示
		pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
		int res = 0;
		while (pos != 0) {
			mostRightOne = pos & (~pos + 1); // 把一个二进制的数,最右侧的1提取出来
			pos = pos - mostRightOne;    //  试皇后
			res += process2(upperLim, colLim | mostRightOne,
					(leftDiaLim | mostRightOne) << 1,
					(rightDiaLim | mostRightOne) >>> 1);
		}
		return res;
	}

	public static void main(String[] args) {
		int n = 14;

		long start = System.currentTimeMillis();
		System.out.println(num2(n));
		long end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

		start = System.currentTimeMillis();
		System.out.println(num1(n));
		end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

	}
}

pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));


mostRightOne = pos & (~pos + 1); // 把一个二进制的数,最右侧的1提取出来
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/760117.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号